ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 💻 [OS 오퍼레이팅시스템] #10 | Thread & Synchronization | 멀티쓰레딩, 프로세스와 쓰레드, ULT, KLT
    CS/OS 2022. 6. 12. 18:49

     

     


    💻 프로세스와 쓰레드

     - 프로세스 
    실행되고 있는 프로그램

    일련의 명령 및 관련 시스템 리소스 집합의 실행으로 특정 지어지는 실행 단위

     

     - 특성 
    1. 자원 소유권의 단위

    2. 실행(프로세스 관점) 단위 또는 스케쥴링(커널 관점) 단위

     

     - 쓰레드 
    한 프로세스는 단일 실행 순서를 갖는다.

    실행 단위는 thread라고 부른다. 또는 LWP(Light Weight Process)라고 하며, 자원 소유 단위는 여전히 프로세스(또는 task)라고 한다.

     

    * 프로세스 세분화 -> 실행 단위, 자원 소유 단위 -> '쓰레드'


    💻 멀티쓰레딩

     - 멀티쓰레딩 

    한 프로세스에는 한 개 또는 다수의 쓰레드가 존재한다.

    단일 프로세스 내에서 다수의 동시 실행 경로를 지원할 수 있는 OS의 능력이다.

    쓰레드는 같은 프로세스의 다른 쓰레드들과 같은 환경(resource)를 공유한다. (장점)

     

    * 최초의 하나 쓰레드 : 메인 쓰레드, 이후 추가 쓰레드 : 일반 쓰레드

     

     - Concurrency (동시성/병행성) 
    모든 작업이 계속 진행될 수 있도록 허용함으로써 다수의 작업을 지원하는 능력
    interleaving(끼어들기, like R.R)할 시 두 개 이상의 스레드가 실행되는 능력

     

     - Parallelism (병렬성)

    하나 이상의 작업들을 같은 시간에 동시 실행(simultaneously)

    두 개 이상의 쓰레드들을 overlapping(중복)해서 실행할 수 있는 능력

    싱글코어는 불가능

     

    차이점 (왼: 동시성, 오: 병렬성)

    * 왼쪽 동시성 그림을 보면 user-level에서 T1은 여전히 진행되는 것처럼 보인다. (illusion)
    * 순차실행 X, interleaving O
    * 동시성은 싱글코어에서도 가능하다.

    * 오른쪽 병렬성 그림을 보면 작업들이 한 타임에 병렬적으로 동시 실행된다.
    * 병렬성은 다중코어에서만 가능하다.
    -> 병렬성 없이 동시성만 가지는 것은 가능하다.

     

     - 역사 진화로 4가지 분류 가능

    1) MS/DOS : 한 프로세스당 단일 쓰레드

    2) UNIX : 멀티 프로세스, 단일 쓰레드

    3) JVM : 단일 프로세스, 멀티 쓰레드

    4) 현대 OS : 멀티 프로세스, 멀티 쓰레드

      MS/DOS older UNIX Java run-time environment NT, Solaris,
    and UNIX의 현대 버전들
    프로세스 단일 프로세스 멀티 프로세스 단일 프로세스 멀티 프로세스
    쓰레드 단일 쓰레드 단일 쓰레드 멀티 쓰레드 멀티 쓰레드

     


    💻 Motivation : 쓰레드가 나온 역사적인 요구사항

     - motivation 
    응용프로그램에 여러 개의 작업이 있을 때, 다른 작업들이 blocking 없이 계속 진행되어야 하는 요구사항

     

    e.g. busy한 웹 서버는 여러 개의 클라이언트들을 동시에 가진다. -> 웹서버는 다양한 concurrent한 요청들을 처리해야한다.

    1) 전통적인 단일 쓰레드 프로세스 : 한 번에 한 클라이언트에게만 서비스 해줄 수 있어서, 다른 클라이언트의 요청은 아주 오랫동안 앞선 서비스가 끝날 때 까지 대기할 수도 있었다. -> 대기 길어지면 블로킹되는 문제

    2) 멀티프로세싱 해결법 : fork(), 서버가 요청을 받으면 새로운 프로세스를 생성해서 요청을 서비스한다.

    3) 멀티쓰레딩 해결법 : 위 방법보다 좀 더 성능 up, 요청이 생기면 프로세스 생성이 아니라 쓰레드를 생성해서 요청을 서비스 하고, 서버는 다시 다른 요청이 오는지 확인하는 걸 재개한다.

     

    멀티쓰레딩 해결법

     

    웹서버 예시 - 3가지 방식


    💻 Benefits

    장점 4가지 기억

     - 1) Responsiveness 응답성 
    블락되거나 실행시간이 긴 작업이라도 프로그램이 계속 실행되도록 할 수 있다.

    I/O와 컴퓨터 계산적인(computation) 작업이 오버랩되어 일어날 수 있다.

     

    ex) RPC : 서로 다른 기계에서 실행될 수 있는 두 프로그램이 call/return절차와 semantics를 사용하여 상호 작용하는 시스템 SW기술(원격인데 아닌 것처럼 실행)

    - 싱글 쓰레드 : 서버의 회신이 올 때까지 프로그램이 대기해야 한다. -> 순차적으로 실행

    - 멀티 쓰레드 : 두 개의 응답에 대해 동시에 기다릴 수 있다.

     

     - 2) Economy of Time 경제성 

    생성시간 Fast (쓰레드 생성에서는 전체 PCB를 생성할 필요 X)

    프로세스 간 스위치도 Fast

     

    * 프로세스 생성 : PCB 생성 + Image 생성

    * 쓰레드 생성 : TLB 생성, 나머지 자원은 공유    -> 그래서 LWP라고 한다. Light Weight Process

     

    단일 쓰레드 멀티 프로세싱에 비해 Creation Cost 감소 

     

     - 3) Resouce sharing 자원 공유 
    프로세스들은 shared memory와 같은 기술을 통해서만 자원을 공유할 수 있다. ->프로그래머가 해주어야 함(int fd = shm_open(name, ..); 또한 독립적인 프로세스 간 통신은 보호 및 통신에 필요한 메커니즘을 제공하기 위해 커널의 개입이 필요하다.

    쓰레드들은 메모리에서 같은 데이터를 읽고 쓰는 것을 같이 할 수 있다. -> 동기화 문제 해결해줘야 함

     

    단일 쓰레드 멀티 프로세싱에 비해 Communication Cost 감소 

     

     - 4) 멀티코어 확장성 (Scalabilty) 
    (장점이자 사용하는 이유이며, 멀티프로세스 환경에서 멀티쓰레딩의 효과는 더 극대화된다.)

    원래 단일 쓰레드 프로세스에서는 얼마나 실행 가능하든지 간에 무조건 한 프로세서 안에서만 실행된다.

     

    한 프로세스가 다양한 CPU에서 실행될 수 있다.(parallelism)

    같은 응용프로그램이 다양한 코어에서 실행될 수 있다. 

    프로세스가 task단위로 쓰레드로 쪼개지고, 각 쓰레드가 각 CPU에 매핑되어 병렬적으로 실행된다 -> 성능 up, very fast


    💻 멀티코어에서 멀티쓰레딩

     - 프로세서 수 많아질수록 SpeedUp 증가 

    SpeedUp = 단일 프로세서에서 실행 시간 / N개의 parallel한 다중 프로세서에서 실행 시간

    암달스 법칙 : 병렬 불가능한 영역이 존재한다.(순차실행 영역) -> 이 영역을 S라고 하면

    -> SpeedUp ≤ 1 / { S+(1-S)/N) }   만약 S(병렬 불가능 영역)이 0이라면 위의 공식 처럼 1/N이 된다.

     

     - 리스케쥴링에 따른 Speedup 감소 

    16개의 프로세서 시스템에서 행렬 곱셈과 FFT(fast Fourier transform)계산에서의 SpeedUp

    쓰레드 많아지면 스위치 비용이 발생해서 speedup이 오히려 감소한다. maximum이 존재한다.

    -> 쓸데없이 조각(쓰레드)내지 말고 for문 돌리는게 더 효율적일 수도 있다.

     

     - 멀티코어에서의 멀티쓰레딩 프로그래밍 Challenges 

    1) Identifyint tasks : task들은 서로 독립적이여서 병렬적으로 실행할 수 있다.

    2) Balance : 프로그래머는 task들이 같은 크기의 일을 수행해야 함을 보장해야한다.

    3) Data dependency : task들이 접근하는 데이터(공유 자원)에 대해서 동기화가 필요하다.

     

     - 멀티코어의 병렬 종류 
    - Data parallelism : 같은 데이터의 부분집합을 여러 core에 분산하고 각 코어에서 같은 연산을 하도록 한다.

    - Task parallelism : 데이터 분산이 아닌 task(thread)를 여러 core에 분산한다. 다양한 쓰레드들이 같은 데이터를 연산할 수도 있다.


    💻 멀티쓰레드 프로세스 모델

     - execution stack (User/Kernel) 

    스택을 각 쓰레드별로 나눈다 -> 각 쓰레드마다 자신만의 실행 스택을 가진다.

    같은 말 : call stack, execution stack, run time stack

     

     - thread-related control block 
    각 쓰레드마다 TCB를 나누어야 한다. (레지스터값 + thread-related 스케쥴링 + 상태 정보)

    TCB와 같은 배타적인 정보들은 쓰레드들 간에 공유를 하지 않는다. 각 쓰레드마다 별도로 가지는 정보들이다.

     

     

     - KLT (Kernel-level thread) 
    (정석적, 일반적이다) 쓰레드 관리는 모두 커널에 의해서 실행된다. -> application level에는 쓰레드 관리 코드가 X

    1 쓰레드 당 1 커널 scheduled entity

    OS가 관리하는 쓰레드를 kernel-level 쓰레드라고 한다.

    단점 : 시스템 콜을 요구한다. 커널의 모드 스위치를 필요로 한다. -> overhead 발생, 느림 (근데 프로세스 fork 생성 보다는 빠름)

    E.g. Windows

     

    * 커널 쓰레드 간 스위칭 : 커널 진입 -> 커널이 실행 중인 쓰레드 프로세서의 정보 TCB에 저장 -> 실행할 새로운 쓰레드 선택 -> 커널이 새로운 쓰레드의 레지스터를 불러오고 TCB에 저장된 PC값으로 이동

     

     - ULT (User-level thread) 
    모든 쓰레드 관리는 application에 의해 실행된다.

    쓰레드 라이브러리를 통해 멀티스레드 될 수 있다. 쓰레드 라이브러리는 쓰레드 생성과 삭제, 쓰레드 간 데이터 교환, 쓰케쥴링, 쓰레드 context 저장과 복구를 포함한다

    procedure 콜에 의해 제어가 전달된다.

    장점 단점
    커널 모드를 필요로 하지 않음
    어떤 OS에서든 실행 가능
    쓰레드 스케쥴링이 application별로 다를 수 있어서 기본 OS 스케쥴러 방해 없이 응용프로그램에 맞게 조정 가능
    커널이 쓰레드의 존재를 알지 못함
    parallelism 불가능(커널이 한번에 한 커널 쓰레드만 스케쥴 할 수 있어서)
    시스템 콜 실행하게되면 프로세스 내 모든 쓰레드들이 블락될 수 있음
    -> 해결법 : blocking 시스템 콜을 non-blocking 시스템 콜로 변환

     

    * 쓰레드 간 스위칭 : 라이브러리가 실행중인 쓰레드의 프로세서 context를 TCB(in library)에 저장 -> 라이브러리가 새로 실행될 쓰레드 선택 -> 라이브러리가 새로운 쓰레드의 정보 복구, TCB에 저장된 PC값으로 이동       (이 과정에서 커널은 전혀 개입 X)

     

    KLT ULT
    OS와 통합됨(안정적)
    생성, 조종, 동기화가 느리다
    OS와 비통합적
    생성, 조종, 동기화가 빠르다.

     

     - Combined 
    안전성 + 빠른 속도

    쓰레드 생성이 user 공간에서 실행된다.

    한 응용프로그램 내의 다중 ULT가 몇몇의 KLT에게 매핑된다.

    프로그래머는 KLT의 수를 조정할 수 있다.

    ex) Solaris

     

    <멀티쓰레딩 모델> 왼쪽부터 순서대로 ULT, KLT, Combined

     


    💻 쓰레드 API

     - Thread API 
    - Linux/Unix : POSIX Pthreads

    - Windows: Win32 Threads

    - JVM: Java Threads

    main()이 시작되면 메인쓰레드 실행됨. 쓰레드는 { } 블럭이 끝나면 자동종료. pthread_exit()는 명시적 종료

     

     - 쓰레드 API 예시 

    <출력 결과>

    출력값

    위의 코드는 동기화 문제를 처리하지 않은 코드라서 문제가 있다.

    순차처리가 아니라 결과가 매번 다르게 나오는 문제 발생. 0으로 끝나야 하는데 그렇지가 않다. -> 동기화 문제


    💻 쓰레드 동기화 문제

     - Thread Synchronization 
    공유자원이면 다 해당되는 문제고 싱글 코어에서도 발생하며 커널 내부 함수에서도 동기화 문제가 발생한다.

    멀티쓰레드 프로그램에서 쓰레드가 같이 일하는 상황에서 공유 자원에 접근하게 된다.

    프로세스 스위치 하는 과정에서 원자적 연산 아니면 load, EX, store하는 과정에서 문제 발생

    동시에 접근하게 되면 올바르지 못한 결과가 나올 수도 있다. -> race condition

    멀티쓰레딩이 올바른 동작을 보장하게 해야한다.