| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 1 | 2 | 3 | 4 | 5 | 6 | 7 |
| 8 | 9 | 10 | 11 | 12 | 13 | 14 |
| 15 | 16 | 17 | 18 | 19 | 20 | 21 |
| 22 | 23 | 24 | 25 | 26 | 27 | 28 |
Tags
- trie
- 비트마스킹
- 게임 서버 아키텍처
- LCA
- Strongly Connected Component
- 최소 공통 조상
- 2-SAT
- 자바스크립트
- DP
- ccw 알고리즘
- JavaScript
- 트라이
- PROJECT
- 분리 집합
- Binary Lifting
- R 그래프
- 그래프 탐색
- Github
- Express.js
- Prisma
- Behavior Design Pattern
- MongoDB
- map
- 강한 연결 요소
- SCC
- 벨만-포드
- Spin Lock
- 비트필드를 이용한 dp
- 이분 탐색
- localstorage
Archives
- Today
- Total
dh_0e
[OS] Thread 본문
Process 복습
- Process는 시스템에서 다음과 같은 추상화 단위를 나타냄
- Execution Unit(실행 단위): 스케줄링의 기본 단위
- Protection Domain(보호 영역): 소유하고 있는 자원에 대한 보호를 제공
- Program과의 관계
- 기존의 프로세스 개념은 단일 실행흐름(single thread of control)을 가진 채 실행 중인 프로그램을 의미
- "만약 1개의 실행 흐름을 여러 개로 만든다면?" 스레드 등장의 배경이 됨
Thread
- 정의
- Execution Unit: Process 내의 실행 흐름
- Finer Grain(더 작은 단위): Process보다 더 작은 실행 단위
- linux에선 light-weight process(LWP)라 명명하고 사용
- Process가 제공하는 Protection Domain은 없음
- 스레드들은 프로세스의 보호 영역을 공유
- 동기
- 병렬 작업 완수: 하나의 프로세스가 하나의 일만 처리하는 한계를 극복하기 위해, 프로세스 내의 작업을 여러 개로 나누어 각각을 스레드 화하여 병렬적으로 작업을 완수
- Cooperative Process와의 차이점
- Cooperative Process(협력 프로세스): 프로세스 간 통신(IPC)이 필요하며, 비용(Context Switching 등)이 많이 듬
- Thread를 이용한 Cooperation: 프로세스 내에서 협력하는 스레드를 만들면, 프로세스보다 적은 비용으로 협력 프로세스가 하는 일을 동일하게 수행 가능
- ex) CPU 16코어가 240개의 프로세스, 3300개의 스레드를 맡았을 경우
- 240개의 주방에서 3300개의 작업(스레드)이 쏟아져 나온다. 16명의 요리사는 특정 주방을 가리지 않고(프로세스가 코어에 배정받는 게 아님), 지금 당장 급한 작업(스레드)을 낚아채서 처리한다. 심지어 한 주방에 요리사 여러 명이 동시에 들어가서 왁자지껄하게 일하기도 하고, 0.01초마다 다른 주방, 다른 작업으로 순간 이동하며 일을 처리한다.
Thread & CPU Utilization
- CPU Utilization 증가: 스레드의 수가 증가할수록 CPU의 활용도(Utilization)가 증가하여 처리량(Throughput)이 향상됨
- 임계값 이후 감소: 스레드 수가 임계값을 넘어가면 스레드 전환(Switching) 비용이 증가하여 처리량이 다시 감소함
- Multi-processor 시스템의 유리함: CPU의 수가 많은 시스템(Multi-processor)일수록, 한 프로세스 내에서 여러 스레드를 병렬적으로 실행하는 것이 유리함

