Why Threads?
- Process는 많은 정보를 포함하기 때문에 hevey-weight하다.
- Address space : code, data pages
- Hardware execution state(Context) : PCB 내부의 PC, SP, registers..
- OS resources, accounting info. : open files..
- 새로운 process를 create(fork)하는 것은 시간이 많이 걸리고 costly하다.
- 여러 프로세스는 각자 독립적인 영역을 가지기 때문에 Inter-Process Communication하는 것이 Costly하다.
Cooperating Processes
- web server같은 경우는 자기 자신의 clone을 만들어서 동시에 작업을 하고 multiprocessor 환경에서 병행적인 프로그램으로 활용한다.
- data를 share하기 위한 같은 address space를 필요로 한다 – OS support shared memory
- 이러한 여러 process들의 scheduling은 OS가 한다.
- 이러한 scheduling을 자원, 시간 측면에서 비효율적이다.
Idea


- fork해서 사용하는 process들은 대부분 code, data, heap, privilege, resources는 동일하다.
- 각 process가 수행상태(hardware execution state)만 다르다.
- 공통적으로 사용하는 것은 생성하지 말고 수행상태 정보만 관리하면 되는 것이다.
- 수행상태를 분리 → Thread / lightweight process(LWP)
- PC, SP, registers, stack, context..
Threads
- program에서 실행되는 instruction들의 sequence를 의미한다.
- PC, register, stack 등의 hardware context로 구성되어 있다.
- thread끼리는 instruction과 data(global variable)를 공유한다.
- global variable이 변화가 생긴다면 다른 thread들도 알 수 있다.
- thread들은 대부분의 OS state를 공유한다.
Processes vs Threads
- process는 덩치가 크고 비용, 시간이 많이 들지만 thread는 덩치가 작고 비용, 시간이 적게 든다.
- process는 여러개의 threads를 가질 수 있다.
- thread끼리 data를 공유하는 것은 cheap하다.
- process가 아닌 thread가 scheduling의 단위가 된다. (kernel-level threading인 경우)
- process는 static하지만 thread는 dynamic하다.
Multi-threading Benefit
- concurrency하게 이용하기 쉽다.
- Improve program structure → Modular
- Throughput
- computation과 I/O를 overlap할 수 있다.
- Responsiveness : 응답성이 좋아진다.
- 자원을 쉽게 공유할 수 있다.
- 경제적이다.
- multiprocessor(core) architectures 성능 향상
Process Address Space – Multi thread

- 각 T1, T2, T3는 code를 공유하며 각자의 HES를 가진다.
Threads Interface
Pthread

- POSIX에서 제안한 API
- Implementation이 아닌 specification이다. → Linux, windows 사용 방식이 다를 수 있다.
- #include <pthread.h>를 이용하여 라이브러리를 사용한다.
- gcc library libpthread.a : -lpthread
- pthread_join : thread의 wait과 같은 것
- *attr : 보통 NULL
pthread_join

Mutexes & Condition variables


- wait을 하기 위해서는 mutex를 항상 지정해줘야 한다.
- mattr는 사용하지 않으면 NULL이 가능하지만 mutex는 object를 줘야하고 NULL은 안된다.
Threading Issues
fork() and exec()
- thread가 fork call을 한 경우
- Pthread : fork를 호출한 thread 1개만 복제를 한다.
- Unix International standard
- fork() : 모든 thread를 복제
- fork1() : fork를 호출한 thread만 복제
- exec() : 기존 thread를 모두 제거하고 다시 새로운 process가 되어 1개의 single thread로 된다.
Thread cancellation
1) Asynchronous cancellation
- thread를 즉시 종료시킨다.
- 좋은 방법은 아님
2) Deferred cancellation (default)
- thread를 즉시 종료하지 않고 safe한 곳까지 대기했다가 종료시킨다.
- cancellation point : safety zone
- pthread_testcancel() : cancellation point에서 함수 호출을 하면 된다.
- cancel이 일어나면 cleanup_handler가 작동하여 처리를 한다.(자원 회수 등..)
3) cancellation func

- int pthread_setcancelstate(int state, int *oldstate);
- int pthread_canceltype(int type, int *oldtype);
- oldstate, oldtype을 지정하지 않는다면 NULL을 이용한다.
Signal handling
- 만일 signal이 전달되었을 때 누구한테 전달을 할 것인가?
1) 1개의 대표 thread를 정하고 모든 signal을 해당 thread에 전달 : solaris 2
2) 모든 thread에 signal을 전달
3) signal mask block을 통해 per-thread 방식
Thread-Local Storage
- TLS : thread에서 각 함수에서 만들어진 data를 개별로 저장하는 local 공간
- thread pool : NULL thread를 만들어서 모아둔 공간
- NULL thread : OS가 모든 자원을 다 제공했지만 함수가 주어지지 않은 빈 thread
- function의 local variable과는 다른 개념으로, local variable은 function이 끝나면 사라진다. 하지만 Thread-local storage에 저장된 data는 thread가 끝날 때까지 사라지지 않는다.
Using libraries
- errno : 각 thread는 각자의 errornum을 가지고 있다.
- Multi thread-safe(MT-safe, reentrant function) : global variable을 사용하지 않고 local variable만 이용해서 안전하게 사용을 하는 방식
- 만일 global variable을 사용해야한다면 critical section으로 지정해서 lock을 하며 MT-safe 방식을 지켜야한다.
Easy sharing but error-prone
- thread에서는 공유가 쉬워졌지만 문제가 발생할 확률이 높아졌다.
- 따라서 공유 변수를 최소화해야한다.
Kernel/User-level Threads
- thread create/manage를 OS가 할 것인가 User가 할 것인가?
- OS : system call을 이용하여 생성 및 관리
- user : library를 이용하여 생성 및 관리
- 여러 thread가 address space를 share하기 때문에 OS의 도움 없이 user가 관리해도 된다.
Kernel-level Threads

