Chapter 4. Threads & Concurrency

박병준·2022년 4월 12일
0

운영체제

목록 보기
4/11

Motivation

하나의 응용 프로그램이 여러 개의 비슷한 작업을 처리해야 할 상황에서, 만약 웹 서버가 단일 스레드 프로세스로 작동한다면, 한 번에 하나의 클라이언트만 처리할 수 있게 돼서 매우 긴 시간이 걸리게 된다.

이를 위한 방법으로는, 서버에게 서비스 요청이 들어오면, 프로세스는 그 요청을 수행할 별도 프로세스를 생성하는 것이다.

하지만 프로세스 생성 작업은 매우 많은 시간을 소비하고 많은 자원을 필요로 한다.

따라서 대부분은 프로세스 안에 여러 스레드를 만들어 나가는 것이 더 효율적이다.


Overview

스레드(Thread)는 CPU 이용의 기본 단위이다. 스레드는 ID, PC(Program Counter), 레지스터 집합, 스택으로 구성된다.

또한 스레드는 같은 프로세스에 속한 다른 스레드와 코드, 데이터 섹션, 그리고 열린 파일이나 신호와 같은 운영체제 자원들을 공유한다.

전통적인 프로세스는 하나의 제어 스레드를 가지고 있지만, 현대 프로세스는 다수의 제어 스레드를 가지고 있기 때문에 해당 프로세스는 동시에 하나 이상의 작업을 수행할 수 있다.

스레드의 장점

  1. 응답성
    응용 프로그램의 일부분이 봉쇄되거나, 긴 작업을 수행하더라도 프로그램의 수행이 계속되는 것을 허용함으로써, 사용자에 대한 응답성을 증가시킨다.

  2. 자원 공유
    프로세스는 공유 메모리와 메시지 전달 기법을 통하여만 자원을 공유할 수 있다.
    그러나 스레드는 자동으로 그들이 속한 프로세스의 자원들과 메모리를 공유한다.

  3. 경제성
    스레드는 자신이 속한 프로세스의 자원들을 공유하기 때문에, 스레드를 생성하고 context-switch하는 것이 더욱 경제적이다.
    또한 context-switch은 일반적으로 프로세스 사이보다 스레드 사이에서 더 빠르다.

  4. 규모 적응성
    다중 프로세서 구조에서 각각의 스레드는 다른 프로세서에서 병렬로 수행될 수 있기 때문에 규모 적응성에서 이점을 갖는다.


Multicore programming

여러 컴퓨팅 코어를 보다 효율적으로 사용하고 병행성을 향상시키는 기법을 제공한다.

어려움

  1. identifying tasks
    어플리케이션을 분석하여 독립된 병행 가능 태스크로 나눌 수 있는 영역을 찾는 작업이 필요하다.

  2. balance
    분석을 통해 찾은 태스크들이 전체 작업에 균등한 기여도를 가지도록 나누는 것이 중요하다.

  3. data spliting
    태스크가 접근하고 조작하는 데이터는 개별 코어에서 사용할 수 있도록 나누어져야 한다.

  4. data dependency
    테스크가 접근하는 데이터는 둘 이상의 태스크 사이에 종속성이 없는지 검토되어야 한다. (동기화 문제)

  5. testing and debugging
    병행 프로그램을 시험하고 디버깅


Multithreading models

스레드는 두 가지로 나뉘는데, 사용자 수준의 사용자 스레드(user threads), 커널 수준의 커널 스레드(kernel threads)이다.

사용자 스레드는 커널 위에서 지원되며 커널의 지원 없이 관리된다.

반면 커널 스레드는 운영체제에 의해 직접 지원되고 관리된다.

사용자 응용 프로그램은 사용자 수준 스레드를 생성하며, 이 스레드는 궁극적으로 CPU에서 실행되도록 커널 스레드에 매핑되어야 한다.

다대일 모델 (Many to One Model)

다대일 모델은 많은 사용자 스레드를 하나의 커널 스레드로 사상한다. 스레드 관리가 사용자 공간의 스레드 라이브러리에 의해 행해지기 때문에 효율적이라 할 수 있다.

하지만, 한 스레드가 봉쇄형 시스템 콜을 할 경우, 전체 프로세스가 봉쇄되는 등 다중 처리 코어의 이점을 살릴 수 없기 때문에 이 모델을 사용 중인 시스템은 거의 존재하지 않는다.

일대일 모델 (One to One Model)

일대일 모델은 각 사용자 스레드를 각각 하나의 커널 스레드로 사상한다.

다대일처럼, 한 스레드가 봉쇄형 시스템 콜을 해도 나머지가 실행될 수 있기 때문에 더 많은 병렬성을 제공한다.

이 모델의 단점은 사용자 스레드를 만들려면 해당 커널 스레드를 만들어야 하며, 많은 수의 커널 스레드가 시스템 성능에 부담을 줄 수 있다는 점이다. Linux, Windows 운영체제는 일대일 모델을 구현한다.