- 그래프 개요
- CPU 수가 많을수록 (x>y>z) 전체적인 처리량 곡선이 높게 형성됨
- 모든 경우에서 스레드 수가 증가하다가 특정 임계값을 지나면 처리량이 감소하는 모양을 보임
Process vs Thread
| 구분 | Process (무거운 단위, Heavy Weight) | Thread (가벼운 단위, Light Weight) |
| 실행 흐름 | 하나의 Thread와 같은 실행 흐름 | 하나의 Process 안에 여러 개의 Thread 존재 |
| 메모리 독립성 | 독립적. 서로 접근하기 어려움 | Process의 Code/Data 영역을 공유 |
| Context Switch 비용 | 큼 (상대적으로 Heavy Weight) | 적음 (같은 Memory 영역 사용) |
Thread 구성 요소
- Thread 고유 정보(Not-Shared)
- Thread ID: 스레드 식별자
- Program Counter(PC): 현재 실행 중인 Instruction 주소
- Register Set: CPU의 레지스터 값들
- Stack: 지역 변수 및 함수 호출 정보 저장
- 프로세스의 Stack 영역에 저장되는 것이 아니라 Free Space에 thread를 할당
- 동일한 Process 내 다른 Thread와 공유하는 것(Shared)
- Code: Program(Process)의 Code Section
- Data: Process의 Data Section
- File: Process에서 Open한 file
[ 높은 주소 (0xFF...)]
+---------------------------+
| OS Kernel Space |
+---------------------------+
| [메인 스레드 Stack] | <-- '프로세스 스택' (주인공 전용)
| ↓ (아래로 자람) |
+---------------------------+
| |
| (빈 공간) |
| |
+---------------------------+
| [스레드 2의 Stack] | <-- ★ 여기에 새로 할당! (mmap)
| ↓ |
+---------------------------+
| (Guard Page) | <-- 서로 침범 못 하게 막는 벽
+---------------------------+
| [스레드 3의 Stack] | <-- ★ 여기도 새로 할당!
| ↓ |
+---------------------------+
| |
| (mmap 영역) |
| |
+---------------------------+
| ↑ (위로 자람) |
| [Heap (동적 할당 영역)] | <-- malloc() 하면 여기 씀
+---------------------------+
| [Data / BSS] | <-- 전역 변수
+---------------------------+
| [Code (Text)] | <-- 240명 요리사가 공유하는 레시피
+---------------------------+
[ 낮은 주소 (0x00...)]


- P1, P2는 메모리를 공유하지 않음
- T1, T2는 메모리를 공유하면서 각자의 스택 영역이 존재
- 표준 스택 영역(커널 바로 밑의 가장 높은 주소)에 가변적인 메모리를 할당받은 T1이 P3의 메인 스레드임
- T2는 메인 스택(T1) 아래쪽의 빈 공간(Heap과의 사이)에 고정적인 크기를 할당받음
Multi-threaded program의 장점
- Responsiveness(반응성)
- 프로그램의 특정 스레드가 블록(Block)되거나 시간이 오래 걸리는 작업을 수행하더라도, 다른 스레드들은 계속 실행되고 있기 때문에 사용자 입장에서는 프로그램이 Interactive 하다고 느낄 수 있음
- Resourcing Sharing(자원 공유)
- 스레드들은 프로세스의 메모리와 다른 자원들을 공유함
- Economy(경제성)
- 스레드는 하나의 프로세스 메모리 영역에서 실행되므로, 새로운 프로세스를 생성하는 것보다 스레드를 생성하는 것이 비용이 적게 듬
- Scalability(확장성)
- 여러 개의 스레드가 각각 다른 Processor(CPU 코어)에서 동시에 실행될 수 있음
Multicore Programming
- 최근 설계 동향
- 최근 프로세서(코어)는 하나의 칩에 여러 개의 컴퓨팅 코어(Multicore Processor)를 탑재
- 효율성: 멀티스레드 프로그래밍은 멀티코어 시스템에서 특히 효율적임
- 운영체제는 각각의 코어를 하나의 프로세서로 인식하고 스케줄링
- 각각의 스레드에 코어를 할당하여 병렬실행이 가능해짐
- Cache 공유
- 멀티코어 시스템은 캐시(Cache)를 공유하기 때문에, 데이터, 코드 등 프로세스의 자원을 공유하는 멀티스레드 프로그래밍에 더욱 효율적임
User and Kernel Threads
- 스레드를 지원하는 주체에 따라 사용자 스레드(User thread)와 커널 스레드(Kernel thread)로 나뉨
User Thread(사용자 수준 스레딩)
| 특징 | 설명 |
| 구현 주체 | Kernel 영역 위, 일반적으로 User Level의 Library를 통해 구현됨 (라이브러리가 생성/스케줄링 관리) |
| 장점 | 동일 메모리 영역에서 관리되므로 속도가 빠르다. |
| 단점 | 하나의 스레드가 System Call 요청으로 Block되면, 커널은 이들을 하나의 프로세스로 간주하므로 나머지 모든 스레드 역시 Block된다. |

