
| 시스템 유형 | program | 설명 |
|---|---|---|
| 일괄 처리 시스템 | 작업(jobs) | 여러 작업을 한꺼번에 모아서 순차적으로 처리하는 방식 |
| 시분할 시스템 | 사용자 프로그램 또는 태스크(user programs or tasks) | 여러 사용자가 동시에 시스템을 공유하는 방식 |
프로그램은 디스크에 가만히 저장되어 있는 파일일 뿐이다. 아직 실행되지 않은 상태이다.
UNIX 프로세스란 정확히 무엇인가?
프로세스는 가상 주소 공간과 제어 정보로 구성된다
핵심 포인트: 프로그램 = 디스크에 저장된 정적 파일(수동적), 프로세스 = 메모리에 올라가 실행 중인 동적 상태(능동적). 같은 프로그램에서 여러 프로세스가 생성될 수 있다.

좌측: 메모리(Memory)
우측: 프로세스의 메모리 레이아웃(주소 공간)
max ┌─────────────┐
│ stack │ ← 스택: 위에서 아래로 성장 (↓)
│ ↓ │
│ │ ← 빈 공간 (스택과 힙 사이)
│ ↑ │
│ heap │ ← 힙: 아래에서 위로 성장 (↑)
├─────────────┤
│ data │ ← 데이터 섹션
├─────────────┤
│ text │ ← 텍스트(코드) 섹션
0 └─────────────┘Process에 포함되는 것
| 구성 | 상세 설명 |
|---|---|
| program counter | 다음에 실행할 명령어의 주소를 가리키는 레지스터 |
| stack | 함수 호출 시 매개변수, 지역 변수, 리턴 주소가 저장되는 영역. 함수 호출이 중첩되면 스택이 아래로 자란다 |
| data section | 전역 변수와 정적 변수가 저장되는 영역 |
| Control information | PCB에 저장되는 프로세스 관리 정보 |
핵심 포인트: 프로세스의 메모리는 text(코드) → data(전역변수) → heap(동적할당, ↑성장) → stack(함수호출, ↓성장)의 구조를 가진다. 프로그램이 디스크에서 메모리로 로드되면 프로세스가 된다.

디스크에 저장된 실행 파일(executable-file disk image)의 구조:
a.out magic number ← 실행 파일 식별자
a.out header ← 각 섹션의 크기 정보
text ← 코드 섹션
initialized data ← 초기화된 데이터
symbol table ← 심볼 테이블 (디버깅용)
이 파일이 메모리에 로드되면:
그림 중앙의 Page table 화살표는 가상 주소를 물리 메모리(Physical memory)로 변환하는 과정을 보여준다.
핵심 포인트: C 코드의 각 요소(전역 변수, 지역 변수, 동적 할당)가 메모리의 어느 영역에 저장되는지 정확히 이해하는 것이 중요하다. bss는 초기화되지 않은 전역 변수 영역으로 디스크에 저장하지 않아 실행 파일 크기를 줄인다.

| 상태 | 원문 | 한국어 | 상세 설명 |
|---|---|---|---|
| new | The process is being created | 프로세스가 생성 중 | fork() 등으로 프로세스가 막 만들어지고 있는 단계 |
| running | Instructions are being executed | 명령어가 실행 중 | CPU를 할당받아 실제로 코드가 실행되고 있는 상태 |
| waiting | The process is waiting for some event to occur | 어떤 이벤트가 발생하기를 기다리는 중 | I/O 완료, 시그널 수신 등을 기다리는 상태. CPU를 사용하지 않는다 |
| ready | The process is waiting to be assigned to a processor | 프로세서에 할당되기를 기다리는 중 | 실행 준비가 되었지만 CPU가 다른 프로세스를 실행 중이라 대기 |
| terminated | The process has finished execution | 실행이 끝남 | exit() 호출 등으로 프로세스가 종료된 상태 |

