운영체제 - ch.04

Jajuna_99·2023년 4월 23일
0

운영체제

목록 보기
5/10
post-thumbnail

운영체제 - 4장 스레드와 병행성

개요

단일 스레드 프로세스와 다중 스레드 프로세스 차이점

동기

대부분의 현대 응용 프로그램들은 멀티스레드이다.

스레드는 응용 프로그램 내에서 실행된다.

응용 프로그램에 멀티 태스크는 스레드를 분리하는 것으로 구현할 수 있다.

다중 스레드 응용 프로그램의 몇 가지 예시

  • update display
  • fetch data
  • spell checking
  • answer a network request

프로세스 생성은 무거운 작업인 반면에 스레드 생성은 가벼운 작업이다. 그리고 코드를 간단히하고 효율을 높인다.

커널들도 통상적으로 멀티스레드이다.

장점

멀티스레드의 장점으로는

  • 빠른 응답시간 : 만약 프로세스가 부분적으로 봉쇄되도, 작업을 연속적인 수행을 가능하게한다. 유저 인터페이스에서 특히 중요하다.
  • 자원 공유 : 스레드들은 프로세스의 자원을 공유한다. 공유 메모리 방식이나 메시지 전달 방식이 더 편하다.
  • 경제성 : 프로세스 생성보다 저렴하다. 스레드 교환은 문맥 교환보다 오버헤드가 적다.
  • 확장성 : 프로세스가 멀티코어 구조의 장점을 얻는다.

다중 코어 프로그래밍

!(병행은 단일 프로세스에 다중 프로그램, 병렬은 다중 프로세스이다.)
(암달의 법칙 교재 p.180)

프로그래밍 도전과제

멀티코어 혹은 멀티프로세서 시스템은 프로그래머들에게도 부담스러운 과제이다.

  • dividing activities : 독립된 병행 가능 태스크로 나눌 수 있는 영역을 찾는 작업이 필요하다. (태스크는 서로 독립적이고 따라서 개별 코어에서 병렬 실행될 수 있어야 한다.)

  • balance : 찾아진 부분들이 전체 작업에 균등한 기여도를 가지도록 태스크를 나누는 것

  • data spliting : 데이터 또한 개별 코어에서 사용할 수 있도록 나누어져야 한다.

  • data dependency : 태스크가 접근하는 데이터는 둘 이상의 태스크 사이에 종속성이 없는지 검토되어야 한다.

  • testing and debugging : 병행 프로그램을 시험하고 디버깅하는 것은 단일 스레드 응용을 시험 디버깅보다 근본적으로 훨씬 어렵다.

다중 스레드 모델

유저 스레드와 커널 스레드

유저 스레드 : 유저 레벨 스레드 라이브러리에서 관리가 가능하다

  • POSIX Pthreads
  • windows threads
  • java threads

커널 스레드 : 커널에서 지원되는 스레드
모든 가상 범용 목적 운영체제를 포함한다. (windows, linux, mac os X, iOS, Android)

유저 스레드와 커널 스레드 관계를 확립하는 일반적인 세가지 방법 다대일, 일대일, 다대다 모델이 있다.

다대일 모델


많은 사용자 수준 스레드를 하나의 커널 스레드로 사상(mapping)한다.

한 스레드가 봉쇄형 시스템 콜은 할 경우, 전체 츠로세스가 봉쇄된다.

또한, 한 번에 하나의 스레드만이 커널에 접근할 수 있기 때문에, 다중 스레드가 다중 코어 시스템에서 병렬로 실행될 수 없다.

이 모델을 사용 중인 시스템은 거의 존재하지 않는다.

이 모델을 사용한 시스템으로는

  • Solaris Green Threads
  • GNU Portable Threads

가 있다.

커널 스레드가 운영체제 전체에 하나만 있다는 뜻이 아닌 사용자 프로세스에 상응하는 커널 스레드가 하나만 있다는 의미

장점으로는 원하는 만큼 사용자가 스레드를 생성할 수 있다.

일대일 모델

각 사용자 스레드를 가각 하나의 커널 스레드로 사상한다.

하나의 스레드가 봉쇄적 시스템 콜을 호출해도 다른 스레드가 실행될 수 있기 때문에 다대일 보다 더 많은 병렬성을 제공하고 여러개의 스레드를 다중코어에 매핑할 수 있다.

사용자 스레드와 커널 스레드가 1:1 이기 때문에 사용자 스레드를 오버헤드 때문에 제한할 필요가 있는 단점이 있다.

이 모델을 사용한 시스템으로는

  • Window
  • Linux

가 있다.

다대다 모델

여러 개의 사용자 수준 스레드를 그보다 작은 혹은 같은 수의 커널 스레드로 멀티플렉스하는 모델이다.

