[운영체제] 04-5. Multithreading
출처 서울대학교 홍성수 교수님, [K-MOOC] 운영체제의 기초, 4주차 Processes and Threads
스레드의 필요성
하나의 프로세스는 context와 thread로 이루어져 있다.
병렬적인 연산을 수행하기 위해 process를 계속 생성하는 것은 오버헤드가 크다. 그런데 process는 무거운 entity이다. 각각 코드 세그먼트, 데이터 세그먼트...등등을 가지고 있기 때문이다. 그래서 사람들은 프로세스의 context와 thread를 분리해야겠다는 생각을 하게 되었다. context는 모두 하드웨어 리소스를 말한다. 이 context는 하나만 두고 thread를 여러 개를 두자는 생각을 하게 된 것이다.
멀티 스레딩 모델
스레드가 이제 스케줄링과 대스패치의 단위가 되었다.
- 스레드는 execution state (running, ready, stopped)를 가진다.
- 스레드의 상태 정보는 TCB에 저장한다.
- 스레드는 독자적인 런타임 스택을 가진다.
- 프로세스의 리소스를 스레드들이 공유한다.
스레드의 State Transition
- running thread: 실행 중인 스레드
- ready thread: 실행할 준비가 된 스레드
- blocked: 실행할 준비가 되지 않은 스레도
스레드에는 suspended 상태가 존재하지 않는다. suspended 상태란 세컨더리 스토리지, 다시 말해서 디스크로 스왑 아웃된 프로세스의 상태를 말한다. 스왑아웃은 프로세스 단위로 일어나기 때문에 스레드는 suspended 상태를 가지지 않는다. 한편 어떤 프로세스가 종료되면 그 리소스를 공유하던 스레드도 모두 종료되어야 한다.
멀티스레딩의 장점
- concurrency programming이 쉬워졌다. 병렬 수행시키려는 일이 여러 개 있을 때 멀티 프로세서에게 태스크를 나눠서 수행하게 하려면 잘 할당하고 로드밸런싱을 해야 하고 어떤 태스크들이 어떤 프로세서 코어에서 실행되어야 한다는 것도 지정해줘야 한다. 그런데 스레드들은 자연스럽게 하나의 프로세스 안에서 관리되기 때문에 병렬 프로그래밍이 효율적이게 되었다.
- 그리고 리소스 공유도 쉬워졌다. IPC를 지원하기 좋아졌다. 스레드들은 이미 공유 메모리를 가지고 있다.
- 프로세서 생성과 비교했을 때 구현이 쉬워진다. 스레드는 스택만 하나 할당해주면 생성할 수 있다.
- 제어 시스템, 상호작용 시스템에서도 유리하다. 반응성 좋은 애플리케이션을 제공할 수 있어서 사용자 경험이 좋아진다.
Concurrent Server
안드로이드 앱들이 concurrent server의 구조를 가지고 있다. Dispatcher Thread는 터치에 대한 접수만 하고 작업은 Worker Thread에게 부여한다.
POSIX (Portable Operationg System Interface)의 멀티스레드 표준 API: pthreads
멀티스레드 시스템에서 진입점은 어디일까? 함수가 스레드를 구성하는 단위가 된다. 일대일 대응이 된다. C 언어로 프로그래밍할 때 하나의 스레드에서 시작한다. 나머지 스레드는 코드에서 만들어줘야 한다. 그 코드를 위한 API가 있다.
예를 들어, 프로세스를 생성하려면 pthread_create() 함수를 호출하고 파라미터로 함수 포인터를 전달하면 된다.
스레드의 라이프 사이클
처음 프로세스는 싱글 스레드에서 시작한다. 이 스레드가 메인 스레드가 된다. 메인 스레드는 자기가 만든 자식 스레드들이 모두 종료할 때까지 기다린다. 이것이 pthread_join() 함수가 하는 일이다.
멀티스레드 코드
pthread API를 3가지 방식으로 구현할 수 있다.
1. 유저 레벨
스레드는 100% 유저 레벨 entity가 된다. OS는 이러한 스레드가 있다는 것을 전혀 모른다. OS 입장에서는 싱글 스레드이다.
- 장점: 모드 전환을 위한 인터럽트가 필요없기 때문에 오버헤드가 낮다.
- 단점: blocking system call을 한 스레드는 그 스레드만 블록이 되어야 하는데 프로세스 전체가 블록된다. 스케줄링을 할 때 프로세스의 어떤 스레드를 재실행할지 판단하는 것이 불가능한다. 선점 스케줄링이 불가능하다. 비선점 스케줄링만 가능하다. 동기적인 연산에 적합하다.
2. 커널 함수
pthread_create()가 유저 레벨 라이브러리가 아니라 시스템 콜로 구현되는 것이다. 스케줄링 단위가 스레드가 된다.
- 장점: 특정 스레드가 block되면 전체 프로세스가 아니라 그 스레드만 block하면 된다.
- 단점: 시스템 콜 핸들링 비용이 비싸다는 것이다. 운영체제의 코드에 접근해서 이 코드를 고쳐야 한다.
3. 두 개의 중간 형태
유저 프로그램에서 스레드를 생성하는 것이 라이브러리로 구성된다. 커널 레벨 스레드를 만드는 API도 제공한다. 커널 레벨 스레드와 코드의 유저 레벨 스레드를 연결시키는 시스템 콜도 제공한다. Light Weight Process (LWP)라는 엔티티를 이용하여 커널과 유저 스레드를 접착시킨다. 보편적으로 사용되는 모델은 아니다.