운영체제(OS)

Operating Systems 4. Threads & Concurrency

아뵹젼 2021. 12. 4.

Multithreaded Architecture

- 프로세스 생성은 heavy-weight, 쓰레드 생성은 light-weight 하다.

- 단일 응용 프로그램이 여러 개의 작업을 동시에 실행할 필요가 있을 때 thread 를 사용한다.

  • word processor: 화면 출력 + 키보드 입력 + 스펠링 검사
  • web browser: 화면출력 + 네트워크에서 데이터 수신 
  • web server: 여러 개(수천 개도 가능)의 client의 요청 처리
  • OS kernel: 장치 관리, 인터럽트 처리 등의 여러 작업

multithreaded server architecture

하나의 웹 서버 프로세스가 외부로부터 요청을 받는 하나의 다수의 클라이언트를 담당하는 thread 들로 이루어진다.

-> 클라이언트로부터 서비스 요청이 들어오면 대기하고 있던 쓰레드가 요청을 받는다.

-> 요청을 받아서 이를 처리할 수 있는 서비스 thread 를 생성하고 요청에 대한 처리를 넘긴다.

-> 서버는 또 다른 클라이언트 요청을 반복해서 기다린다.

 

 

 

Benefits of Multithreading

- Responsiveness : 응용프로그램의 일부가 block 이 되거나, 긴 작업을 수행하더라도 프로그램의 다른부분의 수행이 계속 되도록 허용 -> 응답성 증가

- Resource Sharing : thread 끼리 메모리와 자원을 공유한다.

- Economy : thread 생성은 process 생성보다 비용이 적게 든다.

- Scalability : multithread 는 각 thread 가 다른 cpu 에서 병렬로 수행될 수 있다.

 

 

 

Multicore Programming

multicore 또는 multiprocessor system 은 다음과 같은 것들을 프로그래머들에게 요구한다.

- Task dividing

- Load balancing

- Data splitting

- Minimizing data dependency

- Testing and debugging

 

 

- Parallelism 

시스템이 여러 개의 task 를 동시에 병렬 실행시킬 수 있다.

멀티 코어 시스템에서의 병렬

  • data parallelism : 전체 data 집합을 다수의 부분집합으로 나누고, 이를 다수의 core 에 분배해서 코어가 동일한 연산을 각각 실행하는 경우
  • task parallelism : 각 thread 가 서로 다른 작업을 한다.

 

- Concurrency

여러 개의 task 가 모두 진행한다. (실제로 동시에 실행되는 것이 아니라 cpu burst 를 나눠서 수행)

단일 코어 시스템에서의 병행

 

 

Amdahl's Law

순차실행을 해야하는 구성요소와, 병렬실행을 해야하는 구성요소를 동시에 가지는 프로그램이 있을 때, 이것을 멀티코어에서 실행했을 때 얼마나 성능 향상이 되느냐에 대한 공식이다.

S : 순차 실행 요소 비율

1-S : 병렬 실행 요소 비율

N : 프로세서 개수

예) 이 프로그램이 75%의 병렬실행이 가능할 때, S = 0.25 이다.

코어가 1개일 때는 speedup <= 1

코어가 2개일 때는 1.6 배의 성능향상 효과를 얻는다.

-> 코어갯수를 늘리면 성능이 향상되지만, 물리적인 한계가 있다.

 

 

 

User Threads and Kernel Threads

- User threads : 커널의 지원 없이 사용자 수준(커널 위) 에서 thread library 에 의해서 지원된다.

- thread library 는 다음과 같다.

  • POSIX Pthreads
  • Windows threads
  • Java threads

- Kernel threads : OS 커널에서 직접 지원되고 관리된다.

예) Windows, Solaris, Linux, Mac OS X

 

- 사용자 thread 와 커널 thread 간에는 다음과 같은 연관 관계가 존재한다.

  • Many to One
  • One to One
  • Many to Many

 

 

Many-to-One Model

- 다수의 user-level-thread 가 한 개의 kernel thread 에 연관된다.

- thread 스케쥴링과 동기화가 사용자 공간의 thread library 에서 수행된다.

- 장점 : context switching 과 동기화 overhead 가 작아서 효율적이다.

- 단점 : 하나의 thread 가 blocking system call 을 호출하여 block 되면 전체 process 가 block 된다.

-> 커널이 user level thread 의 존재를 알지 못하기 때문이다. 따라서 multiprocessor 시스템에서 thread 들의 병렬 실행이 불가능하다.

 

One-to-One Model

- 각 user-level thread 가 한 개의 kernel thread 에 연관되는 것으로, 요즘 대부분 사용되는 기법이다.