| 전이 | 원인 | 한국어 설명 |
|---|---|---|
| new → ready | admitted (승인됨) | OS가 프로세스를 승인하여 준비 큐에 넣는다 |
| ready → running | scheduler dispatch (스케줄러 디스패치) | CPU 스케줄러가 이 프로세스를 선택하여 CPU를 할당한다 |
| running → ready | interrupt (인터럽트) | 타임 슬라이스 만료 등 인터럽트로 CPU를 빼앗긴다 |
| running → waiting | I/O or event wait (I/O 또는 이벤트 대기) | I/O 요청, sleep() 등으로 이벤트를 기다린다 |
| waiting → ready | I/O or event completion (I/O 또는 이벤트 완료) | 기다리던 이벤트가 완료되어 다시 실행 준비 상태가 된다 |
| running → terminated | exit (종료) | 프로세스가 실행을 마치고 종료한다 |
주의: waiting → running으로 직접 전이는 없다. 반드시 waiting → ready → running 순서이다. 즉, I/O가 끝나도 바로 실행되지 않고 준비 큐에서 자기 차례를 기다려야 한다.
핵심 포인트: 프로세스는 new → ready → running → terminated의 기본 흐름을 따르며, running 중 I/O가 필요하면 waiting으로, 타임 슬라이스가 만료되면 ready로 돌아간다.

| 상태 | 한국어 | 설명 |
|---|---|---|
| initial (idle) | 초기(유휴) | fork()로 프로세스가 막 생성된 상태 |
| user running | 사용자 모드 실행 | 사용자 모드에서 코드가 실행 중 |
| kernel running | 커널 모드 실행 | 시스템 콜이나 인터럽트로 커널 코드가 실행 중 |
| ready to run | 실행 준비 | CPU 할당을 기다리는 상태 |
| asleep | 수면 | I/O 등 이벤트를 기다리며 잠든 상태 (= waiting) |
| zombie | 좀비 | exit()했지만 부모가 wait()하지 않아 정보만 남은 상태 |
| stopped | 정지 | SIGSTOP 시그널로 일시 정지된 상태 |
| stopped + asleep | 정지 + 수면 | 정지 상태에서 동시에 수면 중인 상태 |
핵심 포인트: 실제 Unix에서는 user running과 kernel running을 구분하며, 좀비(zombie)와 정지(stopped) 상태가 추가된다. 좀비는 프로세스가 종료되었지만 부모가 아직 회수하지 않은 상태이다.

| 항목 | 원문 | 한국어 | 상세 설명 |
|---|---|---|---|
| Process state | ready, running, waiting, halted, and so on | 프로세스 상태 | 현재 프로세스가 어떤 상태인지 |
| Program counter | address of the next instruction to be executed | 프로그램 카운터 | 다음에 실행할 명령어의 주소 |
| CPU registers | stack points, general purpose registers | CPU 레지스터 | 스택 포인터, 범용 레지스터 등의 값 |
| CPU scheduling information | process priority, scheduling queue, nice value | CPU 스케줄링 정보 | 프로세스 우선순위, 스케줄링 큐 위치, nice 값 |
| Memory-management information | values of base and limit, page table, and so on | 메모리 관리 정보 | base/limit 레지스터 값, 페이지 테이블 등 |
| Accounting information | The amount of CPU and real time used, time limits, etc | 계정 정보 | 사용한 CPU 시간, 실제 경과 시간, 시간 제한 등 |
| I/O status information | The list of devices allocated to the process, a list of open files (file descriptor table) | I/O 상태 정보 | 할당된 장치 목록, 열린 파일 목록(파일 디스크립터 테이블) |

┌──────────────────┐
│ process state │ ← ready, running, waiting 등
├──────────────────┤
│ process number │ ← PID (프로세스 식별자)
├──────────────────┤
│ program counter │ ← 다음 실행할 명령어 주소
├──────────────────┤
│ │
│ registers │ ← CPU 레지스터 값들
│ │
├──────────────────┤
│ memory limits │ ← 메모리 범위 정보
├──────────────────┤
│list of open files│ ← 열린 파일 목록
├──────────────────┤
│ ... │ ← 기타 정보
└──────────────────┘
핵심 포인트: PCB는 프로세스의 "신분증"이다. OS는 PCB를 통해 프로세스의 모든 상태 정보를 관리하며, 문맥 전환 시 PCB를 저장/복원하여 프로세스가 중단된 지점에서 다시 실행될 수 있게 한다.

