Process Lifecycle & PCB
프로그램이 실행체로 전환되는 생성과 종료 단계, 그리고 이를 관리하기 위해 커널이 유지하는 프로세스 제어 블록(PCB)의 구조를 다루는 학습 노드입니다.
Article
M
Me
hyunyoun's Blog
operating-systems-system-mechanicsoperating-systemssystem-mechanicsprocessconcurrency-mechanicsprocess-lifecyclepcbos-process9 min read
1. Overview
프로세스 라이프사이클과 PCB 궤적(Process Lifecycle & PCB)은 디스크에 저장된 실행 파일(.exe/ELF)이 커널에 의해 RAM으로 적재되고, PCB(Process Control Block)와 가상 주소 공간을 부여받아 실행 단위로 관리되는 과정을 다룹니다.
학습자는 프로세스가 '준비(Ready) 실행(Running) 대기(Blocked/Waiting)' 상태를 오가는 **상태 천이 머신(State Machine)**을 살펴봅니다. 이어 부모 프로세스가 fork()로 자식 복제본을 만들고 exec()로 실행 이미지를 교체하는 유닉스(UNIX) 프로세스 생성 흐름, 종료된 자식의 상태를 회수하지 않아 남는 좀비(Zombie) 프로세스와 고아(Orphan) 처리를 이해합니다.
2. Scope & Boundaries
In-Scope
- 구조체와 상태(State Physics): PCB(Process Control Block),
task_struct(리눅스), 프로세스 상태 5단계(New, Ready, Running, Blocked/Waiting, Terminated). - 문맥과 스위칭(Context Geometry): 문맥 교환(Context Switch), 레지스터 백업(Save/Restore), 커널 스택(Kernel Stack) 구조.
- 생성과 종료(Lifecycle Syscalls):
fork(),exec(),wait(),exit(), 부모-자식 트리 계층. - 예외적 종료 처리(Anomalies): 좀비 프로세스(Zombie), 고아 프로세스(Orphan),
init(PID 1) 프로세스의 인계 기작.
Out-of-Scope
- 다중 스레드 메모리 공유: 프로세스 안에서 여러 흐름을 가닥가닥 쪼개는 스레딩 모델(Pthreads, TCB 공유) 03-02-02. Threading Models 영역.
- 프로세스 간 메모리 통신망(IPC): 두 프로세스가 데이터를 주고받기 위한 파이프(Pipe), 공유 메모리(Shared Memory) 03-02-04. Inter-Process Communication (IPC) 영역.
Boundaries
- Program vs. Process (03-02-01): 프로그램(Program)은 하드디스크에 저장된 정적 실행 파일이고, 프로세스(Process)는 그 파일이 RAM에 적재되어 CPU 시간, 레지스터, 스택, 열린 파일 같은 실행 상태를 갖는 동적 실행 단위입니다.
3. Counterexample
- 좀비 프로세스 누적과 PID 고갈 (Zombie Accumulation): C 서버 프로그램에서 무한
while루프 안에 클라이언트 요청이 올 때마다fork()로 자식 프로세스를 만들면서, 종료된 자식을 회수하는wait()시스템 콜을 호출하지 않으면 문제가 됩니다. 자식들이 일을 끝내고 Terminated 상태가 되었더라도 부모가 종료 상태를 회수하지 않으면 커널 메모리에 PCB 일부가 좀비 상태로 남습니다. 이 상태가 누적되면 PID와 커널 자원이 고갈되어 새 프로세스를 만들기 어려워집니다. - 포크 폭탄 (Fork Bomb):
while(1) { fork(); }같은 코드를 실행하면 자식이 다시 자식을 만들며 짧은 시간 안에 매우 많은 프로세스가 생깁니다. CPU는 수많은 프로세스 사이에서 [백업 복구]를 반복하는 컨텍스트 스위칭(Context Switch)에 시간을 쓰고, 메모리와 PID 공간도 빠르게 소모됩니다. 결국 시스템은 쓰레싱(Thrashing)에 빠져 응답성이 크게 떨어집니다.
4. Prerequisites
- 시스템 콜의 관문 (Basic):
fork()나exec()가 유저 공간에서 커널 공간(Ring 0)으로 어떻게 점프하는지 검문소의 궤적을 알아야 합니다. (03-01-02 System Call Interface) - 가상 메모리 샌드박스 (Recommended): 프로세스마다 서로의 주소를 훔쳐볼 수 없도록 완전히 분리된 4GB짜리 환상(Virtual Memory)을 제공받는다는 물리적 분리 철학이 필요합니다. (03-03-01 Virtual Memory)
5. Learning Map
6. Learning Topics
Basic
Core Topic 01: PCB와 문맥 교환 (PCB & Context Switch)
- Why to Learn: CPU 코어가 한정되어 있어도 여러 프로그램이 동시에 실행되는 것처럼 보이는 이유는 커널이 짧은 시간 단위로 실행 문맥을 저장하고 교체하기 때문입니다. PCB와 문맥 교환의 비용을 이해하기 위해서입니다.
- What to Learn:
- Concepts: 문맥 교환(Context Switch), PCB(Process Control Block, 리눅스의
task_struct), 레지스터 덤프. - Skills: 오버헤드 프로파일링, 하드웨어 컨텍스트 핑퐁.
- Tools: 리눅스
/proc/[pid]/status. - Trade-offs: 문맥 교환 주기를 0.001초(1ms)처럼 짧게 잡으면 응답성은 좋아질 수 있지만, CPU가 실제 작업보다 레지스터 백업/복구(PCB 저장)에 더 많은 시간을 쓰게 되어 쓰레싱(Thrashing)이 발생할 수 있습니다.
- Concepts: 문맥 교환(Context Switch), PCB(Process Control Block, 리눅스의
- How to Learn:
- 1단계: 커널 공간에 크롬 브라우저의 현재 상태(PID=100, PC 주소, 범용 레지스터, 오픈한 파일 목록)를 저장하는 PCB 구조를 분석합니다.
- 2단계: 타이머 인터럽트가 발생하면 커널이 현재 프로세스의 CPU 레지스터를 PCB에 저장(Save)하고, 다음 프로세스의 PCB에서 저장된 레지스터를 CPU에 복원(Restore)하는 흐름을 추적합니다.
- Implement: 파이썬 딕셔너리로
PCB_A = {'IP': 100, 'R1': 5}와PCB_B = {'IP': 200, 'R1': 9}를 만듭니다. 글로벌CPU_State변수가 있을 때Context_Switch()함수가 호출되면CPU_State값을PCB_A에 백업하고PCB_B의 값을 꺼내어 덮어쓰는 스왑(Swap) 로직의 콘솔 덤프를 작성합니다.
Recommended
Core Topic 02: 프로세스 상태 천이 머신 (Process State Machine)
- Why to Learn: 프로세스가 디스크에서 10GB 파일을 읽는 동안 CPU를 붙잡고 있으면 자원이 낭비됩니다. I/O 대기 중인 프로세스를 Blocked 상태로 옮기고 다른 프로세스에게 CPU를 배정하는 커널의 상태 전이 원리를 이해하기 위해서입니다.
- What to Learn:
- Concepts: 프로세스 5상태(New, Ready, Running, Blocked/Waiting, Terminated).
- Skills: 레디 큐(Ready Queue), 웨이트 큐(Wait Queue / I/O Queue) 매핑.
- Tools:
htop(R, S, D, Z 상태 플래그). - Trade-offs: 디스크를 읽을 때 프로세스를 Blocked 큐로 옮기면 CPU를 다른 작업에 쓸 수 있어 효율적입니다. 다만 디스크 읽기가 끝난 뒤 인터럽트를 받고 Ready 큐로 돌아와 다시 스케줄링되기까지의 지연은 피할 수 없습니다.
- How to Learn:
- 1단계: Running Ready: 실행 중인 프로세스가 타임 슬라이스를 모두 사용하면 커널 타이머가 이를 Ready 큐 뒤로 보내고 다음 프로세스를 선택하는 흐름을 분석합니다.
- 2단계: Running Blocked: 브라우저가 네트워크 I/O를 요청하면 커널은 해당 프로세스를 Wait 큐(Blocked)로 옮기고, 랜카드 인터럽트가 도착할 때 Ready 큐로 되돌리는 흐름을 살펴봅니다.
- Implement: 무한
while루프 메인 스케줄러.Queue_Ready와Queue_Wait리스트를 오가며 프로세스 객체의State변수를 천이시킵니다. 틱(Tick)마다Ready의 첫 번째 프로세스를Running으로 바꾸고,I_O_Request()가 발생하면Wait로 옮긴 뒤 다음 프로세스를 선택하는 콘솔 애니메이션을 작성합니다.
Practical
Core Topic 03: fork()와 exec() 기반 프로세스 생성 (Fork & Exec Genesis)
- Why to Learn: 유닉스(UNIX)는 새 프로세스를 만들 때
fork()로 현재 프로세스를 복제하고, 필요하면exec()로 실행 이미지를 교체합니다. 이 방식이 Windows의CreateProcess와 어떻게 다르고 왜 COW(Copy-On-Write) 최적화와 결합되는지 이해하기 위해서입니다. - What to Learn:
- Concepts:
fork()시스템 콜,execve()실행 이미지 교체, COW(Copy-On-Write) 최적화, PID 리턴 분기. - Skills: 부모-자식 트리(Tree) 계층 제어, 멀티프로세스 서버 포킹(Preforking).
- Tools: 리눅스
pstree. - Trade-offs:
fork()가 실제로 부모의 1GB RAM 데이터를 즉시 모두 복사한다면 비용이 매우 큽니다. 리눅스는 COW(Copy-On-Write)를 사용해 페이지 테이블을 공유하다가 누군가 데이터를 수정할 때만 물리 메모리를 복사함으로써 생성 비용을 줄입니다.
- Concepts:
- How to Learn:
- 1단계: Fork: 부모가
fork()를 호출하면 커널은 부모의 PCB와 주소 공간 메타데이터를 기반으로 자식 프로세스를 만들고,fork()함수의 리턴값을 부모에게는 자식 PID(예: 3000), 자식에게는 0으로 돌려줍니다. 같은 코드가 반환값에 따라 다른 경로로 실행되는 흐름을 분석합니다. - 2단계: Exec: 자식 프로세스가
if (pid == 0) exec("chrome.exe");를 호출하면, 기존 실행 이미지를 버리고 디스크에서 새 실행 파일을 적재합니다. 같은 PID가 새 프로그램 이미지로 바뀌는 과정을 살펴봅니다.
- 1단계: Fork: 부모가
- Implement: C/C++(또는
os.fork()파이썬) 코드로pid = fork()분기점 통과 스크립트 작성. 터미널 창에pid > 0인 부모 프로세스는 "I am Father"를 출력하고 루프를 돌며,pid == 0인 자식 공간에서는os.execvp("ls", ["ls", "-l"])로 실행 이미지를 교체해 다른 리눅스 명령어 결과를 출력하는 흐름을 확인합니다.
Advanced
Core Topic 04: 종료 상태 인계와 좀비/고아 처리 (Zombies, Orphans & Wait)
- Why to Learn: 멀티프로세스 프로그래밍에서 종료된 자식의 상태를 제때 회수하지 않으면 메모리 누수보다 PID 누수(PID Exhaustion)가 문제가 됩니다.
wait()와SIGCHLD를 통해 종료 상태를 회수하는 원리를 이해하기 위해서입니다. - What to Learn:
- Concepts: 좀비 프로세스(Zombie, Defunct), 고아 프로세스(Orphan),
wait()/waitpid(),init(PID 1) 또는systemd입양 메커니즘. - Skills: 포스트모템(Post-mortem) 회수, 시그널(
SIGCHLD) 비동기 핸들링. - Tools: 리눅스
top의 Z(Zombie) 카운트 감시. - Trade-offs: 자식이 끝났을 때 부모가 동기적으로
wait()를 호출하고 멈춰 있으면 좀비는 생기지 않지만 웹 서버가 다른 접속자를 처리하지 못할 수 있습니다. 반대로 부모가 전혀 기다리지 않으면 응답성은 유지되지만 좀비 프로세스가 누적되어 커널 자원(PCB 슬롯)이 고갈될 수 있습니다. 해결책은SIGCHLD비동기 처리입니다.
- Concepts: 좀비 프로세스(Zombie, Defunct), 고아 프로세스(Orphan),
- How to Learn:
- 1단계: 좀비(Zombie): 자식이
exit()를 호출하고 메모리를 반납했더라도, 커널은 부모가 최종 종료 코드(Exit Status)를 읽을 수 있도록 PCB의 일부 정보를 남겨둡니다. 부모가wait()로 상태를 회수해야 해당 좀비가 Reap됩니다. - 2단계: 고아(Orphan): 자식 프로세스가 계속 실행 중인데 부모가 먼저
exit()하면, 고아가 된 자식은 PID 1번(init또는systemd)으로 인계됩니다. 이후 PID 1번이 종료 상태 회수를 대신 수행합니다.
- 1단계: 좀비(Zombie): 자식이
- Implement:
os.fork()후 자식 프로세스는 2초 뒤exit(0)로 종료하고, 부모는time.sleep(10)으로 일부러wait()를 호출하지 않는 파이썬 예제를 작성합니다. 다른 터미널에서ps aux | grep Z를 실행해 해당 자식 프로세스가[python] <defunct>상태로 남는 현상을 관측합니다.
7. Terminology
8. References
Primary
- [P2] SWEBOK v4.0 - Software Construction / Runtime Efficiency (Execution) — Performance context.
- [P1] CS2023 - OS/Operating System Principles (Processes) — Core requirements.
Secondary
- [Operating Systems: Three Easy Pieces] Remzi — The virtualization of the CPU.
- [Understanding the Linux Kernel] Bovet & Cesati — task_struct internal details.
Industry
- [Linux Manual: fork(2) & execve(2)] — System call implementation rules.
- [Microsoft: Process and Thread Internals (Windows Sysinternals)] — Windows process structure.
9. Final Checklist
Primary
- '프로그램'과 '프로세스'의 물리적 차이점(정적 상태 vs 동적 자원 점유 상태)을 명확히 설명 가능한가? (P1)
- 프로세스가 'Running'에서 'Waiting'으로 전이되는 트리거가 왜 주로 'I/O 요청이나 이벤트 대기'인지 물리적 필연성을 입증할 수 있는가? (P1)
Secondary
- '문맥 교환' 과정에서 왜 CPU의 캐시 메모리 오염(Cache Pollution)이 발생하여 시스템이 잠시 느려지는지 설명할 수 있는가?
-
fork()시스템 콜이 실행되는 동안 커널 내부에서 발생하는 '메모리 페이지 맵 복제' 과정을 가시적으로 도출할 수 있는가?
Industry
- 대규모 서버 설계 시, 과도한 프로세스 생성(Forking)이 왜 커널 스케줄러의 부하를 물리적으로 높여 서비스 지연을 초래하는지 제안할 수 있는가? (SFIA)
- '좀비 프로세스'가 너무 많아질 때, 커널이 더 이상 새로운 프로세스를 만들지 못하는 물리적 한계점(PID exhaustion)을 기술할 수 있는가?