들어가며
Process를 공부하면서 단순히 "실행 중인 프로그램"이라는 정의를 외우기보다, 프로그램이 어떻게 프로세스가 되는지, OS가 프로세스를 어떻게 메모리에 프로세스 자원을 할당하고 관리하는지, 그리고 프로세스들이 어떻게 협력하는지를 이해하는 데 집중했다. 이 글은 그 과정을 정리한 기록입니다.
1. 프로세스란 무엇인가?
프로그램 vs 프로세스
프로그램은 디스크에 저장된 정적인 파일이다. 실행 코드가 담긴 실행 파일(binary)일 뿐이며, 그 자체로는 아무 일도 하지 않는다.
프로세스는 실행 중인 프로그램(program in execution)이다. 프로그램이 메모리에 적재되어 CPU가 실제로 명령어를 실행하기 시작하면 프로세스가 된다. 즉 같은 프로그램을 두 번 실행하면 프로세스가 두 개 생긴다.
프로세스의 메모리 구조

프로세스가 메모리에 올라갈 때 주소 공간은 4개 영역으로 나뉜다.
- Text (Code) 영역: 실행할 프로그램의 기계어 명령어가 저장되는 곳. 컴파일된 코드 자체가 여기 들어가고, 실행 중에 내용이 바뀌면 안 되므로 Read-Only로 보호된다. 여러 프로세스가 같은 프로그램을 실행하면 이 영역을 공유할 수 있다.
- Data 영역: 전역 변수(global variable)와 정적 변수(static variable) 가 저장된다. 프로그램 시작 시 할당되고 종료 시 해제. 초기값이 있는 변수(initialized)와 없는 변수(uninitialized, BSS)로 세분화된다.
- Heap 영역: 동적으로 할당되는 메모리 공간. C의 malloc(), C++의 new, Java의 new 로 할당하는 메모리가 여기 들어가며 아래에서 위로(↑) 자랍니다. 프로그래머가 직접 해제(free())하지 않으면 메모리 누수(Memory Leak) 가 발생
- Stack 영역: 함수 호출 정보가 저장되는 공간. 함수를 호출할 때마다 Stack Frame 하나가 쌓이고, 함수가 반환되면 사라짐. Stack Frame에는 지역 변수, 매개변수, 리턴 주소가 들어가며 위에서 아래로(↓) 자란다. 재귀 호출이 너무 깊어지면 Heap과 충돌해 Stack Overflow가 발생한다.
이후에 나올 내용이지만, 멀티스레드 관점에서 같은 프로세스의 스레드들은 Text, Data, Heap을 공유하고 Stack, Registers, Thread ID는 독립적으로 가진다. Stack이 독립적인 이유는 각 스레드가 자신만의 실행 흐름과 지역 변수를 가져야 하기 때문이다.
Virtual Memory, Physical Memory

프로세스가 사용하는 메모리 주소는 사실 실제 물리 메모리의 주소가 아니다. OS는 각 프로세스에게 Virtual Address Space(가상 주소 공간) 를 제공한다. 프로세스 입장에서는 자기가 메모리 전체를 독점하는 것처럼 보이지만, 실제로는 여러 프로세스가 Physical Memory(물리 메모리) 를 나눠 쓰고 있다.
- Virtual Memory(가상 메모리) 는 프로세스가 바라보는 논리적인 주소 공간이다. 각 프로세스는 0번지부터 시작하는 독립적인 가상 주소 공간을 갖는다. 실제로 메모리가 얼마나 있는지, 다른 프로세스가 어디에 올라가 있는지 전혀 신경 쓸 필요가 없다. 덕분에 프로그램 작성이 단순해지고, 프로세스 간 메모리 침범이 불가능해진다.
- Physical Memory(물리 메모리) 는 실제 DRAM 칩 위의 주소 공간이다. 여러 프로세스의 가상 메모리가 이곳에 섞여 적재된다. 물리 메모리가 부족하면 OS는 일부 페이지를 디스크(Swap Space)로 내보낸다.
- Page Table(페이지 테이블) 은 가상 주소와 물리 주소를 연결하는 변환 지도다. 가상 메모리를 일정 크기(보통 4KB)의 Page 단위로 나누고, 물리 메모리도 같은 크기의 Frame 단위로 나눈 뒤, Page Table이 "가상 Page N → 물리 Frame M" 매핑을 저장한다. CPU가 가상 주소로 메모리에 접근할 때마다 MMU(Memory Management Unit) 가 Page Table을 참조해 자동으로 물리 주소로 변환한다. 만약 접근하려는 가상 Page가 물리 메모리에 없으면(디스크에 있거나 아직 할당 안 됨) Page Fault 가 발생하고, OS가 해당 Page를 디스크에서 물리 메모리로 불러온다. 이것이 COW에서 쓰기 시도 시 Page Fault가 발생하는 원리이기도 하다.
Vertual Machine와의 유사성
실제 자원은 하나인데, 각 주체가 독점하는 것처럼 착각하게 만든다
Vertual Memoy는 “가상화”라는 관점에서 이 전 글에서 다루었던 Vertual Macine(가상 컴퓨터)와 비슷한 원리이다. 즉 추상화 대상의 레벨만 다를 뿐, 핵심 원리는 똑같다.
- Virtual Memory 는 하나의 OS 안에서 프로세스에게 "너만의 메모리가 있는 척" 해주는 것
- Virtual Machine 은 하나의 하드웨어 위에서 OS 자체에게 "너만의 컴퓨터가 있는 척" 해주는 것
2. 프로세스 상태
5가지 상태와 상태 전이
OS는 프로세스를 5가지 상태로 관리한다.