각 항목 설명:
| 항목 | 한국어 | 설명 |
|---|---|---|
| process group | 프로세스 그룹 | 시그널을 함께 받는 프로세스 그룹 → 세션(session)과 연결 |
| process credential | 프로세스 자격 증명 | 프로세스의 권한 정보 → 사용자 자격 증명(user credential)과 연결 |
| VM space | 가상 메모리 공간 | 프로세스의 가상 주소 공간 → 영역 리스트(region list)와 연결 |
| file descriptors | 파일 디스크립터 | 열린 파일 정보 → 파일 엔트리(file entries)와 연결 |
| resource limits | 자원 제한 | CPU 시간, 메모리 등의 사용 제한 |
| statistics | 통계 | CPU 사용량 등 통계 정보 |
| signal actions | 시그널 동작 | 각 시그널에 대한 핸들러 지정 |
| process control block | 프로세스 제어 블록 | 레지스터, PC 등 하드웨어 문맥 |
| process kernel stack | 프로세스 커널 스택 | 시스템 콜 실행 시 사용하는 커널 스택 |
핵심 포인트: 실제 Unix에서 프로세스는 단순한 PCB가 아니라, 여러 하위 구조체들의 포인터 네트워크로 이루어져 있다. 프로세스 테이블의 크기가 최대 프로세스 수를 결정한다.


상단 그림 (다중프로그래밍):
하단 그림 (시분할/타임 슬라이스):
핵심 포인트: 다중프로그래밍은 I/O 대기 시 CPU를 다른 프로세스에 넘기는 것이고, 시분할은 정해진 시간 단위(타임 슬라이스)로 CPU를 번갈아 사용하는 것이다.

| 단계 | 동작 | 한국어 설명 |
|---|---|---|
| 1 | P₀ executing | P₀가 CPU에서 실행 중 |
| 2 | Interrupt/system call 발생 | 인터럽트나 시스템 콜이 발생 |
| 3 | save state into PCB₀ | P₀의 현재 상태(레지스터, PC 등)를 PCB₀에 저장 |
| 4 | 스케줄러가 다음 프로세스 P₁ 선택 | OS의 스케줄러가 준비 큐에서 P₁을 선택 |
| 5 | reload state from PCB₁ | P₁의 저장된 상태를 PCB₁에서 로드 |
| 6 | P₁ executing | P₁이 CPU에서 실행 시작 (이전에 중단된 지점부터) |
핵심 포인트: Context Switch의 핵심은 "현재 프로세스의 PCB 저장 → 새 프로세스의 PCB 로드"이다. 이 과정에서 CPU는 유용한 작업을 하지 못하므로 오버헤드가 된다.

| 큐 | 원문 | 설명 |
|---|---|---|
| Job queue | Set of all processes in the system | 시스템 내 모든 프로세스의 집합 |
| Ready queue | Set of all processes residing in main memory, ready to execute | 메인 메모리에 있으며 실행 준비된 프로세스의 집합 |
| Device queues | Set of processes waiting for an I/O device | I/O 장치를 기다리는 프로세스의 집합 |
| Waiting queue per resource | Set of processes waiting for a resource (e.g., message queue, socket, semaphore, etc) | 특정 자원(메시지 큐, 소켓, 세마포어 등)을 기다리는 프로세스 |



CPU를 사용한 후 발생할 수 있는 4가지 상황:
1. I/O request: I/O 요청 → I/O 큐에서 대기 → I/O 완료 후 ready queue로 복귀
2. time slice expired: 타임 슬라이스 만료 → 바로 ready queue로 복귀
3. fork a child: 자식 프로세스 생성 → 자식 실행 완료 후 ready queue로 복귀
4. wait for an interrupt: 인터럽트 대기 → 인터럽트 발생 후 ready queue로 복귀
핵심 포인트: 프로세스는 생명주기 동안 준비 큐, 장치 큐, 대기 큐 사이를 끊임없이 이동한다. 모든 경로는 결국 ready queue로 돌아온다.

| 스케줄러 | 실행 빈도 | 역할 |
|---|---|---|
| 장기 스케줄러 | 매우 드물게 (수 분에 한 번) | 디스크 → 메모리로 프로세스 로드 결정 |
| 단기 스케줄러 | 매우 자주 (수 밀리초마다) | CPU에 실행할 프로세스 선택 |

| 단계 | 한국어 |
|---|---|
| 1 | 현재 프로세스의 PCB를 저장 |
| 2 | 실행 예정인 새 프로세스의 저장된 PCB를 로드 |
| 3 | 캐시와 TLB가 리셋됨 |