- Core 수가 낮을 때 주로 사용
Kernel Thread(커널 수준 스레딩)
| 특징 | 설명 |
| 구현 주체 | 운영체제(Kernel)에서 스레드 생성, 스케줄링 등을 관리함 |
| 장점 1 | 하나의 스레드가 System Call 호출로 Block되면, 커널은 다른 스레드를 실행시켜 전체적인 Thread Blocking이 없음. |
| 장점 2 | Multiprocessor 환경에서 커널이 여러 스레드를 다른 프로세서에 할당할 수 있음. |
| 단점 | User Thread보다 생성 및 관리가 느림 (커널 개입 필요). |
- 현대 방식에서 User Thread보다 선호됨
Mapping of User & Kernel Thread
1. Many-to-One (다대일)
- 매핑 방식: 여러 개의 User Level Thread들이 하나의 Kernel Thread로 매핑됨
- 주요 관리: Thread 관리는 User level에서 이루어짐
- 한계점(Concurrency 문제)
- System Call Blocking: 한 번에 하나의 스레드만 커널에 접근 가능
하나의 스레드가 커널에 접근(System Call)하면 나머지 스레드들은 대기해야 하므로 진정한 Concurrency를 지원하지 못함 - Multiprocessor 비효율: 커널 입장에서 여러 스레드는 하나의 프로세스이므로, Multiprocessor 환경에서도 여러 개의 Processor에서 동시에 수행 불가
- System Call Blocking: 한 번에 하나의 스레드만 커널에 접근 가능

2. One-to-One
- 매핑 방식: 각각의 User Thread를 Kernel Thread로 매핑
- User Thread가 생성될 때마다 그에 따른 Kernel Thread가 생성됨
- 장점
- Many-to-One의 문제였던 System Call 호출 시 다른 thread가 Block 되는 문제를 해결
- 여러 개의 스레드를 Multiprocessor에서 동시에 수행할 수 있음
- 한계점
- Kernel Thread는 한정된 자원이기 때문에, 무한정 생성할 수 없음
- 생성할 스레드 개수에 대한 고려가 필요
- Kernel Thread는 한정된 자원이기 때문에, 무한정 생성할 수 없음

3. Many-to-Many
- 매핑 방식: 여러 개의 User Thread를 여러 개의 Kernel Thread로 매핑
- 특징: Kernel Thread는 생성된 User Thread 수와 같거나 적은 수만큼만 생성되어 적절히 Scheduling 됨
- 장점
- One-to-One처럼 사용할 스레드 수에 대해 고민할 필요 없음
- Many-to-One처럼 System Call 사용 시 다른 thread가 Block 되는 현상 없음
- 커널이 User Thread와 Kernel Thread 사이의 매핑을 적절히 조절하여 위의 장점들을 모두 보장할 수 있음
Thread로 인한 OS의 변화
- 멀티스레드 개념이 도입되면서 기존 프로세스 기반 운영체제의 System Call(특히 fork(), exec())의 의미에 대한 고려가 필요해짐
- Process 관련 System Call Semantics는 Process 기준으로 작성됨
- fork(), exec(): Thread 지원을 위해서는 변화가 필요(더 light 한 fork가 필요)
- linux의 fork() & clone()
- fork: 표준적인 프로세스 복제(메모리 독립 프로세스)
- 실제로는 Copy-on-Write로 처음엔 메모리 페이지를 공유하다가 누가 쓰면 그때 복사
- clone: 리눅스식 만능 생성기(메모리 공유 옵션 설정으로 프로세스/스레드 둘 다 만들 수 있음)
- fork: 표준적인 프로세스 복제(메모리 독립 프로세스)
- linux의 fork() & clone()
- Thread 종료 문제: Multi-thread일 경우, 함께 일하는 thread의 종료는 process보다 복잡해짐 (thread 간의 동기화 복잡 문제)
- fork(), exec(): Thread 지원을 위해서는 변화가 필요(더 light 한 fork가 필요)
- Multi-threaded Programming에 대한 지원
- 스레드를 도입하면 OS가 프로세스만 관리하던 때와 달리, 실행 단위가 더 잘게 쪼개지면서 커널/런타임이 추가로 책임져야 할 것들이 생김
- thread 스케줄링
- thread 간 통신 방법
- thread가 사용할 메모리 공간의 할당
- Stack, Thread-specific Data
- 스레드를 도입하면 OS가 프로세스만 관리하던 때와 달리, 실행 단위가 더 잘게 쪼개지면서 커널/런타임이 추가로 책임져야 할 것들이 생김
Thread Issues: Creation (fork와 exec)
- Multithreaded Program에서 fork와 exec의 의미가 달라져야 함
- fork() System Call의 문제: 하나의 스레드가 fork()를 호출했을 때, 다음 두 가지 중 무엇을 만들 것인지 문제가 발생
- 모든 스레드를 포함한 프로세스 복사
- 부모 프로세스의 모든 스레드를 복사하여 자식 프로세스를 만듦
- fork를 요청한 스레드만 복사
- fork를 호출한 스레드만을 복사한 프로세스를 만듦
- 모든 스레드를 포함한 프로세스 복사
- exec() System Call과의 관계
- exec() 호출은 현재 프로세스의 메모리를 새로운 프로그램으로 완전히 교체함
- fork 후 exec을 수행할 경우
- 메모리 낭비: 모든 스레드를 복사한 경우, exec 수행 시 복사된 나머지 스레드들은 불필요하게 복사된 후 교체
- fork를 요청한 스레드만이 복사되는 것이 더 바람직함
- fork만 수행하고 exec을 수행하지 않는 경우
- 자식 프로세스가 부모 프로세스의 자원을 이용해야 할 수 있으므로 모든 스레드의 복사가 필요하기도 함
- fork 후 부모와 자식이 협력하여 계속 일을 해야 한다면, 모든 스레드를 복사하는 것이 맞음
- 보통 Multithreaded Program에선 exec를 잘 수행하지 않음
- 자식 프로세스가 부모 프로세스의 자원을 이용해야 할 수 있으므로 모든 스레드의 복사가 필요하기도 함
- fork 후 exec을 수행할 경우
- 운영체제는 이 두 가지 상황을 모두 처리할 수 있도록 fork의 버전을 달리 제공해야 함
- exec() 호출은 현재 프로세스의 메모리를 새로운 프로그램으로 완전히 교체함