- user-level thread 를 하나 만들면 그에 대응하는 kernel thread 가 생성된다.

- 장점 : 더 많은 병행 실행, 병렬 실행이 가능하다. 한 thread 가 blocking system call 을 호출하여 block 되면 커널은 같은 process 의 다른 thread 로 스케줄링된다.

- 단점 : thread 생성 및 context overhead 가 발생한다. 또한, 커널에서 thread context switching 이 이루어지고, 많은 수의 커널 thread 가 생성시 시스템에 부담이 될 수 있다.

 

 

Many-to-Many Model

- 다수의 user-level thread 가 다수의 kernel thread 에 연관되는 것이다.

- 이때 kernel thread 의 수는 user-level thread 의 수보다 작다.

- 운영체제는 충분한 수의 커널 스레드를 생성하고, 원하는 수 만큼의 사용자 스레드가 생성된 후 이들에 대응하는 커널 스레드들이 다중 처리기에서 병렬로 수행될 수 있다.

-> 다대일과 일대일의 장점을 취한 접근법이다.

 

 

Two-level Model

many-to-many 모델의 변형모델로, 일부 user-level thread 에 대해서는 one-to-one 연관을 허용한다.

lightweight process 가 user thread 와 kernel thread 간의 중개 역할을 수행한다.

 

 

Thread Library : Pthreads

  • pthread_create() - thread 생성
  • pthread_join() – thread 종료를 기다림
  • pthread_exit() – thread 종료
  • pthread_attr_init() – thread attribute를 default 값으로 초기화

 

 

Java Threads

- Thread 생성 방법

  • Extending Thread class (파생 클래스 생성)
  • Implementing the Runnable interface (인터페이스 구현)

 

Thread Pools

- multithreaded server 에서의 잠재적 문제점

  • thread 생성 오버헤드 -> 요청마다 thead 를 생성하는 데 시간이 소요된다.
  • thread 수 증가에 따른 자원 고갈 가능성 -> thread 수에 제한이 필요하다.

-> 해결책 : Thread Pool

- 프로세스를 시작할 때에 일정한 수의 thread 들을 thread pool 에 미리 생성하여 대기한다.

- 요청을 받을 때마다 pool 에 있는 한 thread 를 깨워서 사용한다.

 

 

OpenMP

- 공유 메모리 환경의 병렬 프로그램을 지원한다.

- openmp 에서 병렬로 실행가능한 부분을 parallel region 이라 한다.

 

 

GCD (Grand Central Dispatch)

- 병렬 섹션을 구분하여 threading 관리를 지원한다. 

- 블록 표시 ^{ }

ˆ{ printf("I am a block"); }

-> 이 블록을 dispatch queue 에 넣어 나중에 실행되도록 스케쥴한다. 이 큐에서 블록을 하나씩 꺼내서 해당 블록을 thread pool 에서 꺼낸 가용 thread 에 핟당하여 실행되도록 한다.

 

- 두 가지 유형의 dispatch queue

  • serial : FIFO
  • concurrent : FIFO 이지만, 한 번에 여러개를 꺼낼 수 있고 그렇다면 동시에 실행이 가능하다.

 

 

Thread Related Issues

fork(), exec() 시스템 호출

exec() -> 기존 스레드들을 모두 무시하고, 새로운 프로그램으로 대체된다.

  • process 의 모든 thread 들을 복제 : fork() - no exec 의 경우에 사용 -> 생성된 프로세스는 multi-threaded
  • 해당 thread 만 복제 : fork() - exec 의 경우에 사용 -> 생성된 프로세스는 single threaded

 

 

시그널 처리

- 시그널은 프로세스에게 어떤 사건이 일어났음을 알리기 위해 사용된다.

  • Synchronous signal : 어떤 사건을 처리하기 위해 발생한다.
  • Asynchronous signal : 임의 시점에 발생한다.

- 모든 시그널은 둘 중 하나의 처리기(handler) 에 의해 처리된다.

  • 디폴트 시그널 처리기 : 시그널마다 기본적인 처리 방법을 정의한다.
  • 사용자 정의 시그널 처리기 : 사용자가 시그널 처리 방법을 정의할 수 있다.

 

Thread Cancellation

  • 비동기(asynchronous) 취소: 즉시 target thread를 취소
  • 지연(deferred) 취소 : 주기적으로 점검하여 target thread를 취소

- 실제로 스레드 취소는 thread 의 상태에 의존한다.

-> cancellation point 에 도달했을 때만 취소가 일어날 수 있다.

 

 

Thread-Local Storage

- thread 자신 만이 접근 가능한 저장공간으로, 특정 thread 에게 고유하게 할당된다.

 

댓글