프로세스
운영체제로부터 자원을 할당받아 실행 중인 프로그램의 인스턴스입니다.
프로그램 vs 프로세스
[ 프로그램 ] [ 프로세스 ]
디스크에 저장된 실행 파일 메모리에 올라가 실행 중인 상태
정적, 생명 없음 동적, 생명주기 있음
app.exe (디스크)
│ 더블클릭 (실행)
▼
┌─── 프로세스 (PID: 1234) ───┐
│ 메모리 할당 │
│ CPU 시간 할당 │
│ 파일 핸들, 소켓 등 자원 │
└─────────────────────────────┘
비유:
프로그램 = 요리 레시피 (종이에 적힌 것)
프로세스 = 실제로 요리하고 있는 행위 (재료, 불, 도구 사용 중)
같은 레시피로 동시에 두 요리 가능 = 같은 프로그램으로 프로세스 여러 개 생성 가능
프로세스의 메모리 구조
┌─────────── 프로세스 메모리 ───────────┐
│ │
│ ┌─── Stack ───┐ 높은 주소 │
│ │ 지역 변수 │ ↓ │
│ │ 매개변수 │ │
│ │ 리턴 주소 │ │
│ └──────────────┘ │
│ ↓ 증가 │
│ │
│ ↑ 증가 │
│ ┌─── Heap ────┐ │
│ │ 동적 할당 │ │
│ │ (malloc, new)│ │
│ └──────────────┘ │
│ │
│ ┌─── Data ────┐ │
│ │ 전역 변수 │ │
│ │ static 변수 │ │
│ └──────────────┘ │
│ │
│ ┌─── Code(Text) ┐ 낮은 주소 │
│ │ 실행할 명령어 │ │
│ │ (기계어 코드) │ │
│ └────────────────┘ │
└───────────────────────────────────────┘
| 영역 | 저장 내용 | 특징 |
|---|---|---|
| Code (Text) | 실행할 기계어 명령어 | 읽기 전용, 공유 가능 |
| Data | 전역 변수, static 변수 | 프로그램 시작 시 할당 |
| Heap | 동적 할당 (malloc, new) | 런타임에 크기 변동, 위로 증가 |
| Stack | 지역 변수, 함수 호출 정보 | 함수 호출마다 push/pop, 아래로 증가 |
프로세스가 OS에서 받는 자원
┌─────── OS (운영체제) ───────┐
│ │
│ 프로세스 생성 요청 들어옴 │
│ │ │
│ ▼ │
│ ┌─ 할당하는 자원 ──────┐ │
│ │ ① 독립된 메모리 공간 │ │
│ │ ② CPU 시간 (스케줄링) │ │
│ │ ③ PID (고유 식별자) │ │
│ │ ④ 파일 디스크립터 │ │
│ │ ⑤ 네트워크 소켓 │ │
│ │ ⑥ 환경 변수 │ │
│ └──────────────────────┘ │
└──────────────────────────────┘
프로세스의 상태 (Process State)
생성
│
▼
┌──── New ─────┐
│ 프로세스 생성 │
└──────┬───────┘
│ admit
▼
┌──── Ready ────┐ ◄──── 타이머 만료 (Preemption)
│ CPU 대기 중 │ ◄──── I/O 완료
└──────┬────────┘
│ dispatch (CPU 할당)
▼
┌──── Running ──┐
│ CPU에서 실행 중 │
└──┬─────┬──────┘
│ │
│ │ I/O 요청, sleep 등
│ ▼
│ ┌──── Waiting (Blocked) ──┐
│ │ I/O 완료를 기다리는 중 │
│ └─────────────────────────┘
│
│ exit
▼
┌──── Terminated ──┐
│ 실행 완료, 자원 해제│
└──────────────────┘
| 상태 | 설명 |
|---|---|
| New | 프로세스 생성 중 |
| Ready | 실행 준비 완료, CPU 할당 대기 |
| Running | CPU를 점유하여 실행 중 |
| Waiting | I/O 등 외부 이벤트 대기 중 |
| Terminated | 실행 완료, 자원 반납 |
PCB (Process Control Block)
OS가 각 프로세스를 관리하기 위해 유지하는 데이터 구조입니다.
┌──── PCB (프로세스 1234) ────┐
│ PID: 1234 │
│ 상태: Running │
│ PC(Program Counter): 0x04A2 │
│ 레지스터 값: {eax, ebx...}│
│ 메모리 정보: 페이지 테이블 │
│ 열린 파일 목록: fd 0, 1, 2 │
│ CPU 사용 시간: 150ms │
│ 우선순위: 5 │
└─────────────────────────────┘
Context Switch(문맥 교환) 시 현재 프로세스의 PCB를 저장하고, 다음 프로세스의 PCB를 복원합니다.
프로세스 간 독립성
각 프로세스는 독립된 메모리 공간을 가지므로 서로의 메모리에 직접 접근할 수 없습니다.
┌── 프로세스 A (PID: 1234) ──┐ ┌── 프로세스 B (PID: 5678) ──┐
│ 가상 주소 공간 │ │ 가상 주소 공간 │
│ ┌────────────────┐ │ │ ┌────────────────┐ │
│ │ Code │ Data │ │ │ │ Code │ Data │ │
│ │ Heap │ Stack │ │ │ │ Heap │ Stack │ │
│ └────────────────┘ │ │ └────────────────┘ │
│ │ │ │
│ A가 B의 메모리 접근 불가 X │ │ B가 A의 메모리 접근 불가 X │
└─────────────────────────────┘ └─────────────────────────────┘
│
통신하려면 IPC 필요
| IPC 방식 | 설명 |
|---|---|
| Pipe | 단방향 데이터 스트림 |
| Shared Memory | 공유 메모리 영역 설정 |
| Message Queue | 메시지 단위 송수신 |
| Socket | 네트워크 기반 통신 |
| Signal | 이벤트 알림 (SIGKILL 등) |
프로세스 vs 스레드
| 항목 | 프로세스 | 스레드 |
|---|---|---|
| 메모리 | 독립 공간 | 같은 프로세스 내 공유 |
| 생성 비용 | 큼 (메모리 복제) | 작음 (Stack만 별도) |
| 통신 | IPC 필요 (느림) | 메모리 직접 공유 (빠름) |
| 안정성 | 하나 죽어도 다른 프로세스 무관 | 하나 죽으면 전체 프로세스 영향 |
| Context Switch | 비용 큼 | 비용 작음 |
┌──── 프로세스 ─────────────────────────┐
│ │
│ Code │ Data │ Heap ← 모든 스레드 공유│
│ │
│ ┌─ 스레드 A ─┐ ┌─ 스레드 B ─┐ │
│ │ Stack │ │ Stack │ │
│ │ PC │ │ PC │ │
│ └────────────┘ └────────────┘ │
└────────────────────────────────────────┘
실제 확인
# 현재 실행 중인 프로세스 확인
$ ps aux
PID USER COMMAND
1 root /sbin/init
1234 user java -jar app.jar
5678 user /usr/bin/chrome
9012 user python server.py
# 특정 프로세스의 스레드 확인
$ ps -T -p 1234
PID SPID COMMAND
1234 1234 java ← main 스레드
1234 1235 java ← GC 스레드
1234 1236 java ← JIT 컴파일 스레드
1234 1237 java ← worker 스레드
핵심 정리
프로세스는 실행 중인 프로그램의 인스턴스로, OS로부터 독립된 메모리 공간, CPU 시간, PID 등의 자원을 할당받습니다. 각 프로세스는 메모리가 격리되어 안정적이지만, 그만큼 생성 비용이 크고 통신에 IPC가 필요합니다. 이런 비용을 줄이기 위해 하나의 프로세스 안에서 여러 실행 흐름을 만드는 스레드 개념이 등장했습니다.