핵심 포인트: 문맥 전환 = PCB 저장 + PCB 로드 + 캐시/TLB 리셋. 전환 시간 자체는 순수한 오버헤드이므로, 너무 자주 전환하면 성능이 떨어진다.

| 원문 | 설명 |
|---|---|
| Parent and children share all resources | 부모와 자식이 모든 자원을 공유 |
| Children share subset of parent's resources | 자식이 부모 자원의 일부를 공유 |
| Parent and child share no resources | 부모와 자식이 자원을 공유하지 않음 |
| 원문 | 설명 |
|---|---|
| Parent and children execute concurrently | 부모와 자식이 동시에 실행 |
| Parent waits until children terminate | 부모가 자식이 종료할 때까지 대기 |

각 프로세스 설명:

UNIX에서의 프로세스 생성:
1. fork() 시스템 콜로 새 프로세스 생성 → 부모의 완전한 복사본
2. exec() 시스템 콜로 프로세스의 메모리 공간을 새 프로그램으로 교체
부모 프로세스 (bash)
│
fork() ──────→ 자식 프로세스 (bash의 복사본)
│ │
│ exec("ls")
│ │
│ 자식 프로세스 (ls 프로그램으로 변환됨)
│ │
wait() exit()
│◄───────────────────┘
(계속)

int main()
{
pid_t pid;
pid = fork(); // 자식 프로세스 생성
if (pid == 0) { // ← 자식 프로세스 (fork()가 0을 반환)
fd = open("/tmp/a.txt", O_READ);
if (fd < 0) exit(-1); // 파일 열기 실패 시 종료
if (read(fd, pBuf, 100) < 0)
exit(-2); // 읽기 실패 시 종료
printf("Hello, world\n");
exit(0); // 정상 종료
}
else { // ← 부모 프로세스 (fork()가 자식의 PID를 반환)
pid = wait(&state); // 자식이 종료할 때까지 대기
printf("Parent: child %d, state:%d\n", pid, state);
exit(0);
}
}
흐름:
1. fork()로 자식 프로세스 생성
2. 자식(pid==0): 파일을 열고, 읽고, "Hello, world" 출력 후 exit(0)
3. 부모(pid>0): wait(&state)로 자식의 종료를 기다림
4. 자식이 종료하면 부모가 종료 상태를 받아 출력


Zombie processes (Unix/Linux)
부모가 자식보다 먼저 죽으면, init 프로세스가 자식을 상속한다.
핵심 포인트: 좀비 프로세스는 종료되었지만 부모가 wait()로 회수하지 않은 프로세스이다. 좀비가 많이 쌓이면 프로세스 테이블이 가득 차서 새 프로세스를 만들 수 없게 된다. 고아 프로세스는 init이 입양하여 정리한다.

| 유형 | 설명 |
|---|---|
| Independent process | 다른 프로세스의 실행에 영향을 주거나 받을 수 없다 |
| Cooperating process | 다른 프로세스의 실행에 영향을 주거나 받을 수 있다 |
| 이유 | 설명 |
|---|---|
| 정보 공유 | 여러 프로세스가 같은 데이터에 접근 |
| 계산 속도 향상 | 작업을 하위 작업으로 분할하여 병렬 실행 |
| 모듈성 | 시스템 기능을 별도의 프로세스로 모듈화 |
| 편의성 | 사용자가 여러 작업을 동시에 수행 |

웹 서버 예시:
[단일 프로세스] [협력 프로세스]
Process → Web server Process → Encode ──→ I/O
(순차 처리) Process → Encode ──→ I/O
Process → Web server
(병렬 처리 → 속도 향상)

| 모델 | 설명 | 방식 |
|---|---|---|
| Shared memory | 협력 프로세스가 공유하는 메모리 영역 | 프로세스들이 같은 메모리 영역을 직접 읽고 쓴다 |
| Message system | 공유 변수에 의존하지 않고 프로세스 간 통신 | 메시지를 send/receive하여 데이터를 교환한다 |



