Little Jay

[OS] Thread II (Multithread Process Model, LWP, KLT, ULT) 본문

Univ/Operating System(OS)

[OS] Thread II (Multithread Process Model, LWP, KLT, ULT)

Jay, Lee 2022. 8. 24. 14:33

Type of Parallelism on Multicore

멀티 코어에서 병렬성은 두 가지의 종류로 나뉘게 된다. 하나는 Data Parallelism이다. 이는 task들 사이에서 Data의 Subset을 병렬화 하는 것이다. 쉽게 예를 들자면 arr[100]이 있을 때 4core라고 한다면 25씩 Data를 나누면 쉽게 병렬처리를 할 수 있다. 다음으로는 Task Parallelism이다. 오히려 이 부분이 조금 더 직관적으로 이해하기가 쉽다. 이는 data를 분산해서 처리하는 것이 아니라 task를 여러 Processor에 할당하여 처리하는 것이다. 

Multi-Threaded Process Model

Thread도 결국 하나의 작업공간이기 때문에 Thread 당 1개의 Stack이 필요하다. 따라서 각각의 Thread는 Execution Stack을 User Level과 Kernel Level에서 하나씩 가지게 된다. 또한 Thread는 서로 다른 State를 가진다. Thread 역시 별도의 Scheduling Entity이기 때문에 별도의 State를 가져 스케줄링의 단위가 된다. 

위의 그림은 직관적으로 Thread가 포함된 Process를 나타내본 것이다. Data 영역은 하나의 PCB 내에서 공유된다. 따라서 Thread의 메타 정보를 관리하는 TCB는 하나의 PCB 내에서 여러개가 생성될 수 있다. 또한 TCB는 단순히 State, Pid 등만 관리하면 되기 때문에 TCB의 크기는 PCB보다 월등히 작다. 

따라서 Process Model이 이전에는 단순히 PCB, Stacks, User Address Space로 이루어져 있다면 이제는 쓰레드의 개수만큼 TCB와 Stack이 증가하게 된다. 

LWP (Light-Weight Process)

새로운 Process를 만드는 것보다는 새로운 쓰레드를 만드는 것이 훨씬 효율적이다. Fork하게 되면 Fork 당 새로운 Process Image가 생성이 되기 때문에 비용이 매우 높다. COW 방식을 통해서 어느 정도 효율적으로 새로운 Process를 만드는 것이 가능하지면 결국 COW 방식도 새로은 Process Image를 생성하는 것이기 때문에 그 비용은 매우 크다. 언젠가는 Copy가 만들어지기 때문이다. 반면 Thread를 만들때는 pthread_create만 해주게 된다면 Stack과 TCB가 Process 내에서 생성이 된다. 이는 매우 경제적이고 가볍다. 따라서 Thread를 LWP, 경량 프로세스라고도 부른다. 그러나 Solaris에서는 LWP가 다른 용어로 사용되기 때문에 Solaris 환경이 아니라면 쓰레드를 LWP라고 불러도 크게 상관이 없다. 

Multithreading Models

쓰레드를 구현하는 방식에 따라 크게 두 가지의 모델로 나눌 수 있다. User-Level-Thread, Kernel-Level-Thread, Combined(Hybrid) 모델인데 하이브리드 모델은 자연스럽게 두 가지 모델의 장점을 따와서 만든 역사적으로 타당한 방식이다. 그러면 먼저 KLT(Kernel Level Thread)를 살펴보자. 

KLT(Kernel Level Thread)

가장 정석적인 방법이라고 할 수 있는 방식이다. Kernel에서 쓰레드를 만들고 관리하기 때문에 Thread의 모든 정보를 Kernel이 다 알수 있다. 결국 pthread_create가 Kernel엣는 thread_create로 mapping되어 있는 것이다. Thread를 관리한다는 것은 wait, exit, run, ready state 등의 모든 상태를 다 알고 있는 것이다. 따라서 Kernel은 Thread의 정보를 보고 User의 Code와 Mapping시켜서 Task를 Run할 수 있게 하는 것이다. 쓰레드의 Creation, Termination까지 Kernel이 관리하기 때문에 Kernel을 Supervisor라고 이해해도 무관하다. Windows, Solaris, POSIX Thread(물론 얘는 ULT도 지원) OS 환경에서는 기본적은 KLT를 활용한다. 그러나 KLT도 마냥 좋을 수는 없는데, 결국 Kernel의 진입점은 Sys Call이다. 즉 Sys Call의 단점이 여기서도 드러나게 되는데, 이 경우 Mode Switch가 빈번하게 발생해 Overhead가 높아지면 수행시간도 높아진다는 단점을 가지고 있다. 

ULT(User Level Thread)

반면 ULT는 User Level에서 Library형태로 쓰레드를 구현하는 것이다. 이는 Kernel로의 진입이 전혀 필요없기에 Syscall이 아닌 쓰레드인 것처럼 Multiplexing을 지원하는 것이다. 당연히 Kernel을 거치지 않아 Mode Switch가 발생하지 않기에 KLT보다 속도적으로 우수하다. 그러나 ULT에서 고려해야 할 점은 안전성의 문제를 고민해봐야 한다는 것이다. Library 형태로 구현이 되어있기 때문에 만약 하나의 ULT가 block된다면 나머지에도 영향을 끼쳐 전부 block되는 참사가 일어날 수도 있다. 그렇기에 ULT들은 모두 Ready 상태로서 존재해야한다. ULT는 KLT보다 훨씬 빠르고, Kernel과 무관하게 코드를 돌릴 수 있고 어느 OS에서 구현만으로 실행가능하다는 장점이 있다. Kernel Independent하다는 장점이 있지만 문제는 Kernel이 이 ULT의 존재를 모른다는 것이다. User Level에서 만들어지고 Kernel과 무관하게 동작하기 때문에 Kernel이 이를 Control할 수 없으며 하나가 Block되면 나머지도 다 같이 Block된다는 단점을 지니고 있다. 이는 non-blocking mode의 함수들의 사용으로 어느정도 회피할 수 있지만 그것이 쉽지만은 않다. 

Combined ULT/KLT

Hybrid 기법으로서 안전성과 속도 두 마리의 토끼를 잡으려는 노력에 의해 등장했다. 역사적으로 이러한 노력은 항상 있었다. 이 기법은 먼저 ULT로 수행이 되다가 Thread가 block상태에 들어가게 되면 KLT로 동작하게 된다. Solaris가 이러한 구조를 지니고 있다. 

Comments