New: 프로세스가 생성되고 있는 상태. PCB 할당, 메모리 할당 등 초기화 진행 중
Ready: CPU를 받을 준비가 된 상태. Ready Queue에서 스케줄러를 기다린다.
Running: CPU를 받아 명령어를 실행 중인 상태. 단일 코어에서는 한 번에 하나의 프로세스만 Running 상태다.
Waiting (Blocked): I/O 완료, 이벤트 발생 등을 기다리는 상태. CPU를 사용하지 않는다.
Terminated: 실행이 끝난 상태. 자원은 해제되지만 PCB는 부모가 wait()를 호출할 때까지 남는다.
CPU 스케줄링이 발생하는 4가지 상황
- Running → Waiting (예: I/O 요청) — 비선점(Non-preemptive): 프로세스가 자발적으로 CPU 반납
- Running → Ready (예: Time Slice 소진) — 선점(Preemptive): OS가 강제로 CPU 회수
- Waiting → Ready (예: I/O 완료) — 선점(Preemptive): 우선순위가 바뀌어 재스케줄링 필요
- Running → Terminated (프로세스 종료) — 비선점(Non-preemptive): 자발적 종료
비선점이라고 해서 CPU가 노는 게 아니다. 자발적 반납 이후에도 스케줄러는 즉시 Ready Queue에서 다음 프로세스를 선택한다. 선점/비선점은 CPU를 어떻게 회수했는지의 문제이고, 스케줄링은 비어있는 CPU를 누구한테 줄지의 문제로 서로 다른 차원이다.
3. PCB와 Context Switch
PCB (Process Control Block)

는 OS가 프로세스를 관리하기 위해 유지하는 자료구조다. TCB(Task Control Block)는 PCB와 동일한 개념으로 이름만 다르다. Linux에서는 task_struct 구조체로 구현한다.
PCB에 포함되는 정보는 다음과 같다.
- Process ID (PID): 프로세스 고유 식별자
- Process State: 현재 상태 (New, Ready, Running, Waiting, Terminated)
- Program Counter (PC): 다음에 실행할 명령어 주소
- CPU Registers: 레지스터 값들 (Context Switch 시 저장/복원)
- Memory Management Info: 페이지 테이블, 메모리 할당 정보
- Scheduling Info: 우선순위, 스케줄링 큐 포인터
- I/O Status: 열린 파일 목록, I/O 장치 정보
- Accounting Info: CPU 사용 시간, 실행 시간 등
Context Switch

Context Switch는 CPU가 현재 실행 중인 프로세스에서 다른 프로세스로 전환하는 과정이다.
프로세스 P1 실행 중
↓ 인터럽트 발생 (I/O 요청 or Timer Interrupt)
1. P1의 CPU 상태 → P1의 PCB에 저장 (PC, Registers 등)
2. 스케줄러: Ready Queue에서 P2 선택
3. P2의 PCB에서 CPU 상태 복원
↓
프로세스 P2 실행 시작
Context Switch 중에는 CPU가 유용한 작업을 못하므로 순수한 오버헤드다. 이 비용을 Dispatch Latency라고 하며 약 1~10μs 정도 걸린다.
참고로, Thread가 Process보다 Context Switch 비용이 낮은 이유는 메모리 공간이 공유되어 있어 교체할 정보가 적기 때문이다.
4. 프로세스 생성과 종료
프로세스 생성 - fork()와 COW

