개발/프로젝트

Pintos Project 2 발표 회고: 핀토스 제국은 어떻게 신민을 통제했는가

cedis 2026. 5. 7. 14:28

운영체제 · Pintos · Project 2 · Week 10

Pintos Project 2 발표 회고: 핀토스 제국은 어떻게 신민을 통제했는가

정글 10주차 발표 자료를 바탕으로 Project 2를 하나의 제국 행정 시스템으로 다시 정리했다.

이번 글에서 다루는 것
  • Project 2 발표 자료를 하나의 제국 세계관과 행정 시스템으로 다시 읽기
  • process, syscall, fd, fork/wait를 각각 신민·검문소·열람증·생산 공장으로 연결하기
  • 85/95 통과라는 결과를 미완성 제국의 행정 지도로 정리하기
Pintos User Process Revolution 발표 표지
Pintos User Process Revolution 발표 표지

1. x86 우주, QEMU 행성, Pintos 제국

이번 발표에서 우리는 Pintos Project 2를 기능 목록으로 설명하지 않았다. 하나의 제국으로 설명했다. x86이라는 우주, QEMU라는 행성, 그 안에 세워진 Pintos 제국. 이 제국의 황제는 커널이고, 유저 프로그램은 황제가 허락한 영토 안에서만 움직이는 신민이다.

초기의 Pintos는 왕궁만 있는 나라였다. 모든 코드가 커널 안에서 움직였고, 모든 권한이 한 장소에 모여 있었다. 빠르고 단순했지만 위험했다. 왕궁 안에서 누군가 실수하면 제국 전체가 무너졌다.

제국의 명분
국가는 시스템이다. Pintos도 시스템이다. 그러므로 Project 2는 자유민주주의 건설이 아니라, 신민을 통제 가능한 형태로 생산하고 관리하는 제국 행정 시스템 구축이었다.
신민의 삶: 새로운 신분을 만드는 제국
신민의 삶: 새로운 신분을 만드는 제국

2. 신민의 탄생: process는 생애 관리 체계다

Pintos에서 신민의 이름은 process다. 신민은 자연발생하지 않는다. `process_create_initd()`에서 첫 신분이 등록되고, `initd()`가 첫 실행 흐름을 맡는다. `process_exec()`는 기존 몸을 버리고 새 실행 파일로 갈아입는 절차다.

`load()`는 ELF를 검증하고 user memory에 올린다. `setup_stack()`과 `setup_args()`는 신민이 들고 갈 짐인 `argc`, `argv`를 user stack에 배치한다. 모든 준비가 끝나면 `do_iret()`가 신민을 왕궁 밖, 즉 user mode로 내려보낸다.

process life cycle과 Pintos 구현 흐름
process life cycle과 Pintos 구현 흐름

3. 왕궁 봉쇄: syscall은 검문소다

제국의 제1국법은 왕궁 봉쇄다. 유저 프로그램은 파일을 열고 싶어도, 글자를 출력하고 싶어도, 자기 생을 마감하고 싶어도 커널 원본에 직접 손을 댈 수 없다. 반드시 syscall이라는 검문소를 지나야 한다.

검문소는 신민을 믿지 않는다. 유저가 넘긴 포인터는 문서가 아니라 도시락폭탄일 수 있다. 주소가 유저 영역인지, 페이지 테이블에 실제로 매핑되어 있는지 확인하지 않으면 커널이 터진다.

syscall gate와 user memory 검열 흐름
syscall gate와 user memory 검열 흐름
구분 제국어 Pintos 구현
요청 민원번호 `f->R.rax`에 들어오는 syscall 번호
서류 검문소에 제출하는 인자 `rdi`, `rsi`, `rdx` 등 syscall argument
검열 국가보안법 `is_crazy_user_address()`, `is_crazy_user_buffer()`
처형 반란 진압 `curtain_call(-1)`로 해당 process만 종료

4. 문서고 통제: fd는 열람증이다

제국의 문서고는 원본을 신민에게 넘기지 않는다. 신민은 `struct file *`를 들고 다니지 않는다. 대신 번호표를 받는다. 이것이 file descriptor, fd다.

fd 0은 stdin, fd 1은 stdout, 일반 파일은 fd 2부터 배정된다. `open()`은 파일을 열지만 원본 포인터를 유저에게 주지 않는다. `fd_table`에 저장하고, 유저에게는 번호만 돌려준다.

fd table과 system call 흐름
fd table과 system call 흐름

5. 복제와 사망: fork는 인간 생산 공장이다

Pintos 제국의 신민은 자연발생하지 않는다. `fork`라는 산아 생산 제도로 노예를 찍어낸다. 하지만 단순 복사는 아니다. 노예는 주인의 기억과 집, 열람증을 이어받아야 하지만, 같은 공간을 공유하면 안 된다.

그래서 제국은 공유 대신 복제를 선택한다. `__do_fork()`는 부모의 register 문맥을 복사하되, 자식의 `rax`는 0으로 조작한다. 같은 사건인데 주인과 노예에게 서로 다른 진실을 배급하는 것이다.

거주지도 복제된다. `pml4_for_each()`가 주인의 영토를 순찰하고, `duplicate_pte()`가 user page만 새 페이지로 베껴준다. 노예는 주인의 집을 공유받는 게 아니라, 똑같이 생긴 독립 수용소를 지급받는다.

fork와 process 생명주기
fork와 process 생명주기
노예증표이자 사망신고서
`child_info`는 누가 누구의 자식인지, 죽었는지, 어떤 exit status를 남겼는지, 부모가 이미 확인했는지를 적어두는 장부다. `wait()`는 남의 노예 장례식에 끼어들 수 없고, 같은 시체를 두 번 회수할 수도 없다.

6. 85/95: 천년제국은 아직 미완성이다

최종 main 기준으로 전체 95개 테스트 중 85개를 통과했다. threads는 18개 모두 통과했고, userprog 기본은 64개 중 58개, filesys/base는 13개 중 9개를 통과했다.

남은 실패는 제국의 미완성 제도다. `rox-simple`, `rox-child`, `rox-multichild`는 실행 파일 금서 보호법, `lg/sm-random`과 `syn-remove`, `syn-write`는 문서고의 더 복잡한 처리, `multi-oom`은 자원 고갈 상황에서의 생존 정책과 연결된다.

한 줄 정리

Project 2는 syscall 몇 개를 만든 과제가 아니었다. 믿을 수 없는 신민에게 제한된 자유를 주기 위해, 검문소·열람증·생산 공장·사망신고소를 세우는 일이었다.