Thread Issues: Cancellation
- Thread Cancellation: Thread의 작업이 끝나기 전에 외부에서 작업을 중지시키는 것
- 하나의 Thread에서 중지 명령이 결국은 다른 Thread의 모든 작업을 중지시켜야 하는 경우
- ex) Web Browser에서 Image를 읽어 오는 과정에서 사용자가 Stop 버튼을 눌러 취소할 경우, Image를 읽어오는 Thread 역시 중지가 되어야 함
- ★ 자원이 Thread에게 할당된 경우 Cancellation의 문제
- 스레드가 시스템 자원을 사용하고 있는 상태에서 중지될 경우, 할당된 자원을 함부로 해제할 수 없음
- 다른 스레드가 그 자원을 사용하고 있을지도 모르기 때문
- ex) Malloc() cancelled without deallocation
- 스레드가 시스템 자원을 사용하고 있는 상태에서 중지될 경우, 할당된 자원을 함부로 해제할 수 없음
Thread Issues: Thread Pools
- Thread Pool 도입 배경
- 스레드가 자주 생성되고 제거되는 상황에서, 새로 스레드를 만드는 시간이 실제 스레드가 동작하는 시간보다 길 수 있음
- 스레드는 시스템 자원이므로, 시스템 동작을 보장하기 위한 최대한의 스레드 수 제한이 필요
- Thread Pool 동작 과정
- Pool 생성: 프로세스가 새로 실행될 때, 정해진 수만큼의 스레드를 미리 만들어 Pool에 할당해 둠
- 새로운 Thread가 필요하면 Pool에서 가져오고, 작업이 끝나면 그 Thread를 제거하지 않고 다시 Pool에 보관
- 장점
- 생성 시간 절약: 스레드 생성에 걸리는 시간을 줄일 수 있음
- 시스템 부하 감소: 많은 스레드 생성에 따른 시스템 부하를 줄일 수 있음
- Pool 크기 결정: Pool에서 관리할 Thread의 수는 System의 Memory나 예상되는 작업의 수를 통해 휴리스틱하게 결정
- 개발자의 역량임
Thread Issues: Thread 간 IPC
- Multithreaded Programming에서는 Process가 아닌 Thread 간 통신이 필요함(ITC)
- 구현 방식: 동일한 프로세스 내의 Thread 간 통신은, 공유 메모리가 가장 효율적임
- 이유: Thread들은 같은 Process의 Data 영역을 공유하므로, 자연스러운 공유 메모리가 가능해짐
- 결과: 스레드 간 자원 공유로 인해 IPC가 최소화됨
- 다른 Process에 존재하는 Thread와의 통신
- 일반 Process의 경우 비슷한 성능을 보임
- ex) Message 전달, Pipe 등
- 이러한 통신이 빈번하다면, 프로그램 설계의 잘못으로 간주
- 스레드의 장점(공유 메모리)을 활용하지 못하고 있기 때문
- 그냥 Multi Process랑 다를 게 없음
- 일반 Process의 경우 비슷한 성능을 보임
'Operating System' 카테고리의 다른 글
| [OS] Synchronization II (0) | 2025.12.05 |
|---|---|
| [OS] Synchronization I (0) | 2025.12.04 |
| [OS] IPC(Inter Process communication) (0) | 2025.11.14 |
| [OS] CPU Scheduling (1) | 2025.11.10 |
| [OS] Basic H/W Mechanisms (Bus, I/O Event, Handling and Device Access Mechanisms) (0) | 2025.11.08 |