운영체제 수업을 수강하며 정리한 내용을 작성하려고 합니다.
스레드와 관련된 이슈들
- Semantics of system calls - exec(), fork()
- exec(), fork()와 같은 함수의 의미가 모호해짐
- Thread cancellation
- 실행중인 스레드를 강제로 종료시키는 것. 적절히 처리하지 않으면 메모리 누수나 데드락의 문제를 야기할 수 있음
- Thread pools
- 스레드의 생성, 관리, 재사용을 담당하는 스레드 관리 기법
- 작업 처리를 위해 필요한 스레드를 미리 생성하여 관리하고, 작업 처리가 요청되면 스레드 풀에서 미리 생성한 스레드 중 하나를 할당하여 작업을 처리하도록 함
- 장점 : 스레드 생성, 삭제에 따른 오버헤드 감소, 재사용에 따른 성능 향상, 스레드 수 조절에 따른 부하 분산
- Communication between threads
Execution : fork() and exec()
- 멀티스레드 프로그램에서 fork와 exec의 의미는 달라져야 함
- fork : 하나의 프로그램 내 스레드가 fork를 호출하면
- 모든 스레드를 가지고 있는 프로세스를 만들 것인가?
- fork를 요청한 thread만을 복사한 프로세스를 만들 것인가?
- 일부 UNIX 기반 시스템에서는 2가지 버전의 fork를 만들어 각각의 경우 처리
- exec: 생성한 프로세스에 실행할 프로그램 교체
- 일반적으로 fork를 하여 모든 스레드를 복사했을 경우, exec 수행 시 모든 스레드들이 새로운 프로그램으로 교체됨
- 일반적으로 스레드들은 하나의 프로그램 내에 구현이 되어 있다고 가정됨
Thread Cancellation
- 스레드 작업이 끝나기 전에 외부에서 작업을 중지시키는 것
- Target thread : 종료 대상 스레드
- 두 가지 방법
- Asynchronous cancellation : 한 스레드가 target thread를 즉시 종료
- Defferd cancellation : target thread가
가 가능할 때 종료함
- 이유 : 다른 thread가 그 자원을 사용하고 있을지도 모르기 때문
- e.g., target thread가 생성한 자료구조를 다른 thread가 공유, 사용할 때
- 기본적으로 스레드를 즉시 삭제하지 않음
- 리눅스 시스템에서, cancellation은 시그널로 처리됨
Thread pools
- 스레드가 자주 생성되고 제거되는 상황에서 새로운 스레드를 만드는 시간이 실제 스레드가 동작하는 시간보다 긴 경우가 존재
- thread pool
- Application별로 정해진 수의 스레드들을 미리 생성 (TCB등 필요한 구조체를 선할당)
- 미리 생성된 스레드들을 스레드 풀로 관리
- 새로운 스레드가 필요하면 pool에서 가져오고, 작업이 끝나면 다시 pool에 넣어둠
- 장점
- 스레드 생성에 필요한 병목 개선
- 애플리케이션별로 최대 스레드 수를 용이하게 제한 (상한선)
Communication between threads
- 공유 메모리를 사용하는 것이 효율적임
- 이미 공유가 허용된 메모리 공간이 있으므로, 서버-클라이언트 간 통신 등 불필요한 메시지 교환 방식의 IPC들을 과도하게 실행할 필요가 적음
- 다른 프로세스 내에 존재하는 스레드와의 통신은?
- 프로세스 간 IPC의 경우와 비슷한 성능
- 이러한 통신이 빈번하다면 프로그램 설계의 잘못
리눅스의 스레드
- 구현상 프로세스와 스레드가 크게 다르지 않음
- 둘 모두 task라고 부름
- PCB를 부르는 것이 task_struct로 구현되어 있음 → 공유되는 영역의 여부로 결정
- 프로세스
- 하나의 PCB → 하나의 PC, 하나의 Stack
- 스레드
- N개의 PCB → N개의 PC, N개의 Stack
스레드 API들
- 스레드 라이브러리
- POSIX Pthread: interoperability를 위한 표준
- IEEE 1003.1c: pthread_create()
- Windows thread API
- LINUX thread
- 버전 2.2에서 소개됨
- clone() 시스템 콜
- Java threads
스레드 생성 예시

- attr : 스레드가 가질 속성
- start_routine : 스레드가 동작할 함수의 포인터
- arg : 함수에 넘길 파라미터
스레드 종료 대기

- 스레드가 종료될 때까지 기다리는 API, valut_ptr은 리턴값을 받을 포인터

동기화
- 배경
- 프로세스는 concurrent하게 실행될 수 있음
- 두 프로세스가 동일한 (공유)데이터에 접근하고 연산을 수행할 수 있음
- concurrent 접근은 데이터 일관성을 해칠 수 있음
- 데이터 일관성을 유지하기 위해 프로세스들이 순차적으로 데이터에 접근하게 만드는 기법이 필요 → 동기화(synchronization)
동기화 문제
-
은행에 1000원의 Balance가 남아있을 때, 500원의 입금과 500원의 출금이 동시에 일어날 경우, 그 결과는?
-
두 프로세스 혹은 스레드

-
Balance 변수가 공유되고 concurrent하게 수행되는 경우

-
실행 순서에 따라 1500이 될 수도 있고, 결과를 예측할 수 없음
-
명령어의 실행 순서에 따라 결과가 달라지는 상황을 “race condition”이라고 함
Race condition
- 배경
- 여러 프로세스(or 스레드)가 존재
- 같은 데이터를 concurrent하게 접근, 조작
- 실행 결과가 접근 순서에 따라 달라짐
왜 동기화가 필요한가
- concurrent execution을 위해
- parallelism을 위해
- 여러 코어에 있는 여러 스레드가 동일한 변수에 동시에 접근할 때 어떻게 제어할 것인가
Critical Section

- 레이스 컨디션을 발생시키는 코드 내 부분
- 여러 프로세스나 스레드가 concurrently execute하면 안됨
Critical section을 처리하는 조건들
- 일반적으로 세 가지 조건을 만족해야 함
- Mutual exclusion(상호 배제)
- 프로세스 A가 critical section에 진입해 있다면, 다른 모든 프로세스는 진입할 수 없어야 함
- Progress
- Critical section에 진입하려는 프로세스들이 존재한다면, 한 프로세스는 반드시 critical section에 진입할 수 있어야 함
- Critical section 진입 단계에서 무한히 대기할 수 없음
- 무한히 대기하는 현상을 “데드락”이라고 부름
- Bounded waiting
- 각 프로세스는 언젠가 Critical section에 진입할 수 있어야 함 → Starvation 방지