Unix/Linux에서 프로세스 생성은 fork() 시스템 콜로 이루어진다. fork()는 현재 프로세스(부모)를 복사해 새 프로세스(자식)를 만든다. fork() 이후 자식 프로세스에서 exec()를 호출하면 새 프로그램을 로드해 실행한다.
(참고) fork vs vfork vs exec
"fork로 복사하고, exec로 갈아엎고, vfork는 exec 직전의 불필요한 복사를 생략한 것"

- fork(): 부모 프로세스를 통째로 복사해서 자식 프로세스를 만든다. 위에서 설명한 COW(Copy-on-Write) 덕분에 실제 메모리 복사는 쓰기 시점까지 미룬다. 부모와 자식은 각자 독립적으로 실행을 이어나간다.
fork()반환값이 0이면 자식, 양수(자식 PID)이면 부모다. - vfork():
fork()의 최적화 버전이다. 자식이exec()또는exit()을 호출할 때까지 부모의 메모리를 그대로 공유(복사 없음) 하고, 부모는 그 동안 일시 정지된다. 어차피exec()으로 덮어쓸 메모리를 굳이 COW로 관리할 필요가 없을 때 쓰는 방식이다. 현대 OS에서는fork()+ COW 자체가 충분히 빨라서vfork()를 쓸 일이 거의 없다. - exec(): 현재 프로세스의 메모리 공간을 새 프로그램으로 덮어쓴다. 새 프로세스를 만드는 게 아니라 기존 프로세스가 다른 프로그램으로 변신하는 것이다. PID는 그대로 유지된다. 보통
fork()직후 자식 프로세스에서 호출하는 패턴(fork+exec)으로 쓰인다.
fork() 직후 Virtual Memory는 독립적으로 분리되지만 실제 물리 메모리는 공유된다. Page Table Entry의 권한이 Read-Only로 설정되어 있어, 쓰기를 시도하면 Page Fault가 발생한다. 이것이 COW(Copy-on-Write) 방식이다. OS는 Page Fault 발생 시 새 물리 페이지를 할당하고 데이터를 복사한 뒤 Page Table을 업데이트한다. 쓰기가 실제로 발생할 때만 물리 메모리를 분리하여 불필요한 메모리 복사를 방지한다.
프로세스 종료 - exit, abort, wait
프로세스 종료에는 두 가지 방식이 있다.
- 정상 종료 (exit): 프로세스가 마지막 명령문을 실행하고 exit() 시스템 콜을 호출한다. 자식은 부모에게 exit status를 반환하고, OS는 프로세스의 자원을 해제한다.
- 강제 종료 (abort): 부모 프로세스가 자식 프로세스를 강제로 종료시킬 수 있다. 자식이 할당받은 자원을 초과하여 사용하거나, 자식에게 시킨 작업이 더 이상 필요하지 않을 때 발생한다. kill() 같은 시스템 콜로 종료 신호를 보낸다.
wait(): 부모 프로세스가 자식 프로세스의 종료를 기다리는 시스템 콜이다. 부모는 wait()를 호출하면 block 상태가 되어 자식이 exit()으로 종료될 때까지 대기한다. 자식이 종료되면 exit status를 받아오고 OS는 자식의 PCB를 완전히 삭제한다.
일부 OS는 Cascading Termination을 지원한다. 부모가 종료되면 모든 자식, 손자 프로세스도 연쇄적으로 종료시키는 방식이다.
좀비 프로세스와 고아 프로세스

