흐름 구상하고 구조체 만들 거 계획하며
예상으로 적어놓은 흐름이 있다
예상흐름 ☜(⌯•ᴗ<⌯)☜
이 흐름과 실제 구현이 어떻게 차이가 나고 동일한지 보여주겠다
사실 똑같다!!!
계획대로 잘 흘러갔다 d ( ܸܸ ì ·̫ í ⑅ )b
그치만 이렇게 끝내면 안되니까 어떻게 구현했길래 흐름이 동일한지 설명하겠다
파일: threads/init.c
함수: run_task(const char *cmdline)
쉘/테스트가 넘겨준 cmdline을 그대로 자식 프로세스 생성 API에 전달
즉시 대기:
int status = process_wait(tid);
printf("Execution of '%s' complete.\n", cmdline);
-q 옵션이면 마지막에 power_off()
부모가 하는 일은 “만들고(wait) 끝까지 기다린다”.
process_exec (ELF 로드 → 스택 구축 → 유저모드 진입)파일: userprog/process.c
함수: process_exec(void *f_name)
커맨드라인에서 프로그램 이름/인자 분리 (필요하면 strtok_r 등)
struct intr_frame if_; memset(&if_,0,…)
load(...) 호출
ELF 헤더 검증/세그먼트 매핑
성공 시 if_.rip = ehdr.e_entry;
사용자 스택 빌드 (setup_stack(...) + 인자 적재)
문자열 복사, argv[] 포인터 배열 푸시, argv[argc]=NULL, 워드 정렬
if_.R.rdi = argc; if_.R.rsi = argv; (System V AMD64 ABI)
if_.rsp = 새 스택 최종 위치
유저모드로 점프:
do_iret(&if_); // iretq로 CPL3 전이 (반환하지 않음)
__builtin_unreachable();
실패 경로에서는 thread_exit();로 종료
여기서부터 사용자 코드
_start → main → exit()가 유저모드에서 돌아간다
exit()/write() 호출 (유저→커널 트랩)파일: lib/user/syscall.c
동작: 각 래퍼가
RAX ← 시스템콜 번호, 인자 RDI, RSI, RDX, R10, R8, R9에 적재
syscall 명령 실행 → CPU가 커널 진입점으로 점프
syscall_entry → syscall_handler() (디스패처)파일: userprog/syscall.c (+ intr-stubs.S)
흐름
syscall_entry(어셈블리 스텁)가 레지스터/스택 저장 후 syscall_handler(struct intr_frame *f) 호출
디스패처 예:
switch (SC_NO(f)) {
case SYS_EXIT: {
int status = (int)ARG0(f);
sys_exit(f, status); // 아래 5번 흐름으로
__builtin_unreachable(); // thread_exit()로 돌아오지 않음
}
case SYS_WRITE: {
int fd = (int)ARG0(f);
const void *ubuf = (const void*)ARG1(f);
unsigned size = (unsigned)ARG2(f);
if (fd == 1) { putbuf(ubuf, size); RETVAL(f) = size; }
else { /* 파일FD면 파일시스템 경로 */ }
break;
}
default:
RETVAL(f) = -1;
}
(포인터 인자 접근 전 유저 포인터 검증 / is_user_vaddr, get_user() 등 필수)
process_exit()에서 자원정리 + “exit(…)” 출력 + sema_up파일: userprog/process.c (또는 syscall.c에서 thread_exit() 전에 호출)
호출 경로: sys_exit() → thread_exit() → (USERPROG일 때) process_exit()
(혹은 sys_exit()에서 직접 process_exit() 호출 후 thread_exit())
해야 할 일(프로젝트2 필수)
현재 쓰던 실행 파일 close(쓰기락 해제), 열린 파일/디스크립터 정리
페이지 테이블/VM 해제
종료 메시지 출력: printf("%s: exit(%d)\n", thread_name(), status);
부모 대기 해제:
struct child_status *cs = thread_current()->my_status;
if (cs) {
cs->exit_code = status;
cs->exited = true;
sema_up(&cs->sema); // 부모의 process_wait() 깨움
if (--cs->ref_cnt == 0) free(cs);
}
마지막에 thread_exit();로 스케줄러에 자신을 넘김
process_wait() 정상 반환 → run_task() 마무리파일: userprog/process.c
함수: int process_wait(tid_t child)
current->children에서 child_status 선형 탐색
없거나 이미 waited==true면 -1
cs->waited = true; sema_down(&cs->sema); // 자식 종료까지 블록
깨어나면 status = cs->exit_code;
list_remove(&cs->elem); if (--cs->ref_cnt == 0) free(cs);
반환: 자식 종료코드
부모(run_task) 는 반환 코드로 진행, 테스트 드라이버가
Execution of '…' complete. 출력
-q면 power_off()로 종료