-
Notifications
You must be signed in to change notification settings - Fork 0
1주차
이번 주차에서는 운영체제가 실행 중인 프로그램을 어떤 단위로 관리하는지 학습한다.
프로세스와 스레드의 차이를 이해하고, 운영체제가 이들의 실행 정보를 PCB와 TCB에 어떻게 저장하는지 살펴본다. 또한 CPU 실행 대상이 변경될 때 발생하는 Context Switching의 과정과 비용을 이해하고, 멀티프로세스와 멀티스레드 구조의 특징을 비교한다.
주요 학습 키워드는 다음과 같다.
- 프로세스
- 스레드
- PCB
- TCB
- Context Switching
- 멀티프로세스
- 멀티스레드
프로세스는 실행 중인 프로그램이다.
프로그램은 디스크에 저장된 명령어와 데이터의 집합이지만, 프로세스는 프로그램이 메모리에 적재되어 CPU에 의해 실행되고 있는 동적인 상태를 의미한다.
디스크에 저장된 프로그램
|
| 실행
v
메모리에 적재된 프로세스
하나의 프로그램을 여러 번 실행하면 동일한 프로그램을 기반으로 여러 개의 프로세스가 생성될 수 있다.
각 프로세스는 일반적으로 독립된 가상 주소 공간과 실행 상태를 가진다. 따라서 하나의 프로세스가 사용하는 메모리에 다른 프로세스가 임의로 접근할 수 없다.
운영체제는 프로세스를 실행하고 관리하기 위해 다음과 같은 정보를 유지한다.
- 프로세스 식별자
- 현재 프로세스 상태
- CPU 레지스터 값
- 다음에 실행할 명령어의 주소
- 메모리 관리 정보
- 스케줄링 정보
- 열린 파일 정보
- 입출력 상태
- 부모 프로세스와 자식 프로세스 정보
- 사용자와 권한 정보
이러한 정보는 주로 PCB에 저장된다.
프로세스의 가상 주소 공간은 일반적으로 Text, Data, BSS, Heap, Stack 영역으로 구성된다.
높은 주소
+----------------------------+
| Stack |
| 함수 호출 정보 |
| 매개변수, 지역 변수 |
+----------------------------+
| ↓ |
| |
| ↑ |
+----------------------------+
| Heap |
| 동적으로 할당된 메모리 |
+----------------------------+
| BSS |
| 초기화되지 않은 전역 변수 |
+----------------------------+
| Data |
| 초기화된 전역 변수 |
+----------------------------+
| Text |
| 실행할 프로그램 코드 |
+----------------------------+
낮은 주소
실제 메모리 배치와 영역의 위치는 운영체제, 실행 파일 형식, 컴파일러에 따라 달라질 수 있다.
프로세스가 실행할 명령어가 저장되는 영역이다.
실행 중인 코드가 임의로 변경되는 것을 방지하기 위해 일반적으로 읽기 전용으로 설정된다.
초기값이 존재하는 전역 변수와 정적 변수가 저장되는 영역이다.
프로그램이 시작될 때 할당되고 프로세스가 종료될 때 해제된다.
초기화되지 않았거나 0으로 초기화되는 전역 변수와 정적 변수가 저장되는 영역이다.
실행 파일에 실제 초기값을 모두 저장하지 않아도 되므로 실행 파일의 크기를 줄일 수 있다.
프로그램 실행 중 동적으로 할당되는 메모리 영역이다.
일반적으로 낮은 주소에서 높은 주소 방향으로 확장된다.
Heap 영역은 여러 스레드가 공유할 수 있으므로, 여러 스레드가 같은 데이터에 동시에 접근하면 동기화 문제가 발생할 수 있다.
함수 호출 정보, 매개변수, 지역 변수, 반환 주소 등이 저장되는 영역이다.
함수가 호출되면 Stack Frame이 생성되고, 함수 실행이 끝나면 해당 Stack Frame이 제거된다.
프로세스 안에 여러 스레드가 존재하면 각 스레드는 독립적인 Stack을 가진다.
PCB는 Process Control Block의 약자로, 운영체제가 프로세스를 관리하기 위해 사용하는 자료구조다.
프로세스는 실행 중 CPU를 빼앗길 수 있다. 이후 해당 프로세스가 다시 CPU를 할당받았을 때 중단된 위치부터 실행하려면 이전 실행 상태가 저장되어 있어야 한다.
운영체제는 이러한 정보를 PCB에 저장한다.
| 정보 | 설명 |
|---|---|
| Process ID | 프로세스를 식별하는 고유 번호 |
| Process State | New, Ready, Running, Waiting, Terminated 등의 상태 |
| Program Counter | 다음에 실행할 명령어의 주소 |
| CPU Registers | 범용 레지스터, Stack Pointer 등의 값 |
| Scheduling Information | 우선순위, 스케줄링 큐 위치, CPU 사용 시간 |
| Memory Information | 페이지 테이블, 세그먼트 정보, 주소 공간 정보 |
| I/O Information | 열린 파일, 할당된 장치, 입출력 상태 |
| Accounting Information | CPU 사용량, 실행 시간, 사용자 정보 |
| Process Relationship | 부모 프로세스와 자식 프로세스 정보 |
PCB가 없다면 프로세스가 CPU를 빼앗긴 후 어디까지 실행했는지 알 수 없다.
운영체제는 프로세스를 중단할 때 CPU 레지스터와 Program Counter 등의 값을 PCB에 저장하고, 다시 실행할 때 PCB의 값을 CPU에 복원한다.
프로세스 실행
|
| 중단
v
CPU 상태를 PCB에 저장
|
| 다시 CPU 할당
v
PCB의 상태를 CPU에 복원
|
v
중단 지점부터 실행
PCB는 운영체제 커널이 관리하는 메모리 영역에 저장된다.
일반 사용자 프로세스가 PCB를 임의로 수정할 수 있다면 스케줄링 정보나 메모리 접근 권한이 손상될 수 있으므로 커널이 보호한다.
스레드는 프로세스 내부에서 실행되는 하나의 실행 흐름이다.
하나의 프로세스에는 최소 하나의 스레드가 존재한다. 프로세스 내부에 여러 스레드를 생성하면 하나의 프로세스가 여러 실행 흐름을 가질 수 있다.
프로세스
+------------------------------------+
| Code |
| Data |
| Heap |
| 열린 파일 |
| |
| Thread 1 Thread 2 Thread 3 |
| Stack 1 Stack 2 Stack 3 |
| Register 1 Register 2 Register 3 |
+------------------------------------+
같은 프로세스에 속한 스레드는 일반적으로 다음 자원을 공유한다.
- Code 영역
- Data 영역
- BSS 영역
- Heap 영역
- 프로세스의 가상 주소 공간
- 열린 파일
- 프로세스 단위의 운영체제 자원
각 스레드는 독립적인 실행 흐름을 유지하기 위해 다음 정보를 개별적으로 가진다.
- Thread ID
- Program Counter
- CPU Register
- Stack
- 스레드 상태
- 스레드 스케줄링 정보
- Thread Local Storage
각 스레드는 서로 다른 함수를 실행할 수 있다.
함수 호출 순서, 매개변수, 지역 변수, 반환 주소는 스레드마다 다르므로 각 스레드는 독립적인 Stack을 가져야 한다.
하나의 Stack을 공유하면 각 스레드의 함수 호출 기록과 지역 변수를 구분할 수 없다.
TCB는 Thread Control Block의 약자로, 운영체제가 스레드를 관리하기 위해 사용하는 자료구조다.
TCB에는 스레드의 실행 상태와 Context Switching에 필요한 정보가 저장된다.
| 정보 | 설명 |
|---|---|
| Thread ID | 스레드를 식별하는 고유 번호 |
| Thread State | Ready, Running, Waiting 등의 상태 |
| Program Counter | 다음에 실행할 명령어의 주소 |
| CPU Registers | 스레드가 사용하던 레지스터 값 |
| Stack Pointer | 현재 Stack의 위치 |
| Scheduling Information | 우선순위, CPU 사용 시간, 스케줄링 큐 정보 |
| Thread Local Storage | 스레드마다 독립적으로 유지되는 데이터 |
| Process Reference | 스레드가 속한 프로세스의 정보 |
멀티프로세스는 하나의 시스템에서 여러 프로세스를 동시에 또는 번갈아 실행하는 구조다.
각 프로세스는 독립된 주소 공간과 자원을 가진다.
Process A Process B
+---------------+ +---------------+
| Code | | Code |
| Data | | Data |
| Heap | | Heap |
| Stack | | Stack |
+---------------+ +---------------+
| |
+------ IPC 필요 --------+
IPC는 Inter-Process Communication의 약자로, 서로 다른 프로세스가 데이터를 주고받는 방법이다.
프로세스는 기본적으로 주소 공간이 분리되어 있기 때문에 명시적인 통신 수단이 필요하다.
한 프로세스의 출력 데이터를 다른 프로세스의 입력으로 전달하는 방식이다.
일반 파이프는 주로 부모와 자식처럼 관계가 있는 프로세스 사이에서 사용된다.
파일 시스템에 이름을 가진 파이프를 생성하는 방식이다.
서로 직접적인 부모·자식 관계가 없는 프로세스도 사용할 수 있다.
운영체제가 관리하는 큐에 메시지를 저장하고 다른 프로세스가 해당 메시지를 읽는 방식이다.
송신 프로세스와 수신 프로세스가 동시에 실행되지 않아도 통신할 수 있다.
여러 프로세스가 같은 물리 메모리 영역을 자신의 주소 공간에 매핑하여 사용하는 방식이다.
데이터 복사 비용이 적어 빠르지만, 여러 프로세스가 같은 데이터를 수정할 수 있으므로 별도의 동기화가 필요하다.
네트워크 통신 인터페이스를 이용해 프로세스 간 데이터를 주고받는 방식이다.
같은 컴퓨터의 프로세스뿐 아니라 서로 다른 컴퓨터의 프로세스 사이에서도 통신할 수 있다.
운영체제가 프로세스에 특정 이벤트가 발생했음을 알리는 방식이다.
전달할 수 있는 정보의 양은 제한적이며, 주로 프로세스 제어나 이벤트 통지에 사용된다.
멀티스레드는 하나의 프로세스 내부에 여러 스레드를 두어 여러 실행 흐름을 처리하는 구조다.
같은 프로세스의 스레드는 Code, Data, Heap, 열린 파일 등을 공유한다.
하나의 프로세스
+-----------------------------------+
| 공유 영역 |
| Code, Data, Heap, 열린 파일 |
| |
| Thread 1 | Thread 2 | Thread 3 |
| Stack 1 | Stack 2 | Stack 3 |
+-----------------------------------+
경쟁 상태는 여러 프로세스 또는 스레드가 공유 데이터에 동시에 접근할 때 실행 순서에 따라 결과가 달라지는 상황이다.
다음 연산을 두 스레드가 동시에 실행한다고 가정한다.
count = count + 1
이 연산은 내부적으로 다음과 같이 분리될 수 있다.
1. 메모리에서 count 값을 읽는다.
2. 읽은 값에 1을 더한다.
3. 결과를 메모리에 저장한다.
초기값이 0일 때 두 스레드가 동시에 0을 읽으면 각각 1을 계산한 뒤 1을 저장할 수 있다.
Thread A: count 읽기 → 0
Thread B: count 읽기 → 0
Thread A: 0 + 1 → 1 저장
Thread B: 0 + 1 → 1 저장
두 번 증가했지만 최종 결과는 1이 된다.
이를 Lost Update라고 한다.
임계 구역은 여러 실행 흐름이 동시에 접근하면 문제가 발생할 수 있는 코드 영역이다.
임계 구역에는 한 번에 하나의 실행 흐름만 진입하도록 제어해야 한다.
상호 배제는 하나의 실행 흐름이 임계 구역을 실행하는 동안 다른 실행 흐름이 진입하지 못하게 하는 성질이다.
대표적인 동기화 기법은 다음과 같다.
- Mutex
- Semaphore
- Monitor
- Spin Lock
- Read-Write Lock
- 원자적 연산
| 키워드 | 핵심 내용 |
|---|---|
| 프로세스 | 실행 중인 프로그램이며 독립된 주소 공간과 자원을 가진다. |
| 스레드 | 프로세스 내부의 실행 흐름이며 같은 프로세스의 자원을 공유한다. |
| PCB | 프로세스의 상태와 실행 정보를 저장하는 운영체제 자료구조다. |
| TCB | 스레드의 상태와 실행 정보를 저장하는 운영체제 자료구조다. |
| Context Switching | 현재 실행 상태를 저장하고 다음 실행 상태를 복원하는 과정이다. |
| 멀티프로세스 | 여러 독립 프로세스를 이용하며 장애 격리에 유리하다. |
| 멀티스레드 | 하나의 프로세스에서 자원을 공유하며 여러 실행 흐름을 처리한다. |
| IPC | 독립된 프로세스 사이에서 데이터를 전달하는 방법이다. |
| 경쟁 상태 | 공유 데이터에 대한 실행 순서에 따라 결과가 달라지는 문제다. |
| 동시성 | 여러 작업이 일정 시간 범위에서 함께 진행되는 성질이다. |
| 병렬성 | 여러 작업이 실제로 같은 시점에 실행되는 성질이다. |
프로세스는 생성된 뒤 항상 CPU를 사용하고 있는 것이 아니다.
CPU 할당을 기다리거나 입출력 작업의 완료를 기다리면서 여러 상태를 이동한다.
대표적인 프로세스 상태는 다음과 같다.
| 상태 | 설명 |
|---|---|
| New | 프로세스가 생성되고 있는 상태 |
| Ready | CPU를 할당받으면 바로 실행할 수 있는 상태 |
| Running | CPU를 할당받아 명령어를 실행하고 있는 상태 |
| Waiting 또는 Blocked | 입출력이나 특정 이벤트가 완료되기를 기다리는 상태 |
| Terminated | 실행이 끝나거나 종료된 상태 |
stateDiagram-v2
[*] --> New
New --> Ready: 생성 완료
Ready --> Running: CPU 할당
Running --> Ready: 선점 또는 시간 할당량 만료
Running --> Waiting: 입출력 요청 또는 이벤트 대기
Waiting --> Ready: 입출력 또는 이벤트 완료
Running --> Terminated: 실행 종료
Terminated --> [*]
Ready 상태와 Waiting 상태는 모두 CPU를 사용하지 않는 상태지만, CPU를 사용하지 못하는 이유가 다르다.
실행에 필요한 모든 준비가 끝났지만 다른 프로세스가 CPU를 사용하고 있어 기다리는 상태다.
CPU만 할당되면 즉시 실행할 수 있다.
입출력 작업이나 특정 이벤트가 완료되지 않아 실행을 계속할 수 없는 상태다.
Waiting 상태의 프로세스는 CPU를 할당받아도 필요한 조건이 충족되지 않았기 때문에 작업을 진행할 수 없다.
프로세스 상태는 다음과 같은 원인으로 전환된다.
- New에서 Ready: 프로세스 생성이 완료됨
- Ready에서 Running: 스케줄러가 프로세스에 CPU를 할당함
- Running에서 Ready: 시간 할당량이 끝나거나 다른 프로세스에 의해 선점됨
- Running에서 Waiting: 입출력 작업이나 이벤트를 요청함
- Waiting에서 Ready: 기다리던 입출력이나 이벤트가 완료됨
- Running에서 Terminated: 프로세스 실행이 종료됨
운영체제는 프로세스의 상태에 따라 프로세스를 여러 큐에 저장한다.
시스템에 존재하는 전체 프로세스를 관리하는 큐다.
메모리에 적재되어 있으며 CPU를 할당받기를 기다리는 프로세스가 저장된다.
CPU 스케줄러는 Ready Queue에서 다음에 실행할 프로세스를 선택한다.
입출력이나 특정 이벤트의 완료를 기다리는 프로세스가 저장된다.
기다리던 작업이 완료되면 프로세스는 Waiting Queue에서 제거되어 Ready Queue로 이동한다.
CPU 할당
Ready Queue -------------> Running
^ |
| | 입출력 요청
| v
+------------------- Waiting Queue
입출력 완료
운영체제는 새로운 프로그램을 실행할 때 프로세스를 생성한다.
Unix 계열 운영체제에서는 대표적으로 fork()와 exec()를 사용한다.
fork()는 현재 실행 중인 부모 프로세스를 기반으로 자식 프로세스를 생성한다.
자식 프로세스는 부모 프로세스의 주소 공간을 기반으로 생성되지만, 논리적으로는 독립된 주소 공간을 가진다.
부모 프로세스
|
| fork()
v
부모 프로세스 + 자식 프로세스
초기에는 부모와 자식이 같은 메모리 내용을 가진 것처럼 보이지만, 한쪽에서 메모리를 수정하면 다른 쪽의 메모리가 함께 변경되지는 않는다.
많은 운영체제는 실제 메모리 복사 비용을 줄이기 위해 Copy-on-Write 방식을 사용한다.
Copy-on-Write는 fork() 직후 부모와 자식이 물리 메모리 페이지를 공유하고, 어느 한쪽이 해당 페이지를 수정할 때 실제 복사를 수행하는 방식이다.
fork 직후
부모 가상 주소 ─┐
├── 동일한 물리 페이지
자식 가상 주소 ─┘
한쪽이 페이지 수정
부모 가상 주소 ─── 부모용 물리 페이지
자식 가상 주소 ─── 복사된 자식용 물리 페이지
이 방식을 사용하면 자식 프로세스를 생성할 때 전체 메모리를 즉시 복사하지 않아도 된다.
exec() 계열 시스템 콜은 현재 프로세스의 주소 공간을 새로운 프로그램의 코드와 데이터로 교체한다.
exec()는 새로운 프로세스를 생성하는 것이 아니라, 현재 프로세스가 실행하는 프로그램을 다른 프로그램으로 변경한다.
부모 프로세스
|
| fork()
v
자식 프로세스
|
| exec()
v
새로운 프로그램 실행
프로세스는 작업을 완료하거나 오류가 발생하면 종료된다.
프로세스가 종료되면 운영체제는 해당 프로세스가 사용하던 메모리와 자원을 회수한다.
Unix 계열 운영체제에서는 자식 프로세스가 종료되더라도 부모 프로세스가 종료 상태를 확인할 때까지 일부 정보가 프로세스 테이블에 남을 수 있다.
자식 프로세스가 종료되었지만 부모 프로세스가 종료 상태를 회수하지 않은 상태다.
좀비 프로세스는 실행되지 않으며 대부분의 자원은 해제되었지만, 종료 상태와 프로세스 식별자 등의 정보가 프로세스 테이블에 남아 있다.
부모 프로세스가 자식 프로세스보다 먼저 종료되어 부모가 사라진 프로세스다.
운영체제는 일반적으로 특정 시스템 프로세스가 고아 프로세스를 대신 관리하도록 한다.
Context Switching은 CPU가 현재 실행 중인 프로세스 또는 스레드의 실행 상태를 저장하고, 다음 실행 대상의 상태를 복원하는 과정이다.
CPU는 한 시점에 하나의 실행 흐름만 수행할 수 있다. 여러 프로세스와 스레드를 번갈아 실행하려면 각 실행 흐름의 상태를 저장하고 복원해야 한다.
여기서 Context는 실행을 이어가기 위해 필요한 상태 정보를 의미한다.
- Program Counter
- Stack Pointer
- CPU Register
- CPU 상태 레지스터
- 메모리 관리 정보
- 스케줄링 정보
- 실행 상태
1. 프로세스 A가 CPU에서 실행된다.
2. 인터럽트나 스케줄링 이벤트가 발생한다.
3. 운영체제가 프로세스 A의 Context를 PCB 또는 TCB에 저장한다.
4. 스케줄러가 다음에 실행할 프로세스 B를 선택한다.
5. 프로세스 B의 Context를 PCB 또는 TCB에서 복원한다.
6. CPU가 프로세스 B를 이전 중단 지점부터 실행한다.
sequenceDiagram
participant A as Process A
participant OS as Operating System
participant B as Process B
A->>OS: 실행 중 인터럽트 또는 선점 발생
OS->>OS: A의 Context 저장
OS->>OS: 다음 실행 대상 선택
OS->>OS: B의 Context 복원
OS->>B: B 실행 재개
- CPU 시간 할당량이 만료된 경우
- 더 높은 우선순위의 프로세스가 Ready 상태가 된 경우
- 실행 중인 프로세스가 입출력을 요청한 경우
- 인터럽트가 발생한 경우
- 프로세스가 Waiting 상태로 전환된 경우
- 현재 프로세스가 종료된 경우
- 실행 중인 스레드가 동기화 객체를 기다리는 경우
프로세스 간 Context Switching이 발생하면 운영체제는 현재 프로세스의 CPU 상태를 PCB에 저장하고 다음 프로세스의 상태를 복원한다.
프로세스는 서로 다른 가상 주소 공간을 사용하므로 CPU 실행 상태뿐 아니라 메모리 주소 변환과 관련된 정보도 변경될 수 있다.
- Program Counter
- CPU Register
- Stack Pointer
- 페이지 테이블 기준 정보
- 가상 주소 공간
- 스케줄링 상태
- 커널 Stack
- 메모리 보호 정보
프로세스가 변경되면 CPU Cache와 TLB에 저장된 정보의 활용도가 낮아질 수 있다.
스레드 간 Context Switching에서는 현재 스레드의 실행 상태를 TCB에 저장하고 다음 스레드의 상태를 복원한다.
같은 프로세스에 속한 스레드는 주소 공간과 여러 프로세스 자원을 공유한다.
따라서 일반적으로 다음 정보는 그대로 유지될 수 있다.
- 가상 주소 공간
- Code 영역
- Data 영역
- Heap 영역
- 열린 파일
변경되는 주요 정보는 다음과 같다.
- Program Counter
- CPU Register
- Stack Pointer
- 스레드 상태
- 스케줄링 정보
서로 다른 프로세스에 속한 스레드 사이에서 전환이 발생하면 스레드의 실행 상태뿐 아니라 프로세스의 주소 공간과 메모리 관리 정보도 함께 변경될 수 있다.
따라서 같은 프로세스에 속한 스레드 간 전환보다 비용이 커질 가능성이 높다.
Context Switching은 애플리케이션의 실제 작업을 수행하지 않는다.
운영체제가 다음 실행 대상을 선택하고, 현재 상태를 저장하고, 새로운 상태를 복원하는 동안 CPU는 원래 프로그램의 연산을 진행하지 못한다.
- CPU Register 저장
- Program Counter 저장
- Stack Pointer 저장
- PCB 또는 TCB 갱신
- 다음 실행 대상 선택
- 새로운 Context 복원
- CPU Cache 적중률 감소
- TLB 적중률 감소
- 파이프라인 무효화
- 분기 예측 정보 활용도 감소
- 커널 스케줄러 실행 비용
- 실행 가능한 프로세스와 스레드 수가 지나치게 많을 때
- CPU 시간 할당량이 지나치게 짧을 때
- 입출력 대기가 자주 발생할 때
- Lock 경쟁이 심할 때
- 우선순위가 다른 작업이 자주 선점할 때
프로세스와 스레드의 수를 늘린다고 항상 성능이 좋아지는 것은 아니다. 실행 단위가 지나치게 많으면 Context Switching 비용이 증가해 전체 처리 성능이 낮아질 수 있다.
| 구분 | 프로그램 | 프로세스 |
|---|---|---|
| 의미 | 실행 가능한 명령어와 데이터의 집합 | 실행 중인 프로그램 |
| 상태 | 정적 | 동적 |
| 저장 위치 | 주로 보조 기억장치 | 주로 주기억장치 |
| 실행 상태 | 없음 | CPU 실행 상태를 가짐 |
| 자원 | 실행 자원을 직접 보유하지 않음 | 메모리, 파일, 레지스터 상태 등의 자원을 보유 |
| 개수 | 하나의 프로그램 파일 | 하나의 프로그램으로 여러 프로세스 생성 가능 |
PCB는 프로세스 단위의 자원과 상태를 저장하고, TCB는 스레드 단위의 실행 상태를 저장한다.
PCB
├── 프로세스 ID
├── 주소 공간 정보
├── 열린 파일 정보
├── 프로세스 권한
├── TCB 1
├── TCB 2
└── TCB 3
하나의 프로세스에 여러 스레드가 존재하면 하나의 PCB와 여러 개의 TCB가 연결될 수 있다.
다만 PCB와 TCB의 구체적인 구성과 관계는 운영체제 구현에 따라 다르다. 일부 운영체제는 프로세스와 스레드를 공통 실행 단위로 표현하고 하나의 자료구조 안에서 관리하기도 한다.
| 구분 | PCB | TCB |
|---|---|---|
| 전체 이름 | Process Control Block | Thread Control Block |
| 관리 대상 | 프로세스 | 스레드 |
| 저장 정보 | 주소 공간, 열린 파일, 프로세스 상태, 권한 | 레지스터, Program Counter, Stack Pointer, 스레드 상태 |
| 생성 개수 | 일반적으로 프로세스마다 하나 | 일반적으로 스레드마다 하나 |
| 공유 관계 | 프로세스 전체 자원 관리 | 같은 프로세스의 PCB와 자원 정보를 참조 |
| 비교 기준 | 프로세스 | 스레드 |
|---|---|---|
| 의미 | 실행 중인 프로그램이자 자원 관리 단위 | 프로세스 내부의 실행 흐름 |
| 주소 공간 | 프로세스마다 독립적 | 같은 프로세스의 주소 공간 공유 |
| Code 영역 | 프로세스마다 독립적 | 같은 프로세스의 스레드가 공유 |
| Data 영역 | 프로세스마다 독립적 | 같은 프로세스의 스레드가 공유 |
| Heap 영역 | 프로세스마다 독립적 | 같은 프로세스의 스레드가 공유 |
| Stack 영역 | 프로세스마다 독립적 | 스레드마다 독립적 |
| Program Counter | 프로세스 실행 흐름에 필요 | 스레드마다 독립적으로 보유 |
| CPU Register | 실행 단위별로 저장 | 스레드마다 독립적으로 보유 |
| 자원 공유 | IPC가 필요함 | 공유 메모리에 직접 접근 가능 |
| 생성 비용 | 상대적으로 큼 | 상대적으로 작음 |
| 전환 비용 | 상대적으로 큼 | 같은 프로세스 내에서는 상대적으로 작음 |
| 장애 영향 | 다른 프로세스와 격리됨 | 하나의 스레드 오류가 전체 프로세스에 영향을 줄 수 있음 |
| 동기화 | 프로세스 간 통신 방식 필요 | 공유 자원에 대한 동기화 필요 |
각 프로세스는 독립된 주소 공간을 사용한다.
한 프로세스에서 잘못된 메모리 접근이 발생해도 다른 프로세스의 메모리에 직접 영향을 주지 않도록 격리하기 위해서다.
스레드는 하나의 프로세스 내부에서 여러 실행 흐름을 만들기 위한 단위다.
같은 작업에 필요한 코드와 데이터를 공유함으로써 메모리 사용량과 통신 비용을 줄일 수 있다.
하지만 여러 스레드가 같은 데이터에 동시에 접근하면 경쟁 상태가 발생할 수 있다.
스레드는 누가 스레드를 관리하고 스케줄링하는지에 따라 사용자 수준 스레드와 커널 수준 스레드로 구분할 수 있다.
사용자 영역의 스레드 라이브러리가 스레드를 생성하고 관리한다.
커널은 개별 사용자 스레드의 존재를 직접 알지 못하고 하나의 프로세스 또는 제한된 수의 실행 단위만 인식할 수 있다.
- 커널 모드 전환 없이 스레드를 생성할 수 있다.
- 스레드 생성과 전환 비용이 상대적으로 작다.
- 운영체제와 무관한 스케줄링 정책을 구현할 수 있다.
- 하나의 스레드가 블로킹 시스템 콜을 호출하면 전체 프로세스가 대기할 수 있다.
- 커널이 개별 스레드를 알지 못하면 여러 CPU 코어에 스레드를 직접 배치하기 어렵다.
- 운영체제의 스케줄링 지원을 충분히 활용하기 어렵다.
운영체제 커널이 스레드를 직접 생성하고 관리한다.
커널은 각 스레드의 상태를 알고 있으며 스레드 단위로 CPU를 할당할 수 있다.
- 하나의 스레드가 대기 상태가 되어도 다른 스레드를 실행할 수 있다.
- 여러 CPU 코어에 스레드를 배치하여 병렬 실행할 수 있다.
- 운영체제의 스케줄링 기능을 활용할 수 있다.
- 스레드 생성과 관리에 커널이 개입한다.
- 스레드 전환 시 사용자 모드와 커널 모드 사이의 전환이 필요할 수 있다.
- 사용자 수준 스레드보다 관리 비용이 클 수 있다.
여러 사용자 스레드를 하나의 커널 스레드에 연결한다.
User Thread 1 ─┐
User Thread 2 ─┼── Kernel Thread 1
User Thread 3 ─┘
하나의 사용자 스레드가 블로킹되면 전체 스레드가 실행되지 못할 수 있으며, 여러 CPU 코어를 활용한 병렬 실행이 어렵다.
사용자 스레드 하나를 커널 스레드 하나에 연결한다.
User Thread 1 ─── Kernel Thread 1
User Thread 2 ─── Kernel Thread 2
User Thread 3 ─── Kernel Thread 3
여러 스레드를 병렬로 실행할 수 있지만, 많은 스레드를 생성하면 커널이 관리해야 하는 스레드 수도 증가한다.
여러 사용자 스레드를 여러 커널 스레드에 연결한다.
User Thread 1 ─┐ ┌── Kernel Thread 1
User Thread 2 ─┼────┤
User Thread 3 ─┤ └── Kernel Thread 2
User Thread 4 ─┘
사용자 수준 스레드의 유연성과 커널 수준 스레드의 병렬성을 함께 활용하는 방식이다.
Context Switching과 Mode Switching은 같은 개념이 아니다.
CPU의 실행 권한이 사용자 모드와 커널 모드 사이에서 변경되는 것이다.
시스템 콜이나 인터럽트가 발생하면 사용자 모드에서 커널 모드로 전환될 수 있다.
현재 실행 중인 프로세스 또는 스레드를 다른 프로세스 또는 스레드로 변경하는 것이다.
시스템 콜이 발생하더라도 같은 프로세스가 계속 실행된다면 Mode Switching만 발생하고 Context Switching은 발생하지 않을 수 있다.
| 구분 | Mode Switching | Context Switching |
|---|---|---|
| 변경 대상 | CPU 권한 수준 | 실행 중인 프로세스 또는 스레드 |
| 대표 원인 | 시스템 콜, 예외, 인터럽트 | 선점, 입출력 대기, 스케줄링 |
| 상태 저장 | 일부 CPU 상태 저장 | 실행 Context 전체 저장 및 복원 |
| 실행 대상 변경 | 반드시 변경되는 것은 아님 | 다른 실행 대상으로 변경됨 |
| 방식 | 특징 | 장점 | 단점 |
|---|---|---|---|
| Pipe | 단방향 바이트 스트림 | 구조가 단순함 | 관계가 있는 프로세스 중심 |
| Named Pipe | 이름이 있는 파이프 | 관계없는 프로세스도 사용 가능 | 파일 시스템 객체 관리 필요 |
| Message Queue | 메시지 단위 전달 | 비동기 통신 가능 | 커널 복사 비용 발생 가능 |
| Shared Memory | 메모리 직접 공유 | 데이터 전송 속도가 빠름 | 동기화가 필요함 |
| Socket | 통신 종단점 사용 | 원격 프로세스 통신 가능 | 프로토콜 처리 비용 |
| Signal | 이벤트 통지 | 간단한 제어 가능 | 전달 정보가 제한적 |
같은 프로세스의 주소 공간을 공유하므로 데이터를 전달하기 위해 별도의 IPC를 사용할 필요가 적다.
프로세스 전체를 생성하는 것보다 스레드 실행 정보를 생성하는 비용이 일반적으로 작다.
같은 프로세스의 스레드 간에는 주소 공간을 공유하므로 프로세스 전환보다 변경해야 하는 정보가 적을 수 있다.
하나의 스레드가 입출력을 기다리는 동안 다른 스레드가 작업을 수행할 수 있다.
여러 CPU 코어가 있다면 여러 스레드를 서로 다른 코어에서 병렬로 실행할 수 있다.
여러 스레드가 공유 데이터를 동시에 읽고 수정하면 실행 순서에 따라 결과가 달라질 수 있다.
공유 데이터의 일관성을 유지하기 위해 Mutex, Semaphore, Monitor 등의 동기화 기법이 필요하다.
여러 스레드가 서로가 보유한 자원을 기다리면서 무한히 대기할 수 있다.
하나의 스레드에서 잘못된 메모리 접근이 발생하면 전체 프로세스가 종료될 수 있다.
스레드 실행 순서는 매번 달라질 수 있으므로 동시성 오류를 재현하기 어렵다.
| 비교 기준 | 멀티프로세스 | 멀티스레드 |
|---|---|---|
| 실행 단위 | 여러 프로세스 | 하나의 프로세스 내부의 여러 스레드 |
| 주소 공간 | 각각 독립적 | 같은 프로세스의 주소 공간 공유 |
| 데이터 공유 | IPC 필요 | 공유 메모리에 직접 접근 |
| 생성 비용 | 상대적으로 큼 | 상대적으로 작음 |
| Context Switching | 상대적으로 비용이 큼 | 같은 프로세스 내에서는 상대적으로 작음 |
| 메모리 사용량 | 상대적으로 큼 | 상대적으로 작음 |
| 장애 격리 | 높음 | 낮음 |
| 동기화 | IPC 방식에 따라 필요 | 공유 자원 동기화가 중요 |
| 구현 난이도 | 통신 구조 설계가 필요 | 동시성 제어가 어려움 |
| 확장 방식 | 프로세스 단위 | 스레드 단위 |
- 작업 간 높은 독립성이 필요한 경우
- 하나의 작업 실패가 다른 작업에 영향을 주지 않아야 하는 경우
- 보안과 권한 분리가 중요한 경우
- 프로세스 단위의 병렬 처리가 필요한 경우
- 여러 실행 흐름이 데이터를 자주 공유해야 하는 경우
- 실행 단위 생성 비용을 줄여야 하는 경우
- 하나의 프로세스 내부에서 여러 작업을 동시에 처리해야 하는 경우
- 공유 자원에 대한 동기화를 적절히 설계할 수 있는 경우
어떤 방식이 항상 더 우수한 것은 아니다.
독립성과 장애 격리가 중요하면 멀티프로세스가 유리하고, 자원 공유와 상대적으로 가벼운 실행 단위가 중요하면 멀티스레드가 유리하다.
멀티프로세스와 멀티스레드를 이해하려면 동시성과 병렬성을 구분해야 한다.
여러 작업이 일정한 시간 범위 안에서 함께 진행되는 성질이다.
단일 CPU 코어에서도 운영체제가 실행 대상을 빠르게 전환하면 여러 작업이 동시에 진행되는 것처럼 보일 수 있다.
시간 ─────────────────────────>
Task A 실행 ── 대기 ── 실행 ── 대기
Task B 대기 ── 실행 ── 대기 ── 실행
여러 작업이 실제로 같은 시점에 실행되는 성질이다.
여러 CPU 코어를 사용하여 서로 다른 작업을 물리적으로 동시에 수행한다.
시간 ─────────────────────────>
Core 1 Task A ────────────────
Core 2 Task B ────────────────
| 구분 | 동시성 | 병렬성 |
|---|---|---|
| 의미 | 여러 작업을 함께 다루는 구조 | 여러 작업을 실제로 동시에 실행 |
| 단일 코어 | 가능 | 일반적으로 불가능 |
| 다중 코어 | 가능 | 가능 |
| 핵심 | 실행 순서의 전환 | 물리적인 동시 실행 |
| 목적 | 응답성과 자원 활용 | 처리 시간 단축과 처리량 증가 |
멀티스레드를 사용한다고 반드시 병렬로 실행되는 것은 아니다.
스레드 수가 여러 개이더라도 CPU 코어가 하나라면 스레드는 Context Switching을 통해 번갈아 실행된다.
프로세스는 실행 중인 프로그램이다. 운영체제는 프로세스마다 독립된 가상 주소 공간과 CPU 실행 상태, 메모리 정보, 열린 파일 등의 자원을 관리한다.
프로그램은 디스크에 저장된 정적인 명령어와 데이터의 집합이고, 프로세스는 프로그램이 메모리에 적재되어 실행 중인 동적인 상태다. 하나의 프로그램으로 여러 프로세스를 생성할 수 있다.
일반적으로 Text, Data, BSS, Heap, Stack 영역으로 구분한다. Text에는 실행 코드, Data에는 초기화된 전역 변수, BSS에는 초기화되지 않은 전역 변수, Heap에는 동적 할당 메모리, Stack에는 함수 호출 정보와 지역 변수가 저장된다.
Ready 상태는 CPU만 할당받으면 실행할 수 있는 상태다. Waiting 상태는 입출력이나 특정 이벤트가 완료되지 않아 CPU를 받아도 작업을 계속할 수 없는 상태다.
PCB는 운영체제가 프로세스를 관리하기 위해 사용하는 자료구조다. 프로세스 ID, 상태, Program Counter, CPU Register, 메모리 정보, 스케줄링 정보, 열린 파일 정보 등이 저장된다.
프로세스가 CPU를 빼앗긴 뒤 다시 실행될 때 이전 실행 위치와 CPU 상태를 복원해야 하기 때문이다. 운영체제는 프로세스의 Context를 PCB에 저장하고 이후 다시 CPU에 복원한다.
스레드는 프로세스 내부에서 실행되는 하나의 실행 흐름이다. 같은 프로세스의 스레드는 Code, Data, Heap, 열린 파일 등을 공유하지만 Stack, Program Counter, CPU Register는 각각 독립적으로 가진다.
각 스레드는 서로 다른 함수를 실행하고 서로 다른 지역 변수와 함수 호출 기록을 가져야 하기 때문이다. Stack을 공유하면 스레드별 함수 실행 상태를 구분할 수 없다.
TCB는 운영체제가 스레드를 관리하기 위해 사용하는 자료구조다. Thread ID, 스레드 상태, Program Counter, CPU Register, Stack Pointer, 스케줄링 정보 등이 저장된다.
PCB는 주소 공간, 열린 파일, 권한 등 프로세스 단위의 자원과 상태를 저장한다. TCB는 Program Counter, CPU Register, Stack Pointer 등 스레드 단위의 실행 상태를 저장한다. 하나의 프로세스에 여러 스레드가 있다면 하나의 PCB와 여러 TCB가 연결될 수 있다.
프로세스는 독립된 주소 공간과 자원을 가지는 실행 단위다. 스레드는 프로세스 내부의 실행 흐름으로, 같은 프로세스에 속한 스레드끼리 주소 공간과 자원을 공유한다.
CPU가 현재 실행 중인 프로세스나 스레드의 상태를 PCB 또는 TCB에 저장하고, 다음 실행 대상의 상태를 복원하는 과정이다.
CPU Register와 Program Counter를 저장하고 복원해야 하며, 스케줄러 실행과 PCB 또는 TCB 갱신이 필요하기 때문이다. 프로세스 전환에서는 주소 공간 변경으로 인해 Cache와 TLB의 활용도가 낮아질 수도 있다.
Mode Switching은 CPU 권한이 사용자 모드와 커널 모드 사이에서 변경되는 것이다. Context Switching은 실행 중인 프로세스나 스레드가 다른 실행 대상으로 변경되는 것이다. Mode Switching이 발생하더라도 실행 대상이 같다면 Context Switching은 발생하지 않을 수 있다.
프로세스는 서로 다른 주소 공간을 사용하므로 CPU 상태뿐 아니라 메모리 관리 정보도 변경해야 할 수 있다. 같은 프로세스의 스레드는 주소 공간을 공유하므로 주로 Register, Program Counter, Stack Pointer 등의 실행 상태만 변경하면 된다.
장점은 프로세스 간 독립성이 높고 장애 격리와 보안에 유리하다는 것이다. 단점은 프로세스 생성과 Context Switching 비용이 크고, 데이터를 공유하려면 IPC가 필요하다는 것이다.
장점은 자원을 쉽게 공유할 수 있고 생성 및 전환 비용이 상대적으로 작다는 것이다. 단점은 공유 데이터에 대한 경쟁 상태와 교착 상태가 발생할 수 있고, 하나의 스레드 오류가 전체 프로세스에 영향을 줄 수 있다는 것이다.
항상 한쪽이 더 좋은 것은 아니다. 장애 격리와 독립성이 중요하면 멀티프로세스가 적합하고, 자원 공유와 비교적 가벼운 실행 단위가 중요하면 멀티스레드가 적합하다. 작업 특성과 동기화 비용, 메모리 사용량, 장애 허용 범위를 함께 고려해야 한다.
동시성은 여러 작업이 일정한 시간 범위 안에서 함께 진행되는 구조이고, 병렬성은 여러 작업이 실제로 같은 시점에 실행되는 것이다. 단일 코어에서도 동시성은 가능하지만 병렬성을 위해서는 일반적으로 여러 CPU 코어가 필요하다.
- Microsoft Learn - Processes and Threads
- Microsoft Learn - About Processes and Threads
- Linux man-pages - pthreads(7)
백현빈 → 강민주 → 조수빈 → 임현빈 → 전병훈 → 이건희
(이후 동일한 순서로 반복)