[Operating System] Thread (2)

dandb3·2023년 3월 9일
0

Operating system

목록 보기
11/31

Thread Libraries

  • thread library가 thread를 만들고 관리하는 API를 제공해 준다.
  • thread를 구현하는 두 가지 방법
    • kernel의 도움 없이 전적으로 user space에서만 library를 제공 : library 내부 함수들을 사용해도 user space 에서만 이루어지고 system call이 호출되지 않는다.
    • 운영체제에 의해 지원되는 kernel-level library를 구현 : 애초에 라이브러리를 위한 코드와 자료구조는 커널 내부에 있으며, API 내부의 함수를 호출하는 것은 kernel의 system call을 호출하게 된다.
  • POSIX Pthreads : user-level & kernel-level 다 제공됨
  • Windows : kernel-level 제공됨
  • Java : 원래 운영체제가 무엇인지에 따라 결정됨
  • POSIX와 Windows threading에서는 전역적으로 선언된 데이터는 그 프로세스의 모든 스레드에서 공유된다.
  • 여러 thread를 만드는 두 일반적인 방법
    • asynchronous threading : parent가 child thread를 만들면, parent는 실행을 재개해서 둘은 동시에 독립적으로 실행된다. -> data sharing은 적다.
      앞에서 설명했던 multithreaded server에서 주로 사용됨 <- responsive user interface를 만들기 위함.
    • synchronous threading : parent가 children을 만들고 나서 모든 children이 종료될 때까지 기다린다.
      일반적으로 많은 데이터 공유가 이루어진다. -> 예 : parent에서 각 children의 작업이 끝난 후에 각각의 결과를 총집합해서 전체 결과를 만들어내는 방식.

Pthreads

  • Pthreads : POSIX standard; -> specification이지 implementation은 아니다. (그니까 기본적인 성질들이 POSIX standard에 정의되어 있지만 어떻게 구현되는지는 환경마다 다를 수 있다.)

Implicit Threading

  • run-time libraries, compilers에게 threading을 맡기는 것.
  • 개발자들은 parallel task를 알려주기만 하면 libraries가 알아서 만들고 관리해준다. -> 최근 유행하는 방식. 근데 그냥 일단은 대충 알기만하고 넘어가는게 좋을듯.

Thread Pools

  • multithreaded server의 경우 스레드를 여러 개 만들어 관리를 하지만, 스레드를 만들고 없애는 과정이 시간이 많이 필요하다. 또한, 스레드의 개수가 너무 많아지게 되면 CPU시간, 메모리와 같은 자원이 많이 쓰여서 성능이 저하된다. -> thread pool을 사용해서 해결!
  • Thread Pool?
    • 미리 여러 스레드들을 만들어서 pool이라는 곳에 집어넣는다. server가 요청을 받으면, pool에서 스레드를 꺼내와서 해당 요청을 수행한다. 만약에 남은 pool이 없으면 queue에 집어넣어서 대기시킨다. 작업이 끝난 스레드는 다시 pool로 돌아와서 다음 작업을 대기한다. asynchronous한 작업들을 실행할 때 잘 동작한다.
  • 이점?
    1. 새로 만드는 것 보다 있던 스레드를 꺼내오는게 더 빠르다.
    2. 애초에 thread의 최대 수를 pool에 있는 수로 제한하기 때문에 큰 수의 스레드를 제한하지 않는 시스템에 좋다.
    3. task를 수행하는 부분과 생성하는 부분을 나누면 다양한 전략들을 사용할 수 있다 : time delay 후에 task가 scheduled될 수 있고, 주기적으로 실행되게 할 수 있다.
  • pool 안의 thread의 수는 CPU의 수, physical memory의 크기, concurrent client request의 예상 수 등등 여러 조건에 의해 결정된다. 사용 패턴에 따라 dynamically 조절되는 경우도 있다. (그냥 대충 개념 정도만 알고 넘어가면 될듯)

Fork Join

  • 기존 예제코드에서의 방법은 fork-join model이라고도 불린다. 이러한 synchronous model은 explicit thread creation이라고도 불리지만, implicit한 모델로도 활용될 수 있다. 코드상에서 thread를 만드는 부분을 실제 thread를 만드는 것이 아닌, parallel하게 실행되는 부분이라고 간주하고, library 내부에서 스레드의 수를 관리하면서 필요할 때 스레드를 나누어주는 이러한 방식도 가능하다.

Threading Issues

The fork() and exec() System Calls

  • multithreaded program에서는 fork()와 exec() system call이 바뀐다.
  • fork()를 실행 -> 실행한 현재 스레드만 있는 새로운 프로세스를 생성할 것인가, 아니면 모든 스레드를 포함한 프로세스를 생성할 것인가?
  • exec() system call은 기존과 동일하게 프로세스 전체를 replace 하기 때문에 모든 스레드 또한 replace된다.
  • fork()를 둘 중 어떻게 사용할 것인지는 application에 따라 달라지는데, 예를들어 fork()이후에 바로 exec()을 사용한다면 어차피 전체 스레드를 복제해 봤자 exec()이 가리키는 프로그램의 스레드로 대체될 것이므로 이 경우에는 현재 스레드만 복제한다. 이외의 경우에는 스레드 전체를 복제하게 된다.

