cf) 23.10.4 updated
또다시 정리해보는 흐름
> pintos --filesys-size=2 -p (..../경로) -a (실행파일 이름) -- -f -q run 'exec-bad-arg'
실행파일이 Pintos disk에 올라오고, filesystem으로 복사된다.
핀토스 운영체제가 시작되면서 run.., process_execute.. 등등의 흐름을 따라 'exec-bad-arg'를 실행할 자식 스레드를 생성한다.
'exec-bad-arg'를 파싱한 후 user stack에 쌓는다(passing)
실행파일의 instruction들이 실행되는데, c코드로 치면 main의 파라미터인 argc, argv로 내가 쌓은 user stack이 들어온다.
실행파일의 instruction 중 하나가 write같이 특정 시스템 콜을 호출한다.
이 시스템 콜은 src/lib/user/syscall.c에 있다. 이 c파일에서 코드의 흐름은 int write() - #define syscall3 이다.
실행파일이니 user code였지만 #define syscall3 여기에 int 즉 인터럽트가 있어서 mode가 커널모드로 전환된다.
그럼 src/userprog/syscall.c (내가 작성하는!!) 으로 넘어와서 write에 해당하는 부분을 수행한다.
이런식으로 실행파일의 instruction 중에 syscall을 요구하는 부분이 있으면 인터럽트 걸려서 커널코드를 따르다가 돌아오곤 하며
실행파일의 모든 instruction이 수행되게 된다.
그러면 thread_exit - process_exit의 흐름으로 자식 스레드가 죽는다.
자식 스레드가 죽으니 아까 이 자식을 생성했던 부모 스레드가 process_wait하면서 thread_yield로 양보하며 대기타고 있다가
reap을 하는 것이다.
이러면 첫줄에 썼던 내 cmd가 끝난다.
운영체제의 중요한 특징 중 하나는 dual mode를 활용한다는 것이다.
user mode와 kernel mode의 두 모드를 통해
user는 kernel의 다양한 api를 제공받을 수 있고, os는 user가 함부로 중요한 영역에 침범하지는 않을지 걱정도 덜 수 있다.
그럼 user는 kernel의 api를 어떻게 제공받는다는 것이냐!!
바로 바로 의도적으로 interrupt를 발생시켜 system call ... 등등의 '일련의 과정'을 통해
mode변경 (user mode -> kernel mode)을 함으로써 제공받는다는 거다!!
그 '일련의 과정'에 대해 코드를 뜯어서 알아보자.
만약 아래 코드를 실행한다면,
pintos --filesys-size=2 -p ../examples/echo -a echo -- -f -q run 'echo x'
simulated file system(size는 2)에 echo가 복사될 것이다.
그리고 kernel mode 명령으로 run 'echo x'를 했으니 user program이 아닌 os의 프로그램이 돌아가기 시작한다.
먼저 threads/init.c에 main이란 이름을 가진 함수가 있으므로 여기서 os 코드를 시작한다.
init.c의 함수 flow를 따라가보자.
main 함수를 보면 thread_start가 있다.
이 thread_start는 부모의 thread를 create한다. (thread_start 내부에 thread_create있음)
다시 흐름을 따라가다 발견할 수 있는 run_task()에는
process_wait((process_execute(*)))라고 쓰여진 부분이 있다.
그럼 먼저 '인자'인 process_execute(*)가 실행되겠지?
그러니 userprog/process.c 로 들어간다.
process_execute(*)에는 thread_create(... start_process...)라는 부분이 있다.
먼저 thread를 새로 만드는 것이다. 내 명령(ex. echo x)를 실행하기 위해서이다.
thread_create이라는 함수는 새 kernel thread를 만들어낸다.
이 새로 만들어진 kernel thread는 start_process를 실행시킬 것이다.
우선 thread_create에서 스레드 생성을 마치면,
원래 코드(부모 프로세스?)는 process_execute를 마치고 process_wait으로 가서
자식을 기다린다!
그럼 이제 start_process 코드가 돌아가기 시작한다. (printf 다찍어서 확인해봄ㅠ.ㅠ)
start_process 실행 도중 load에 가서 args parsing&passing도 마치고~
그리고 나서 usercode 즉 echo.c (src/tests/userprog/echo.c)가 실행된다.
이 시점에서 usercode가 실행된다는 증거가 있는 코드상의 지점은 찾지 못했다.
아마 새로 만든 자식 thread가 queue에서 대기하고 있는데
부모는 wait에서 기다리고 있으니 실행되는게 아닐까?
usercode인 echo.c의 실행이 끝나면 자식이 exit하고,
process_wait에서 기다리고 있던 부모도 기다림을 끝내고 모든게 종료된다.
Test Cases 중 args-* 의 흐름
부모 스레드 생성
부모가 process_execute에서 자식 스레드 생성
부모는 process_wait에서 기다림 시작
자식 스레드는 start_process - load를 거치며 args parsing&passing
test 메시지 출력을 위해 system call write 을 지속적으로 호출 (ex. (args) begin 같은 것들)
자식 스레드의 exit을 위해 system call exit 호출
자식 스레드 thread_exit, process_exit
부모가 process_wait에서 기다리던 것 끝
Test Cases 중 exec-once 의 흐름
부모 스레드 생성
부모가 process_execute에서 자식 스레드 생성
부모는 process_wait에서 기다림 시작
자식 스레드는 start_process - load를 거치며 args parsing&passing
test 메시지 출력을 위해 system call write 을 호출
wait (exec("child-simple"));
exec("child-simple")을 위해 system call exec 호출
wait을 위해 system call wait 호출
MY ERRORS & QUESTIONS with ANSWER!
src/userprog 에서 make check를 하면 내 코드의 출력에서 뭐가 문제인지 알려준다!
+ 로 잘못된 나의 출력
- 로 출력되어야하는데 빠진 출력
을 알려주고 있었다..
나는 그것도 모르고 대체 어떻게 디버깅해야하는거야... 하면서 삽질하고 있었다.
컴퓨터는 나에게 계속 이야기하고 있었어 못들은건 나야ㅠㅠㅠㅠㅠ
'시스템' 카테고리의 다른 글
[시스템 프로그래밍] exit / fork / wait / execve (1) | 2023.03.22 |
---|---|
[시스템 프로그래밍] error handling wrapper / pid / process states (0) | 2023.03.15 |
[시스템 프로그래밍] Process와 Context Switching (0) | 2023.03.14 |
[시스템 프로그래밍] Exceptional control flow의 종류 (0) | 2023.03.09 |
[OS_Memory] VPN&offset 계산 기초 (0) | 2022.11.07 |