콘텐츠로 바로가기

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) \rightarrow 실행(Running) \rightarrow 대기(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 공유) \rightarrow 03-02-02. Threading Models 영역.
  • 프로세스 간 메모리 통신망(IPC): 두 프로세스가 데이터를 주고받기 위한 파이프(Pipe), 공유 메모리(Shared Memory) \rightarrow 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는 수많은 프로세스 사이에서 [백업 \rightarrow 복구]를 반복하는 컨텍스트 스위칭(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

Sequence Core Cluster Objective & Description Evidence (BoK)
1 PCB & Context Switch PCB에 저장되는 실행 상태와 레지스터를 저장/복원하는 문맥 교환 과정을 이해합니다. P1
2 Process State Machine Running, Blocked, Ready 상태 사이를 오가는 프로세스 상태 천이를 분석합니다. P5
3 Fork & Exec Genesis fork()로 프로세스를 복제하고 exec()로 실행 이미지를 교체하는 유닉스 생성 흐름을 이해합니다. Industry
4 Zombies & Orphans 부모가 wait()를 호출하지 않아 남는 좀비 프로세스와, 부모가 먼저 종료되어 PID 1번으로 인계되는 고아 처리를 이해합니다. Industry

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)이 발생할 수 있습니다.
  • 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) 로직의 콘솔 덤프를 작성합니다.

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 \rightarrow Ready: 실행 중인 프로세스가 타임 슬라이스를 모두 사용하면 커널 타이머가 이를 Ready 큐 뒤로 보내고 다음 프로세스를 선택하는 흐름을 분석합니다.
    • 2단계: Running \rightarrow Blocked: 브라우저가 네트워크 I/O를 요청하면 커널은 해당 프로세스를 Wait 큐(Blocked)로 옮기고, 랜카드 인터럽트가 도착할 때 Ready 큐로 되돌리는 흐름을 살펴봅니다.
  • Implement: 무한 while 루프 메인 스케줄러. Queue_ReadyQueue_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)를 사용해 페이지 테이블을 공유하다가 누군가 데이터를 수정할 때만 물리 메모리를 복사함으로써 생성 비용을 줄입니다.
  • How to Learn:
    • 1단계: Fork: 부모가 fork()를 호출하면 커널은 부모의 PCB와 주소 공간 메타데이터를 기반으로 자식 프로세스를 만들고, fork() 함수의 리턴값을 부모에게는 자식 PID(예: 3000), 자식에게는 0으로 돌려줍니다. 같은 코드가 반환값에 따라 다른 경로로 실행되는 흐름을 분석합니다.
    • 2단계: Exec: 자식 프로세스가 if (pid == 0) exec("chrome.exe");를 호출하면, 기존 실행 이미지를 버리고 디스크에서 새 실행 파일을 적재합니다. 같은 PID가 새 프로그램 이미지로 바뀌는 과정을 살펴봅니다.
  • 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 비동기 처리입니다.
  • How to Learn:
    • 1단계: 좀비(Zombie): 자식이 exit()를 호출하고 메모리를 반납했더라도, 커널은 부모가 최종 종료 코드(Exit Status)를 읽을 수 있도록 PCB의 일부 정보를 남겨둡니다. 부모가 wait()로 상태를 회수해야 해당 좀비가 Reap됩니다.
    • 2단계: 고아(Orphan): 자식 프로세스가 계속 실행 중인데 부모가 먼저 exit()하면, 고아가 된 자식은 PID 1번(init 또는 systemd)으로 인계됩니다. 이후 PID 1번이 종료 상태 회수를 대신 수행합니다.
  • Implement: os.fork() 후 자식 프로세스는 2초 뒤 exit(0) 로 종료하고, 부모는 time.sleep(10) 으로 일부러 wait()를 호출하지 않는 파이썬 예제를 작성합니다. 다른 터미널에서 ps aux | grep Z 를 실행해 해당 자식 프로세스가 [python] <defunct> 상태로 남는 현상을 관측합니다.

7. Terminology

Term (EN / ko, abbr) 1문장 정의 단계(기본/권장/실무/심화) 역할/맥락 관련 개념 유사/대비/함께 사용 오해 포인트 Evidence(Primary/Secondary/Industry) Flags(core)
Process 실행 파일이 메모리에 적재되어 CPU를 할당받아 동작 중인 물리적 실행 주체입니다. 기본 관리 단위 Code / Program Thread 정적인 '파일'과 혼동 금지 P1:CS2023 core
PCB 프로세스의 모든 물리적 상태 정보를 담고 있는 커널 내부의 데이터 구조체입니다. 기본 장부/사물함 registers / task_struct TCB '사용자 메모리'에 있지 않음 P1:CS2023 core
Context Switch CPU의 제어권을 한 프로세스에서 다른 프로세스로 물리적으로 넘겨주는 과정입니다. 추천 전환 비용 Overhead / PCB Mode Switch '단순한 점프'보다 훨씬 무거움 P2:SWEBOK core
COW (Copy-on-Write) 자식 프로세스 생성 시 실제 데이터 수정이 일어날 때만 물리 메모리를 복사하는 최적화 기법입니다. 실무 메모리 절약 fork / Page Fault Deep Copy '한 번에 다 복사'하지 않음 Industry/Linux core

8. References

Primary

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)을 기술할 수 있는가?

OS Process & Concurrency

1 / 6