개발자는 필요한 만큼 많은 사용자 수준 스레드를 운영체제는 컬널 스레드를 생성할 수 있다는 장점이 있고 구현이 어렵다는 단점이 있다.

윈도우의 TheadFiber를 제외하고는 잘 사용되지 않는다.

두 수준 모델

다대다 모델과 유사하지만 유저 스레드가 커널 스레드 bound?될수 있다는 점이 다르다.

스레드 라이브러리

스레드 라이브러리 : 개발자에게 스레드를 생성하고 관리하는 API를 제공한다.

스레드 라이브러리르 구현하는데 주된 두 가지 방법

  • 커널의 지원 없이 완전히 사용자 공간에서만 라이브러리를 제공하는 것 (사용자 공간의 지역 함수를 호출하게 되는 결과)
  • 운영체제에 의해 지원되는 커널 수준 라이브러리를 구현하는 것 (커널에서 시스템 콜 호출하는 결과)
  • 비동기 스레딩 : 부모(main)와 자식(sub, worker) 스레드가 독립적으로 병행 실행
  • 동기 스레딩 : 부모 스레드는 모든 자식 스레드가 종료될 때까지 기다림
  • POSIX Pthreads : 사용자 또는 커널 수준 라이브러리로서 제공
  • Windows : 커널 수준 라이브러리로서 제공
  • Java threads : OS에 따라 다른 수준 라이브러리 제공 (호스트 운영체제에서 실행되기 때문)

Pthreads

Pthreads : POSIX가 스레드 생성과 동기화를 위해 제정한 표준 API (IEEE 1003.1c)

스레드 동작에 관한 명세일 뿐이지 그것 자체를 구현한 것은 아니다.

UNIX 운영체제 (Linux & Mac OS X)의 많은 시스템이 Pthreads 명세를 구현하고 있다.

스레드 라이브러리의 작동을 명세하는 API이고 구현은 라이브러리의 개발자에 달렸다.

사용자, 커널 수준 둘 다에서 제공될 수 있다.

(부모가 다수의 자식 스레드가 종료할 때까지 기다리는 경우)

암묵적 스레딩

다중 코어 처리의 성장과 그에 따른 스레드 응용 개수가 늘어나면서 인기가 많아졌다.

프로그램의 정확도는 명시적 스레딩에서 더욱 어렵다.

한 가지 해결 방안으로 스레딩의 생성과 관리 책임을 응용 개발자로부터 컴파일러와 실행시간(런타임) 라이브러리에게 넘겨주는 것이다. (개발자는 병렬로 수행할 수 있는 task를 식별하는 데 주력)

밑에 다섯가지 방법으로 구현되어왔다.

스레드 풀

프로세스를 시작할 때 아예 일정한 수의 스레드들을 미리 풀로 만들어 일감을 기다리게 해놓는 것이다.

장점

  • 새 스레드를 만들어 주기보다 기존 스레드로 서비스해 주는 것이 종종 덜 빠르다.
  • 스레드 풀은 임의 시각에 존재할 스레드 개수에 제한을 둔다. 이러한 제한은 많은 수의 스레드를 병렬 처리할 수 없는 시스템에 도움이 된다.
  • 태스크를 생성하는 방법을 태스크로부터 분리하면 태스크 실행을 다르게 할 수 있다.
    • 예를 들면 태스크를 일정 시간 후에 실행되도록 스케줄 하거나 혹은 주기적으로 실행시킬 수 있다.

Fork Join

메인 스레드가 하나 이상의 자식 스레드를 생성(fork)하고 자식의 자식의 종료를 기다린 후 join하고 그 시점부터 자식의 결과를 확인하고 결합할 수 있다.

OpenMP

OpenMP : C, C++, Fortran으로 작성된 API와 컴파일러 디렉티브(컴파일러 지시문)의 집합니다.

공유 메모리 환경에서 병렬 프로그래밍을 할 수 있도록 도움을 준다.

병렬로 실행될 수 있는 블록을 찾아 병렬 영역이라고 하고 응용 개발자는 자신들의 코드 중 병렬 영역에 컴파일러 지시문을 삽입, 이 지시문은 OpenMP 런타임 라이브러리에 해당 영역을 병렬로 실행하고 지시한다.

Grand Central Dispatch

macOS 및 iOS 운영체제를 이해 Apple에서 개발한 기술이다.

개발자가 병렬로 실행될 코드 섹션(태스크)을 식별할 수 있도록 하는 런타임 라이브러리, API 및 언어(C, C++) 확장의 조합이다.

OpenMP와 마찬가지로 스레딩에 대한 대부분의 세부 사항을 관리한다.

