이 중에서 thread가 공유할 수있는 게 뭐가 있을까? 이미지에는 user stack 빼고 가능하다. stack이 불가능한 이유는, 하나하나 현재 상태라서 그렇다. context 중에서는 kernel context를 공유한다.

만약 프로세스가 thread를 여러개 들고 있다면 여러개의 thread는 자신만의 stack, stackpointer, pc, data register, tid를 가지고 나머지는 전부 공유한다.(code, data, kernel context, shared library, heap) 즉 thread가 고유하게 가지고 있는 것 = user stack + registers + stackpointer + program counter
Images: 실체
address space가 다른 것을 확인할 수 있다.
process hierarchy와 thread도 많이 다르다. tree hierarchy도 일단 없다.
실제 코드로도 다르다. process : fork/thread: thread_fork이다.
이제 thread model에서 process는 그저 thread를 실행하는 container이다. 즉, thread는 단일 프로세스에 귀속되며, 프로세스에는 여러개의 thread가 있을 수 있다는 것이다.
thread, process의 공통점은 각각 scheduled된다는 점과, 논리적인 흐름이 있다는 것이다.
차이점은 thread는 code, data를 공유하지만 process는 그렇지 않고, thread는 switching, terminating이 덜 비싸다는 점이다.

위 그림에서, 커널은 동그라미 부분만 안다. 즉 User-level thread인 원 안의 네모가 마치 조그만 커널같은 느낌이다.
이들은 Many-to-one, 즉 많은 User-level thread가 single kernel thread(=process)에 map되는 것이다. 이들은 아주 가볍고, 싸고, 빠른 모델이다. 그러나 문제는 IO 등에 의해 thread 1번이 block되면 2, 3번도 block 된다. 커널이 볼 때는 “스레드 A, B, C가 돌아가는 중”이 아니라 “프로세스(또는 하나의 Kernel level thread) 하나가 system call을 수행하다가 블록됐다”로 인식한다. 그래서 이 프로세스는 모드스위치되고, 다른 process가 기회를 가지게 된다.
따라서 시스템 콜이 블록 상태가 되면 그 ‘프로세스/ULT 전부’가 스케줄링에서 빠지고, 유저 레벨 스레드 A, B, C 중 어느 것도 CPU를 얻을 수 없게 된다. 유저 레벨 스레드는 커널 차원에서 개별적으로 관리되지 않기 때문에, 하나가 블록되면 프로세스 자체가 블록되어 그 안의 모든 유저 레벨 스레드가 함께 멈출 수밖에 없다는 것이 핵심이다. “그냥 다른 스레드로 컨텍스트 스위칭”은 유저 레벨에서 할 수 있으나, 이미 커널에 의해 프로세스가 블록되었으므로 실질적인 스케줄링 자체가 불가능하다. Multiprocessor에서 performance가 늘지 않는다. 전혀 멀티쓰레드의 이점이 없다.
Q1. klt방식으로 구현해도 user level thread를 하나씩 만드는 건가?
1:1 : 매번 새 커널 스레드를 만들어서 1:1 연결
N:1 : 모든 유저 스레드가 한 커널 스레드를 공유
M:N : 여러 유저 스레드가 여러 커널 스레드를 공유
응용 입장에선 KLT인지 ULT인지 신경 안 씀. 그냥 스레드 만들었음.
구현 시점
이 호출이 어떻게 처리되는지에 따라:
libpthread가 clone() 시스템 콜을 부르면 → KLT (1:1 모델)
libpthread가 자체 자료구조만 만들면 → ULT (N:1 모델)
pthread_create() → 라이브러리가 유저 공간에 TCB 생성
Pthread: POSIX API
thread는 성능, 자원에 대한 고민이 많아짐.
밑에는 예시 코드이다.
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <pthread.h>
/* 최대 쓰레드 갯수 */
#define MAX_THREAD_NUM 5
/* thread main function 선언 */
void *thread_function(void *arg);
int main()
{
int res;
pthread_t thread_handle[ MAX_THREAD_NUM ];
void *thread_result;
int i;
/* MAX_THREAD_NUM 만큼 pthread 를 생성한다. */
for ( i = 0 ; i < MAX_THREAD_NUM ; i++ ) {
/* pthread 를 생성하여 생성된 pthread 에 대한 handle 을
tread_handle 배열에 저장한다.
pthread 의 main function 은 세번째 인자인 thread_function 이
되며, 인자로 몇번째로 생성된 pthread 인지를 나타내는 i 값을
넘긴다.
*/
res = pthread_create( &(thread_handle[i]),
NULL,
thread_function,
(void *)i );
/* 결과값을 확인하여 Error 가 발생하였는지 확인한다. */
if ( res != 0 ) {
printf( "[Main] Thread Creation Fail.\n" );
exit(1);
}
}
/* 메세지 출력 */
printf( "[Main] Waiting for threads to finish...\n" );
/* MAX_THREAD_NUM 만큼 생성된 모든 pthread 가 종료되기를
기다린다 */
for ( i = 0 ; i < MAX_THREAD_NUM ; i++ ) {
/* 생성된 pthread 가 종료되기를 기다린다.
* pthread 내부에서 pthread_exit 가 호출되기 전까지 블럭된다.
* pthread_exit 의 인자를 thread_result 값으로 전달받는다.
*/
res = pthread_join( thread_handle[i], &thread_result );
/* 결과값을 확인하여 pthread 가 올바로 join 되었는지 확인한다. */
if ( res == 0 ) {
printf( "[Main] join thread(%d)\n", i );
}
else {
printf( "[Main] join thread(%d) failed\n", i );
}
}
/* 메세지 출력 */
printf( "[Main] All done\n" );
exit(1);
}
/* pthread 의 main function */
void *thread_function( void *arg )
{
/* 인자로 받은 값을 my_number 에 저장한다. */
int my_number = (int)arg;
int rand_num;
/* 몇번째 pthread 가 실행중인지 메세지를 출력한다. */
printf( "[thread(%d)] is running\n", my_number );
/* 1 부터 5 사이의 임의의 값을 생성한다. */
rand_num = 1+(int)(5.0*rand()/(RAND_MAX+1.0));
/* 생성된 임의의 값만큼 sleep 한다. */
sleep( rand_num );
/* sleep 이 끝났음을 메세지로 출력한다. */
printf( "[thread(%d)] job done\n", my_number );
/* thread 작업이 종료되었음을 알린다. */
pthread_exit( NULL );
}
linux는 프로세스, 쓰레드를 구별하지 않는다. 그렇다면 어떻게 구현하는가? Process와 Thread의 철학적인 차이를 구별해보면, 사실은 공유의 유무라고 볼 수 있다. fork랑 비슷한데, 그러면 인자를 넣어서 주는 fork처럼 구현해보면 되지 않을까?
Linux는 clone syscall을 통해서 thread를 구현한다. fork와 유사하지만 process의 복사본을 생성하지는 않고, address space of parent task(=process)만 공유한다. 다만, clone의 flag들로 parent 가 child와 얼마나 많이 공유하는지를 구별한다.
Linux의 자료구조인 PCB는 Task Structure로, process, thread를 전부 다 커버하는 내용이다. 되게 흥미로운 구현 방법인데, thread는 자료구조에 손댈 필요가 없어진 것이다..!!
Linux에서의 thread는 프로세스와 같아서, 공유하지 않으면 process, 공유하면 thread가 된다.
1-1: clone() syscall을 사용함.