send(P, message) ← 프로세스 P에게 메시지를 보낸다
receive(Q, message) ← 프로세스 Q로부터 메시지를 받는다
| 특성 | 설명 |
|---|---|
| 링크가 자동으로 설정됨 | 두 프로세스가 서로를 알면 바로 통신 가능 |
| 한 쌍에 정확히 하나의 링크 | 두 프로세스 사이에 하나의 채널만 존재 |
| 보통 양방향 | 양쪽 모두 보내고 받을 수 있다 |
단점 (hard coding): 프로세스 식별자를 직접 지정하므로, 종료된 프로세스의 ID가 재사용되면 엉뚱한 프로세스와 통신할 수 있다.


send(A, message) ← 메일박스 A에 메시지를 보낸다
receive(A, message) ← 메일박스 A에서 메시지를 받는다
| 특성 | 설명 |
|---|---|
| 메일박스에 고유 ID가 있음 | 각 메일박스는 유일한 식별자를 가진다 |
| 공통 메일박스를 공유해야 통신 가능 | 같은 메일박스를 아는 프로세스만 통신 |
| 여러 프로세스가 하나의 링크에 연관 가능 | 메일박스 하나에 여러 프로세스가 연결 |


Process 1 (송신자):
#define KEY 100
struct mymesg { long mtype; char mtext[512]; };
int main(void) {
struct mymesg msg;
msgqId = msgget(KEY, S_IRUSR|S_IWUSR); // KEY=100으로 메시지 큐 생성/열기
msg.mtype = 3; // 메시지 타입을 3으로 지정
strcpy(msg.mtext, "Writing to message queue");
msgsnd(msgqId, &msg, sizeof(msg), 0); // 메시지 전송
return 0;
}
Process 2 (수신자):
#define KEY 100
int main(void) {
char* pMem = NULL;
msgqId = msgget(KEY, S_IRUSR|S_IWUSR); // 같은 KEY=100으로 메시지 큐 열기
msgrcv(msgqId, pBuf, 512, 3, 0); // 타입 3인 메시지를 수신
printf("%s", pMem);
return 0;
}
핵심 함수 설명:
| 함수 | 한국어 | 설명 |
|---|---|---|
msgget(KEY, ...) | 메시지 큐 생성/열기 | KEY 값으로 메시지 큐를 식별. 없으면 생성, 있으면 열기 |
msgsnd(id, msg, size, 0) | 메시지 전송 | 지정된 큐에 메시지를 보낸다 |
msgrcv(id, buf, size, 3, 0) | 메시지 수신 | 지정된 큐에서 타입 3인 메시지를 받는다 |

msgsnd()로 메시지를 보내면 사용자 공간의 데이터가 커널의 큐로 복사(copy)된다.msgrcv()로 메시지를 받으면 커널의 큐에서 사용자 공간으로 복사된다.
(a) 메시지 전달 (Message Passing)
┌────────────┐
│ process A │ M ──┐
│ │ │ 1: send
├────────────┤ │
│ process B │ M │
│ │ │ 2: receive
├────────────┤ │
│ │ │
│ kernel │ M ◄─┘ ← 커널을 통해 전달
└────────────┘
(b) 공유 메모리 (Shared Memory)
┌────────────┐
│ process A │ 1: write
│ ┌──────┤ ◄──┐
│-----│shared│ │
│ └──────┤ │ ← 공유 영역에 직접 읽고 쓴다
│ process B │ ◄──┘ 2: read
├────────────┤
│ kernel │ ← 커널 개입 최소 (설정 시에만)
└────────────┘
| 비교 항목 | 메시지 전달 | 공유 메모리 |
|---|---|---|
| 커널 개입 | 매번 (send/receive마다) | 설정 시에만 (이후 직접 접근) |
| 속도 | 상대적으로 느림 (복사 오버헤드) | 상대적으로 빠름 (직접 접근) |
| 분산 환경 | 적합 (네트워크를 통해 가능) | 부적합 (같은 머신에서만) |
| 동기화 | 내장 (send/receive 자체가 동기화) | 별도 필요 (세마포어 등) |
| 구현 난이도 | 쉬움 | 어려움 (동기화 필요) |

