Thread

Jin·2022년 2월 27일
0

운영체제

목록 보기
2/3

Thread가 떠오른 배경

  • 프로세스는 너무 무거움
    • 각각의 프로세스는 address space를 가져서 code와 data를 따로 차지
    • OS 자원과 정보
    • Hardware State
  • 프로세스를 구성하기 위한 공간과 data structure가 필요하기 때문에 새로운 프로세스를 생성하는 비용이 너무 비쌈
  • OS를 거쳐서 IPC를 하는 비용이 너무 비쌈

위와 같은 치명적인 이유들로 Thread가 부상되었고 다음의 특징을 가집니다

  • code와 data 영역을 공유
  • 같은 권한을 가짐
  • 자원들을 공유
  • 자신만의 hardware state를 가짐

프로세스가 모든 자원과 공간을 독립적으로 가지고 있는 개체라고 한다면 스레드는 공통된 부분은 공유하고 필요한 부분만 독립적으로 구성되어 있습니다.

Process vs. Thread

  • 스레드는 하나의 프로세스 안에 존재
  • 프로세스는 여러 개의 스레드를 가질 수 있음
  • 스레드는 같은 address space를 가지기 때문에 sharing data가 쌈
  • 최근에 프로세스는 스레드를 담기 위한 컨테이너의 역할을 주로 수행
  • 프로세스에 비해 스레드가 더 dynamic

이런 점들은 throughput, responsiveness, resource sharing, economy, utilization 같은 부분들에서 훨씬 뛰어나기 때문에 이제는 thread를 통해 multiprogramming을 하는 경우가 대부분입니다.

Pthread

이제 threads interface 중 가장 대표적인 POSIX Thread에 대해 살펴보려고 합니다.

  • Thread creation / termination
    • #include <pthread.h>
    • gcc library libpthread.a : -lpthread
  • int pthread_create (pthread_t tid, pthread_attr_t attr, void (start_routine)(void ), void *arg)
    • tid: thread의 id로 OS가 부여
    • *attr: pthread의 속성을 설정하는 것으로 default는 NULL
    • (void *): 수행할 함수
    • *arg: 함수에 전달할 인자
  • void pthread_exit (void *retval)
    • pthread를 종료
  • int pthread_join (pthread_t tid, void **thread_return)
    • parent thread가 child thread가 종료되기를 기다리는 함수로 프로세스의 waitpid 함수와 같은 기능

Threading Issues

1. fork() & exec()

  • 만약 하나의 프로세스에 여러 개의 thread들이 동작하고 있다고 가정해봅시다. fork() 시 프로세스 안에 있는 모든 thread들이 복제되는 것일까요?
  • Pthread에서는 fork()를 호출하는 thread만 복제
  • Unix standard에서 fork()은 프로세스 안에 있는 모든 thread를 복제
  • Unix standard에서 fork1()은 fork()를 호출하는 thread만 복제
  • 일반적으로 exec()은 프로세스 전체를 대체

2. Thread cancellation

  • Asynchronous cancellation
    • target thread를 즉시 종료
    • 만약 target thread가 자원을 가지고 있을 시 문제가 발생할 수도
  • Deferred cancellation (default)
    • target thread가 바로 종료되지 않음
    • pthread_testcancel()을 통해 thread 스스로 cancel 되어야 하는지를 주기적으로 check 하여 종료되어야 한다고 확인 시 종료
  • Cancellation APIs
    • int pthread_cancel (pthread_t thread): return 0, error시 return -1
    • int pthread_setcancelstate (int state, int *oldstate): cancel 허용 여부를 결정. oldstate는 이전의 state로 복구를 위해 필요
    • int pthread_canceltype (int type, int *oldtype): deferred, asynchronous 같이 cancel의 타입을 결정

3. Signal handling

  • signal이 왔을 때 어디에서 받을 것인가에 대한 문제입니다. 프로세스 안의 모든 thread, 특정 thread, signal을 요청한 thread 등 여러 가지 방법이 있지만 일반적으로 signal에 unblock인 thread 중 하나가 받습니다.

4. Thread-Local Storage

  • 프로세스 안의 thread들이 프로세스의 데이터를 공유하는 영역
  • TLS는 thread마다 자신만의 데이터 copy를 가질 수 있도록 함
  • 지역변수와의 차이점은 지역변수는 하나의 함수 안에서만 보이지만 TLS는 해당 thread 안에서는 다 보인다는 점에서 static 변수와 유사함

5. Using libraries

  • errno: 오류 코드의 매크로 번호

6. Easy sharing but error-prone

  • 전역 변수는 thread들 간에 쉽게 공유되지만 명시적이 아닌 묵시적으로 동기화를 할 시 error가 발생할 수 있음. 또한, 포인터처럼 주소를 넘겨주는 작업은 caller와 callee 사이를 넘어오면서 주소 값이 변할 수 있기 때문에 바람직하지 않음

Kernel-level Threads

  • OS가 관리하는 thread
    • OS가 thread와 프로세스를 모두 관리
    • 모든 thread의 실행은 kernel의 제어를 받음
    • OS가 시스템의 모든 thread들의 스케줄링을 담당
      • 만약, 어느 프로세스 안의 하나의 thread가 block 상태로 빠진다면 OS가 이를 알아채고 그 프로세스 안의 다른 thread를 실행시킬 수 있음
      • I/O와 CPU의 overlap이 가능
    • kernel을 거치지만 그럼에도 프로세스보다는 쌈
    • Windows, Mac OS, Linux 등 대부분의 OS가 이 방식을 채택
  • 한계
    • 여전히 비싼 편에 속함
      • 높은 수준의 병행성을 요구할 시, 더 적은 비용의 thread가 요구됨
      • thread operation을 함수 호출만큼 빠르게 할 수 없음
    • Thread operation이 모두 system call
      • thread operation을 할 때마다 protection boundary를 넘어가야 되고 이것은 같은 프로세스 내의 thread라도 마찬가지여서 비효율적
    • thread마다 kernel state를 유지시켜야 하므로 동시에 위치시킬 수 있는 thread의 수가 제한적 (~ 1000)
    • kernel-level thread는 프로그래머, 언어, 시스템에 모두 지원을 받을 수 있어야 함

User-level Threads

  • 동기
    • thread를 더 값싸고 빠르게 커널의 도움 없이 user level 선에서 실행시키기 위해
    • library 수준에서 처리되므로 이식성을 높이고자
  • 장점
    • 각각의 thread는 PC, registers, stack, TCB (Thread Control Block)에 의해 간단히 표시
    • thread를 생성, switching, 동기화시키는 것이 함수 호출 수준으로 동작
    • kernel-level thread에 비해 10~100배 빠름
  • 한계
    • OS에 보이지 않음
      • OS와 합이 잘 맞지 않음
    • OS가 잘못된 결정을 할 가능성이 높아짐
      • idle 상태에 있는 thread들을 스케줄링할 수 있음
      • 프로세스 안에 ready 상태의 다른 thread가 존재함에도 하나의 thread가 block 상태라는 이유로 프로세스 전체가 block됨
      • lock을 소유하고 있는 thread를 unscheduling 시키지 않을 수 있음
      • 위의 경우들은 모두 OS가 user-level thread를 볼 수 없기 때문에 발생함
      • 이를 해결하기 위해서는 kernel과 user-level thread manager (library) 간의 협력이 요구됨
profile
배워서 공유하기

0개의 댓글