https://gyumingomin.tistory.com/85
Pintos - 어떻게 부팅해서 운영체제가 되는가? [1]
Pintos는 어떻게 부팅해서 운영체제가 되는가?start.S에서 main(), 그리고 thread_exit() 까지의 흐름 정리 [1] Pintos를 보면서 가장 인상 깊었던 지점은 main()이 시작점이 아니라는 점이다.보통 프로그램에
gyumingomin.tistory.com
먼저 읽고 오시길 추천드립니다.
start.S에서 main() 그리고 thread_exit()까지의 흐름 정리 [2]
이전에서 start.S의 어셈블리어 흐름을 조사했다면(물론 어셈블리어 전부를 다 보진 못했지만), 이제는 실제로 OS가 어떻게 초기화되는지 명확히 하고 넘어가고자 한다.
start.S -> main() -> thread_exit() 흐름을 따라가며, Pintos가 어떻게 "실제로 동작하는 운영체제"가 되는지 정리
init.c의 main()
앞에서 보았듯 start.S에서 CPU를 "실행 가능 상태"로 만드는 부트 어셈블리의 역할이 끝났다.
start.S
↓
long mode 진입
↓
임시 페이지 테이블 준비
↓
GDT 로드
↓
커널 스택 설정
1. bss_init(): 커널의 전역 상태를 0으로 정리하는 첫 작업
이 함수는 .bss 영역을 0으로 채움 (.bss는 초기값이 없는 전역 변수와 static 변수가 놓이는 공간)

2. read_command_line() / parse_options(argv)
로더가 메모리를 남겨 둔 커맨드 라인 인자를 읽어 argv 형태로 해석후, -q, -mlfqs, -ul 같은 옵션을 파싱해 이후 초기화 정책을 결정
이 순서가 앞쪽에 있는 이유는 스케줄러를 어떤 방식으로 쓸지, 종료 시 전원을 끌지, 유저 메모리를 얼마나 줄지 같은 정책은 나머지 커널 초기화보다 먼저 정해져야 하기 때문
parse_options : 어떤 커널로 부팅할지 먼저 결정

3. thread_init() : 현재 실행 중인 흐름을 main 스레드로 바꿈
현재 실행 중인 흐름을 main 스레드로 변경
ready_list, tid_list, destruction request list를 ㅊ기화하고, 지금까지 흘러오던 부팅 흐름 자체를 main 스레드로 등록
이 순간부터 pintos는 하나의 흐름이 그냥 실행되는 코드가 아니라 스레드라는 추상화 위에서 돌아가는 운영체제가 됨

4. console_init()
콘솔 락은 스레드와 동기화 primitive 위에서 동작하므로, 반드시 thread_init()뒤에 와야 함
즉, 콘솔도 그냥 출력 장치가 아니라 동시성 환경에서 안전하게 쓰이도록 재정의 되는 셈

5. palloc_init() / malloc_init() / paging_init()
메모리 초기화
palloc_init()은 페이지 단위 물리 메모리 할당기를 준비
malloc_init()은 작은 크기 요청을 처리할 블록 할당기를 준비
paging_init()은 커널이 사용할 정식 페이지 테이블을 만듬
운영체제가 메모리를 쓰기 전에 먼저 메모리를 어떻게 해석하고 나눌 것인지 규칙부터 만든다는 것

더보기
(다음 주 차에 공부할 예정)
tss_init() / gdt_init()
`USERPROG`가 켜져 있다면 다음 단계는 `tss_init()`과 `gdt_init()`
TSS가 "Task 전환 전체"를 담당하는 오래된 구조라기보다, Pintos에서는 "유저 모드에서 인터럽트나 시스템 콜이 들어왔을 때 사용할 커널 스택 주소를 알려주는 역할"을 한다는 점
즉, 유저 프로그램을 돌리기 전 먼저 준비해야 하는 것은 "유저 코드를 실행하는 방법"보다 "문제가 생겼을 때 커널로 안전하게 복귀하는 방법"
6. intr_init() 부터 input_init()
인터럽트를 켜기 전에 먼저 인터럽트 핸들러를 등록하고, 인터럽트 디스크립터 테이블을 설정해서 정책 준비

7. thread_start()
idle thread를 생성한 뒤 intr_enable을 호출해 여기서 부터 진짜 운영체제가 됨
이 시점부터는 더 이상 하나의 부팅 흐름이 순서대로만 흘러가지 않게 됨
타이머 인터럽트가 들어오고, ready queue가 의미를 가지며, 필요하면 현재 실행 중인 스레드가 CPU를 빼앗길 수도 있음

8. serial_init_queue() / timer_calibrate()
스케줄러가 살아난 뒤에 시리얼 큐 , 타이머 보정, 파일 시스템, VM 같은 상위 기능들이 붙음

9. run_action
부팅이 끝난 뒤, 실제 일을 시작
Boot complete가 출력된 뒤 Pintos는 run_actions(argv)를 호출
여기서 run test-name이면 스레드 테스트를 돌리고, 유저 프로그램 모드에서는 첫 유저 프로세스인 initd를 만든 뒤 기다림
부팅과 실행이 분리되어 있는 상태
즉, 지금까지의 모든 과정은 "일을 하기 위한 준비"였고, 실제로 커널이 무엇을 할지는 run_actions()에서 비로소 시작
10. power_off() 또는 thread_exit()
마지막에 -q 옵션이 있으면 power_off()를 호출하고, 아니면 thread_exit()으로 빠짐
일반 사용자 프로그램이라면main()이 끝나면 그냥 return 하고 프로세스가 종료
하지만 Pintos의 main()은 이미 하나의 커널 스레드로 끝도 함수 반환이 아니라 스레드 종료로 처리
thread_exit()은 현재 스레드를 즉시 free하지 않고 자기 자신의 커널 스택 위에서 아직 실행 중이므로 THREAD_DYING으로 상태를 바꾸고 스케줄링한 뒤, 나중에 다른 문맥에서 안전하게 정리함

정리
Pintos의 부팅 흐름은 단순한 초기화 목록이 아니라
start.S 에서는 CPU가 커널을 실행할 수 있는 환경을 만들고,
init.c 에서는 커널이 운영체제로 구성되며
thread_start() 이후에는 진짜 인터럽트와 스케줄링이 살아있는 시스템이 되며
마지막에 main()은 함수가 아니라 하나의 스레드로서 생을 마침
'정글캠프-WIL > 서브아이템' 카테고리의 다른 글
| Pintos - VM [vm.h 학습] (0) | 2026.05.11 |
|---|---|
| Pintos - 사용자 프로그램 흐름 (0) | 2026.05.11 |
| Pintos - 어떻게 부팅해서 운영체제가 되는가? [1] (0) | 2026.04.30 |
| [C언어] - Robust I/O 함수 (0) | 2026.04.24 |
| 하네스 엔지니어링 - 이해의 폭을 넓히기 위해 (0) | 2026.04.10 |