| 유형 | 방식 | 설명 |
|---|---|---|
| Blocking send | 메시지가 수신될 때까지, 또는 큐가 가득 차면 송신자가 차단 | 보낸 메시지가 확실히 전달될 때까지 기다린다 |
| Blocking receive | 메시지가 도착할 때까지 수신자가 차단 | 메시지가 올 때까지 기다린다 |
| Non-blocking send | 송신자가 메시지를 보내고 계속 진행 | 전달 여부와 관계없이 바로 다음 작업 수행 |
| Non-blocking receive | 수신자가 유효한 메시지 또는 null을 받음 | 메시지가 없으면 null을 받고 계속 진행 |

| 방식 | 원문 | 한국어 | 동작 |
|---|---|---|---|
| Zero capacity | 0 messages. Sender must wait for receiver (rendezvous) | 용량 0. 송신자가 수신자를 기다려야 한다 (랑데뷰) | 큐가 없으므로 송신자와 수신자가 동시에 만나야 전달 가능 |
| Bounded capacity | Finite length of n messages. Sender must wait if link full | 유한 용량 n개. 링크가 가득 차면 송신자가 대기 | 최대 n개까지 메시지 저장 가능. 가득 차면 송신자가 차단됨 |
| Unbounded capacity | Infinite length. Sender never waits | 무한 용량. 송신자가 절대 대기하지 않음 | 이론적으로 무한한 메시지를 저장 (실제로는 메모리 한계 존재) |
핵심 포인트: 랑데뷰(zero capacity)는 가장 강한 동기화를 제공하지만 성능이 낮고, 무한 버퍼(unbounded)는 가장 유연하지만 메모리 문제가 발생할 수 있다. 실제 시스템은 대부분 bounded capacity를 사용한다.
| Key Terms | 설명 |
|---|---|
| Program | 디스크에 저장된 정적 코드 (수동적 개체) |
| Process | 실행 중인 프로그램 (능동적 개체) |
| PCB (Process Control Block) | 프로세스의 모든 상태 정보를 담는 커널 자료구조 |
| PID (Process ID) | 각 프로세스를 구분하는 고유 번호 |
| Text / Code | 프로그램의 기계어 코드가 저장되는 메모리 영역 |
| Data section | 전역 변수/정적 변수가 저장되는 영역 |
| BSS | 초기화되지 않은 전역 변수 영역 (Block Started by Symbol) |
| Heap | 동적 메모리 할당(malloc/new) 영역 (↑ 성장) |
| Stack | 함수 호출 시 지역변수/리턴주소 저장 (↓ 성장) |
| Context Switch | 현재 프로세스의 PCB 저장 + 새 프로세스의 PCB 로드 |
| Ready Queue | CPU 할당을 기다리는 프로세스들의 큐 |
| Device Queue | I/O 장치를 기다리는 프로세스들의 큐 |
| Long-term Scheduler | 디스크 → 메모리로 프로세스 로드 결정 |
| Short-term Scheduler | CPU에 실행할 프로세스 선택 |
| fork() | 자식 프로세스를 생성하는 UNIX 시스템 콜 |
| exec() | 프로세스 메모리를 새 프로그램으로 교체하는 시스템 콜 |
| exit() | 프로세스를 종료하는 시스템 콜 |
| wait() | 자식 프로세스 종료를 기다리는 시스템 콜 |
| Zombie Process | 종료되었지만 부모가 wait()하지 않아 잔류하는 프로세스 |
| Orphan Process | 부모가 먼저 종료된 프로세스 (init이 입양) |
| Cascading Termination | 부모 종료 시 모든 자식도 종료 |
| IPC | 프로세스 사이의 데이터 교환 메커니즘 |
| Shared Memory | 프로세스들이 공유하는 메모리 영역을 통한 IPC |
| Message Passing | send/receive로 메시지를 교환하는 IPC |
| Direct Communication | 프로세스를 명시적으로 지명하여 통신 |
| Indirect Communication | 메일박스(포트)를 통한 통신 |
| Mailbox / Port | 간접 통신에서 메시지가 저장/인출되는 추상 객체 |
| Blocking (Synchronous) | 상대방의 응답을 기다림 |
| Non-blocking (Asynchronous) | 기다리지 않고 바로 진행 |
| Rendezvous | 버퍼 없이 송신자와 수신자가 동시에 만나는 방식 |
| Bounded Capacity | 큐에 최대 n개까지 메시지 저장 가능 |
| TLB (Translation Lookaside Buffer) | 가상→물리 주소 변환을 캐싱하는 하드웨어 |