흐름 구상하고 구조체 만들 거 계획하며
예상으로 적어놓은 흐름이 있다
예상흐름 ☜(⌯•ᴗ<⌯)☜
이 흐름과 실제 구현이 어떻게 차이가 나고 동일한지 보여주겠다
사실 똑같다!!!
계획대로 잘 흘러갔다 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()
로 종료