- 모든 thread 관리를 OS가 한다.
- thread 관련된 operation이 kernel에 구현되어 있다.
- OS가 kernel에 있는 모든 thread를 scheduling을 한다. - scheduling의 최소 unit이 thread
- thread operation이 모두 system call이기 때문에 protection boundary를 계속 지나기 때문에 너무 비싼 operation이 계속된다.
- kernel space를 OS가 관리하는데, thread가 너무 많아지면 문제가 될 수 있다.
User-level Threads

- thread를 조금 더 싸고 빠르게 만들기 위한 방식
- portable하게 사용할 수 있다. 즉, 다른 system으로 library의 형태로 쉽게 porting할 수 있다.
- 각 thread의 PC, register, stack을 user space에서 할당한다.
- Run-time system : library라고 생각하면 된다.
- kernel-level thread보다 최대 100배까지 빠르다.
- user level에서 관리를 하다 보니 OS는 invisible하다. 예를 들어 1개의 thread가 I/O를 했어도 1개의 process로 인식해서 모든 thread가 block을 당할 수 있다.
- 즉, OS가 잘못된 판단을 하는 것이 쉽다.
Implementing User-level Thread
Thread context switch
- 현재 library에 관리하는 thread의 stack에 machine state를 저장하고 다음 thread의 context를 가져오고 하는 방식
- 모든 것이 assembly language로 이루어져 있다.
Thread scheduling
- library 내에 있는 thread scheduler가 thread들을 queue를 이용하여 관리한다.
- OS가 scheduling을 하듯이 적용한다.
Non-preemptive scheduling
- thread끼리 cooperative하게 작동을 하기 위해 사용한다.
- alarm()과 같은 함수를 이용하여 user level에서 적용할 수 있다.
Threading Models
Many-to-One(N:1)

- kernel thread 1개에 여러개의 user thread가 있는 방식
One-to-One(1:1)

- 일대일 대응 방식으로, 주로 kernel level thread에 이용된다.
Many-to-Many(M:N)

- user, kernel도 여러개인 형태
- kernel이 돌아가면서 user과 대응하게 되고, 상황에 따라 kernel thread가 달라지는 형태
Linux Thread
- Linux에서 기본 unit은 “task”로, process와 thread 모두 task로 불린다.
- clone() system call을 이용하여 multi threading을 지원한다.
- 별도의 task가 생성되며 이전 task와 어떤 부분을 공유할지 선택적으로 지정할 수 있다. - clone()
- POSIX와 Linux는 공유관계에 대한 접근 방법이 다른 것이다.
- NPTL 모델이 지원되고 있다.(1:1 model)
Java Thread
- Runnable interface를 이용하여 class를 정의하고 thread class와 binding 등을 진행한다.
- run()함수는 thread의 main function을 실행시키는 함수이다.
Creating / Destroying Mutexes
initialization
- mutex는 초기에 unlocked 상태이다.
1) static
- pthread_mutex_t mymutex = THREAD_MUTEX_INITIALIZER
2) dynamic
- pthread_mutex_init(attr)
pthread_mutexattr_t : attr
- NULL : default
- protocol : inversion 문제 해결하기 위해 사용
- prioceiling : priority ceiling 방법을 사용
- process-shared : 서로 다른 process끼리 mutex를 사용할 때
Mutex Lock/Unlock
- pthread_mutex_lock(mutex) : blocking operation
- pthread_mutex_trylock(mutex) : non-blocking operation
- pthread_mutex_unlock(mutex) : mutex 반납
- error가 생기는 경우
- 다른 thread가 이미 mutex를 가진 경우
- mutex가 이미 unlock인 경우
synchronous call / Asynchronous call : blocking / non-blocking과 같은 말
Advisory Mutex & Mandatory Mutex
Advisory lock
- synchronous의 책임이 programmer에게 있는 lock
Mandatory Mutex
- OS가 지원하는 lock
- file에 lock을 하는 경우(permission bit 이용)
- 모든 접근에 대해 OS가 control을 해야하기 때문에 성능이 떨어질 수 있다.
Condition Variables
initialization
1) static
- pthread_cond_t myconvar = PTHREAD_COND_INITIALIZER;
2) dynamic
- pthread_cond_init (condition, attr);
Waiting / Signal on CVs
- cond_wait시 mutex를 지정해야 제대로 작동한다.
- cond_signal은 stack되지 않는다.