- 좀비 프로세스(Zombie Process): 자식 프로세스가 종료(exit)되었지만 부모 프로세스가 wait()를 호출하지 않아, 자식의 PCB와 exit status가 프로세스 테이블에 남아있는 상태다. CPU나 메모리 같은 실제 자원은 이미 해제되었지만 프로세스 테이블의 슬롯을 차지한다. 부모가 나중에 wait()를 호출하거나 부모가 종료되면 정리된다.
- 고아 프로세스(Orphan Process): 부모 프로세스가 자식보다 먼저 종료된 상태다. OS가 고아 프로세스를 init 프로세스(PID 1)에 입양시키고, init이 주기적으로 wait()를 호출하여 정리해주므로 문제가 되지 않는다.
5. Cooperating Processes와 IPC
Independent vs Cooperating
프로세스는 다른 프로세스와의 관계에 따라 두 가지로 구분된다.
- Independent Process(독립 프로세스): 다른 프로세스의 실행에 영향을 주거나 받지 않는 프로세스다. 완전히 독립적으로 실행하며 데이터를 공유하지 않는다.
- Cooperating Process(협력 프로세스): 다른 프로세스의 실행에 영향을 주거나 받을 수 있는 프로세스다. 서로 협력하여 작업을 수행하며, 이를 위해 IPC(프로세스 간 통신)가 필수다.
Cooperating Process를 사용하는 이유는 세 가지다.
- 첫째, Information Sharing으로 여러 프로세스가 공유 파일 등의 정보를 함께 사용할 수 있다.
- 둘째, Computation Speed-up으로 작업을 여러 subtask로 나눠 병렬 실행하면 처리 속도가 향상된다.
- 셋째, Modularity로 시스템 기능을 독립적인 작업들로 분리하여 구조화할 수 있다.
Producer-Consumer Pattern
Cooperating Process의 전형적인 예제로, 두 IPC 방식 모두로 구현 가능하지만 각 방식의 트레이드오프(속도 vs 안전성)에 따라 선택된다.
IPC (Inter-Process Communication)
Cooperating Process들은 데이터를 주고받기 위해 IPC가 필요하다. IPC의 방식은 두 가지다.

1) Shared Memory (공유 메모리)
프로세스들이 같은 메모리 영역을 공유한다. 직접 메모리를 읽고 쓰므로 속도가 빠르다.
하지만 여러 프로세스가 동시에 접근할 때 Race Condition이 발생할 수 있어 동기화가 필요하다.
Producer-Consumer Problem이 동기화 방법의 대표적인 예시로, Producer는 데이터를 공유 버퍼에 생산하고 Consumer는 버퍼에서 소비한다. 동기화를 위해 Semaphore, Mutex 같은 도구가 필요하다.
2) Message Passing (메시지 전달)
OS가 중간에서 메시지 전달을 관리한다. 프로세스들은 직접 메모리를 공유하지 않고 send()/receive() 시스템 콜로 통신한다. OS가 동기화를 자동으로 처리하므로 Race Condition이 발생하지 않는다. 대신 커널을 거치는 오버헤드가 있어 Shared Memory보다 느리다. Microkernel 구조에서 주로 사용하는 방식이다.
| 항목 | Shared Memory | Message Passing |
|---|---|---|
| 속도 | 빠름 | 느림 (커널 개입) |
| 동기화 | 직접 처리 필요 | OS가 자동 처리 |
| Race Condition | 발생 가능 | 없음 |
| 사용 예시 | Producer-Consumer | Microkernel IPC |
전체 정리
프로세스는 실행 중인 프로그램으로, Text/Data/Heap/Stack 4개의 메모리 영역을 가진다. OS는 PCB를 통해 각 프로세스의 상태를 관리하며, Context Switch를 통해 프로세스 간 CPU 전환을 수행한다.
프로세스 생성은 fork() + COW 방식으로 이루어지며, 물리 메모리는 실제 쓰기가 발생할 때만 분리된다. 종료 시에는 exit()으로 자발적으로 종료하거나 부모의 abort()로 강제 종료되며, 부모는 wait()로 자식의 종료를 기다려야 좀비 프로세스 발생을 방지할 수 있다.
Cooperating Process들은 Shared Memory 또는 Message Passing을 통해 협력한다. Shared Memory는 빠르지만 동기화 문제가 있고, Message Passing은 느리지만 OS가 동기화를 보장한다. 이 동기화 문제를 해결하기 위한 도구들이 이후 Synchronization에서 다루는 Mutex, Semaphore, Monitor다.
'Computer Science > OS(운영 체제)' 카테고리의 다른 글
| [OS] 4. Thread(쓰레드)란? (0) | 2026.05.03 |
|---|---|
| [OS] 2. Operating System Structures (0) | 2026.04.29 |
| [OS] 1. Introduction to Operating Systems (0) | 2026.04.29 |
경이로운 BE 개발자가 되기 위한 프로그래밍 공부 기록장
도움이 되었다면 "❤️" 또는 "👍🏻" 해주세요! 문의는 아래 이메일로 보내주세요.