Signal Handling

  • UNIX system에서 process에게 특정한 event가 발생했다는 것은 signal을 통해서 알려줄 수 있다.
  • signal 또한 synchronously 또는 asynchronously하게 수신될 수 있다. 다음 세 가지 이유로 구분된다 :
    1. 특정한 event에 의해 시그널이 발생했다.
    2. signal이 process에게 전달되었다.
    3. 수신된 이후에, signal은 handle되어야 한다.
  • synchronous signal은 signal을 발생시킨 operation을 한 프로세스 본인에게 전달된다.
    • 예시 : illegal memory access and division by 0.
  • asynchronous signal : 현재 실행중인 프로세스의 외부의 event에 의해 signal이 발생해서 전달된다면 synchronous signal이라고 한다.
    • 예시 : Ctrl + C로 종료시그널 보냈을 경우, timer expire가 발생했을 경우.
  • signal은 둘 중 하나의 handler에 의해 handle되어야 한다.
    1. A default signal handler
    2. A user-defined signal handler
  • 모든 시그널은 kernel이 실행시키는 default handler가 존재하고, user-defined signal handler에 의해 override된다. signal을 ignore하는 경우도 있음.
  • single-threaded program의 경우 signal은 process에 전달되니까 이해하기가 쉬운데, multi-threaded program에서는 어디로 시그널을 보내야 할까?
    1. signal이 적용되는 스레드로 시그널을 보내자.
    2. 프로세스의 모든 스레드에 시그널을 보내자.
    3. 프로세스의 몇몇 스레드에만 시그널을 보내자.
    4. 모든 시그널을 수신하는 스레드를 하나 만들자.
  • 생성된 signal의 type에 따라 시그널을 보내는 방법이 달라짐.
    • synchronous signal의 경우 그 시그널을 만든 thread로만 전달되어야 한다.
    • asynchronous signal의 경우, 명확하지 않다. Ctrl + C의 경우에는 모든 스레드에 전달이 되어야 한다.
  • standard UNIX function for delivering a signal : kill(pid_t pid, int signal);
  • UNIX에서는 스레드 별로 signal을 받아들일건지 block할 건지 결정할 수 있다. -> block하지 않은 스레드에게 시그널이 전달되고, 일반적으로 block하지 않은 첫 번째 스레드에만 시그널을 전달하게 된다.
  • POSIX pthread에서는 특정한 스레드에 시그널을 전달하는 함수가 있다 :
    • pthread_kill(pthread_t tid, int signal);

Thread Cancellation

  • thread cancellation : thread가 작업을 다 완료하기 전에 terminate 하는 것이다.

  • 예를 들어, web browser에서 이미지 로딩 중에 유저가 다른 버튼을 클릭했을 때 이미지 로딩을 담당하는 스레드는 terminate되게 된다.

  • 이런 방식으로 cancel될 스레드를 target thread라고 부르고, 이 스레드를 2가지 방법으로 cancel시킬 수 있다 :

    1. Asynchronous cancellation : 하나의 thread가 target thread를 즉시 terminate 시킨다.
    2. Deffered(미루다) cancellation : target thread가 주기적으로 terminated 되어야 하는지 확인하고, 정해진 방식에 따라 스스로를 terminate한다.
  • 문제가 발생하는 경우가 있는데, canceled thread에게 할당된 자원이 있거나 다른 스레드들과 공유하는 자원을 수정하는 도중에 cancel되는 경우가 있다. 특히 asynchronous하게 cancel되었을 경우 자원이 덜 회수된다던지 하는 문제가 발생한다.

  • deferred cancellation의 경우 target thread가 스스로 cancel되어야 하는지 체크한 다음에 자신을 cancel시키기 때문에, 할당한 자원을 모두 해제하고 종료시키므로 안전하다.

  • Pthreads에서는 pthread_cancel()함수가 있다. 예제코드 :

  • pthread_cancel()은 target thread로 하여금 cancel하도록 요청만 하고, 강제하지는 않는다. 스레드가 cancel되는 방식은 thread가 request를 어떻게 처리할건지 설정된 값에 따라 달라진다. <아래 표 참고>

  • 표를 보면 알 수 있듯, Pthreads는 thread가 cancellation을 disable할지 enable할지 선택하게 한다. disabled라면 cancel되지 않는다. 하지만 request 자체는 남아있기 때문에, 나중에 mode가 바뀐다면 cancel될 수도 있다.

  • default cancellation type : deferred cancellation

  • thread가 cancellation point에 도달해야지만 cancellation이 이루어진다.

  • 대부분의 POSIX와 standard C library의 blocking system call들은 cancellation point라고 정의되어 있고, man pthreads를 통해 확인해 볼 수 있다.

  • 직접 cancellation point를 만드는 방법 : pthread_testcancel()함수 사용하기.

  • pthread_testcancel()함수 : cancellation request가 존재한다면 리턴하지 않고 thread는 종료되고, 존재하지 않는다면 리턴되고 thread는 다음 명령을 실행하게 된다.

  • Pthreads는 스레드가 canceled 되었을 때 cleanup handler 함수가 호출되도록 할 수 있다.

  • thread가 terminate 되기 전에 할당해 놓은 자원들을 할당해제해주는 그런 과정이 포함될 수 있음.

  • 이런 과정을 통해서 deferred cancelation을 구현할 수 있다.

  • 앞선 문제점들 때문에 Pthread documentation에서는 asynchronous cancellation을 추천하지 않는다.

  • Linux system에서, Pthreads API를 이용한 thread cancellation은 signal을 통해 이루어진다.

참고 자료

  • Abbraham Silberschatz, Operating system concepts, 10th edition
profile
공부 내용 저장소

0개의 댓글