다대다 모델 (Many to Many Model)


다대다 모델은 여러 개의 사용자 스레드를 그보다 작거나 같은 수의 커널 스레드로 멀티플렉스 한다.

이 모델은 스레드 수가 맞지 않아도 되는, 진정한 병렬성을 제공한다는 점에서 위 두 가지 모델의 문제점을 어느 정도 해결했다.

하지만 실제로 구현하기 매우 어렵기 때문에 대부분의 운영체제는 일대일 모델을 사용한다.


Thread libraries

프로그래머에게 스레드를 생성하고 관리하기 위한 API를 제공한다.

스레드 라이브러리를 구현하는데에는 주된 두 가지 방법이 있다.

  • 커널의 지원 없이 완전히 사용자 공간에서만 라이브러리를 제공하는 것
  • 운영체제에 의해 지원되는 커널 수준 라이브러리를 구현하는 것

현재는 POSIX, Windows, Java 세 종류의 스레드 라이브러리가 주로 사용된다.


Threading issues

Fork() 및 Exec() 시스템 콜

우리는 다중 스레드 프로그램에서 fork()와 exec()의 의미에 대해서 생각해 보아야 한다.

만일 한 프로그램의 스레드가 fork()를 호출하면 새로운 프로세스는 모든 스레드를 복제해야 하는가 아니면 한 개의 스레드만 가지는 프로세스여야 하는가?

  • UNIX 시스템은 fork() API로써 이 둘의 기능을 다 지원한다.

보통 어떤 스레드가 exec() 시스템 콜을 부르면 exec()의 매개변수로 지정된 프로그램이 모든 스레드를 포함한 전체 프로세스를 대체시킨다.

우리는 운영체제에서 지원해주는 기능에 따라 적절히 fork()와 exec()를 사용해야 한다.

스레드 취소 (Thread Cancellation)

스레드가 끝나기 전에 그것을 강제 종료시키는 작업을 일컫는다.

취소되어야 할 스레드를 목적 스레드(target thread)라고 부른다.
목적 스레드는 다음과 같은 두 가지 방식으로 취소할 수 있다.

  • 비동기식 취소(asynchronous cancellation): 한 스레드가 즉시 목적 스레드를 강제 종료시킨다.

  • 지연 취소(deferred cancellation): 목적 스레드가 주기적으로 자신이 강제 종료 되어야 할지를 점검한다. (질서 정연하게 강제 종료 가능)

스레드 취소를 어렵게 만드는 것은 취소 스레드들에 할당된 자원의 문제가 가장 크기 때문에 이를 잘 고려해서 스레드 취소를 해야 한다.

신호 처리 (Signal Handling)

신호는 UNIX에서 프로세스에 어떤 이벤트가 일어났음을 알려주기 위해 사용된다.

신호는 비동기적, 동기적으로 발생할 수 있는데 이와 상관없이 모든 신호는 다음과 같은 형태로 전달되어야 한다.

  1. 신호는 특정 이벤트가 일어나야 생성된다.
  2. 생성된 신호가 프로세스에 전달된다.
  3. 신호가 전달되면 반드시 처리되어야 한다.

모든 신호는 둘 중 하나의 처리기에 의해 처리된다.

  1. 디폴트 신호 처리기
  2. 사용자 정의 신호 처리기

프로세스가 여러 스레드를 가지고 있는 경우 동기식 신호는 그 신호를 야기한 스레드에 전달되어야 하고 다른 스레드에 전달되면 안 된다.
반면에 비동기 신호의 경우에는 그 프로세스 내 모든 스레드에 전달되어야 한다.

스레드 풀

다중 스레드 프로세스의 여러 문제점을 해결해 줄 수 있는 방법이 스레드 풀(Thread pool) 방법이다.

스레드 풀의 기본 아이디어는 프로세스를 시작할 때 아예 일정한 수의 스레드들을 미리 풀로 만들어두는 것이다. 이 스레드들은 평소에 하는 일 없이 일감을 기다리게 된다. 서버는 스레드를 생성하지 않고 요청을 받으면 대신 스레드 풀에 제출하고 추가 요청 대기를 재개한다. 풀에 사용 가능한 스레드가 있으면 깨어나고 요청이 즉시 서비스 된다. 스레드가 서비스를 완료하면 풀로 돌아가서 더 많은 작업을 기다린다.

스레드 풀의 장점

  1. 새 스레드를 만들어 주기보다 기존 스레드로 서비스해 주는 것이 종종 더 빠르다.
  2. 스레드 풀은 임의 시각에 존재할 스레드 개수에 제한을 둔다.
  3. 태스크를 생성하는 방법을 태스크로부터 분리하면 태스크의 실행을 다르게 할 수 있다.

profile
뿌셔뿌셔

0개의 댓글