Thread pool의 크기를 자동으로 조절하고 하부는 POSIX pthread로 구현됐다.

실행시간 수행을 위해 태스크를 디스패치 큐에 넣어서 스케줄 한다.

  • 큐에서 태스크를 제거할 때 관리하는 스레드 풀에서 가용 스레드를 선택하여 태스크를 할단한다.

두 가지 유형의 디스패치 큐를 유지한다.

  • 직렬(serial) : FIFO 순서대로 하나씩 제거한다. 각 프로세스ㄴ에는 고유한 직렬 큐(메인 큐)가 있다.

    • 개발자는 특정 프로세스에 로컬인 추가 직렬 큐를 만들 수 있다.
    • 프로세스 내에 있기 때문에 private dispatch queue라고도 부름
  • 병렬(concurrent) : FIFO 순서대로 여러개가 한번에 제거될 수 있다.

Intel 스레드 빌딩 블록 (TBB)

C++에서 병렬 응용 프로그램 설계를 지워나는 탬플릿 라이브러리이다.

라이브러리이므로 특별한 컴파일러나 언어 지원이 필요하지 않다. 개발자는 병렬로 실행할 수 있는 태스크를 지정하고 TBB 태스크 스케줄러는 이러한 태스크를 하부 스레드에 매핑한다.

스레드와 관련된 문제들

다중 스레드 프로그램 설계 시 고려해야할 문제들

Fork() 및 Exec() 시스템콜

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

유닉스 운영체제 중에서는 두 경우를 둘 다 지원하기도 한다.

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

fork()후 exec()이 바로 수행되는 경우 '한 개의 스레드만 가지는 프로세스(호출한 스레드)'가 유리하고 리눅스 또한 이 경우를 지원한다.

신호 처리

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

신호 처리기는 신호를 처리하기 위해 사용된다.

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

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

  • 디폴트 신호 처리기
  • 사용자 정의 신호 처리기

신호가 처리될 때, 모든 신호는 커널에서 실행되는 디폴트 신호 처리기가 있다.

디폴트 처리기는 사용자 정의 처리기에 의해 override 될 수 있다.

단일 스레드 프로그램에서의 신호 처리는 간단하다. 신호는 항상 프로세스에 전달된다.

다중 스레드 프로그램은 신호 처리가 복잡하고, 다음과 같은 선택이 있다.

  • 신호가 적용될 스레드에게 전달한다.
  • 모든 스레드에 전달한다.
  • 몇몇 스레드들에만 선택적으로 전달한다.
  • 특정 스레드가 모든 신호를 전달받도록 지정한다.

스레드 취소

스레드가 끝나기 전에 강제 종료 시키는 작업

취소되어야 할 스레드를 목적 스레드라고 한다.

목적 스레드의 취소는 두 가지 방식으로 발생할 수 있다.

  • 비동기식 취소 : 한 스레드가 즉시 목적 스레드를 강제 종료시킨다. (할당된 자원을 모두 free 하지 못 할 경우가 생길 수 있음)
  • 지연 취소 : 목적 스레드가 주기적으로 자신이 강제 종료되어야 할 지를 점검한다. 이 경우 목적 스레드가 질서정연하게 강제 종료될 수 있는 기회가 생긴다.
    • 취소지점에서 스레드를 캔슬한다. 일반적으로 read()와 같은 bolcking system call이 취소 지점이 된다.


(스레드 취소 예시 코드)

pthread_cancel()을 호출하면 대상 스레드를 취소라라는 요청만 표시된다. 그러나 실제 취소는 요청을 처리하기 위해 대상 스레드가 설정되는 방식에 달려 있다.

만약 스레드 취소가 사용 불가능이라면, 사용 가능 상태가 될 때까지 지연된다.

기본 취소 유형은 지연 취소이다.

스레드 취소는 스레드가 취소 지점에 도착해야지만 실행된다. (pthread_testcancel() 은 인위적으로 취소 지점을 지정할 때 사용한다.)

또, Pthread는 취소될 때 정리 핸들러라고 하는 함수가 호출도게 할 수 있다. 이를 통해 스레드가 흭득한 모든 자원을 해체할 수 있다.

리눅스 시스템에서는 스레드 취소는 신호를 통해 처리된다.

스레드-로컬 저장장치 (TLS)

TLS : 한 프로세스에 속한 스레들이 그 프로세스의 데이터를 모두 공유하게 하는 것

스레드 생성 절차에 대한 제어가 없을 때 유용하다.

운영체제 사례

리눅스는 스레드보다 태스크라는 표현을 사용한다.

스레드 생성 또한 clone() 시스템 콜을 사용한다.

profile
Learning bunch, mostly computer and language

0개의 댓글