1 프로세스의 문제점

  • 프로세스 생성의 큰 오버헤드
  • 프로세스 컨텍스트 스위칭의 큰 오버헤드
  • 프로세스 사이의 통신 어려움

2 스레드 개념

2.1 스레드의 출현 목적

  • 프로세스보다 작은 실행 단위 필료
  • 프로세스의 생성 및 소멸에 따른 오버헤드 감소
  • 빠른 컨텍스트 스위칭
  • 프로세스들 사이의 통신에 대한 어려움 해소 스레드: 프로세스보다 작고 쉽게 데이터를 주고받을 수 있는 실행 단위

2.2 스레드 개념

스레드는 실행 단위이며 스케줄링 단위이다.

실행단위라는 뜻은 스레드마다 TCB(Thread Control Block) 구조체를 두고 스레드 ID, 스케줄링 우선순위 등 스레드 정보를 관리하며, 생성, 소멸, 스케줄링 등 스레드를 독립된 단위로 다룬다는 의미

프로세스는 스레드들의 컨테이너이다.

스레드는 독립적으로 존재할 수 없고 프로세스 안에 존재하도록 개념화히였다. 프로세스를 생성할 때, 커널은 자동으로 프로세스 내에 1개의 스레드를 생성하는데, 이 스레드를 메인 스레드(main thread) 라고 부른다. 프로세스 마다 PCB 생성, 스레드 마다 TCB 생성 프로세스와 스레드 관리

프로세스는 스레드들의 공유 공간 제공

스레드들은 프로세스의 주소 공간을 나누어 사용 프로세스 내에 작성된 함수들을 어떤 것이든 호출할 수 있고 프로세스 내에 선언된 전역 변수를 액세스할 수 있으며 프로세스의 힙을 공유

스레드가 실행할 작업은 함수로 작성한다.

운영체제에게 함수를 스레드로 만들어 줄 것을 요청

스레드와 프로세스의 생명

  • 스레드 함수 종료 스레드 종료
  • 모든 스레드 종료 프로세스 종료 & 메모리/자원 제거

2.5 멀티스레딩 분석

TCB는 커널에 의해 생성되고 관리된다(시스템에 따라 스레드 라이브러리에 의해 관리되기도 함)

일반적으로 함수는 다른 함수에 의해 호출되어 실행되지만, 스레드 함수의 코드는 커널에 의해 직접 CPU가 실행하도록 제어된다.

멀티스레딩과 concurrency, parallelism

  • concurrency(동시성): 1개의 CPU로 2개 이상의 스레드(혹은 프로세스)를 시간을 나누어 실행
  • parallelism(병렬성): 2개 이상의 스레드가 서로 다른 CPU에서 동시에 실행

3 스레드 주소 공간과 컨텍스트

3.1 스레드 주소 공간

스레드가 실행 중에 사용하는 메모리 공간으로 스레드의 코드, 데이터, 힙, 스택 영역이며, 이들은 모두 프로세스의 주소 공간에 형성된다.

  • 스레드 사적 공간(private space) - 스레드 스택과 스레드 로컬 스토리지(TLS, Thread Local Storage)
  • 스레드 공유 공간(shared space) - 프로세스의 코드, 데이터(로컬 스토리지 제외), 힙 영역

스레드 주소 공간에 대한 설명

  • 스레드 코드 영역
    • 프로세스 코드 영역 내에 있음 모든 스레드가 호출하여 사용 가능 스레드가 실행을 시작하는 스레드 코드 역시 여러 스레드의 스레드 코드로 동시에 활용 가능
  • 스레드 데이터 영역
    • 프로세스의 데이터 공간 내에 형성
    • 자신만 사용가능한 변수 선언 가능 - 스레드 로컬 스토리지(thread local storage)
    • 프로세스에서 선언된 모든 전역 변수들은 프로세스의 모든 스레드에 의해 공유
  • 스레드 힙 영역
    • 프로세스의 힙은 프로세스에 속한 모든 스레드들이 동적 할당받는 힙 공간으로 공유됨
    • 프로세스 내 다른 스레드에서 접근 가능
  • 스레드 스택 영역
    • 스레드의 사적 공간
    • 프로세스의 사용자 스택에서 할당
    • 프로세스에게 할당된 커널 스택에서 각 스레드마다 고유한 스택에 할당
      • 스레드가 시스템 호출을 통해 커널 모드로 진입할 때마다 사용됨

3.2 스레드의 주소 공간 확인

3.3 스레드 상태와 스레드 운용(operation)

여러 운영체제에서 공통적으로 구현하고 있는 4가지 스레드 상태에 대해 설명하며 스레드의 상태 변이는다음과 같다.

  • 준비 상태(Ready) - 스레드가 스케줄을 기다리는 상태
  • 실행 상태(Running) - 스레드가 현재 CPU에 의해 실행되고 있는 상태
  • 블록 상태(Blocked) - 스레드가 입출력을 요청하거나 sleep()과 같은 시스템 호출로 커널에 의해 중단된 상태
  • 종료 상태(Termniated) - 스레드가 종료한 상태 스레드 상태는 TCB(스레드 제어 블록)에 저장

스레드 생성(thread creation)

프로세스를 생성할 때 운영체제가 자동으로 main 스레드를 생성 main 스레드를 제외하고, 스레드는 스레드에 의해 생성됨, 부모-자식 스레드 부모 스레드가 자식 스레드에 대한 스레드 ID를 가지고 있어 자식 스레드를 제어할 수 있다.

  1. TCB 구조체 생성 후 스레드에게 ID 부여
  2. 스레드가 실행을 시작할 코드(함수)의 주소를 TCB의 PC에 기록
  3. 스레드 사용자 스택을 할당, 그 주소를 TCB의 SP에 저장 후 Ready 상태 변경, 준비 리스트에 넣기
  4. TCB를 프로세스의 PCB와 다른 TCB에 연결

스레드 종료(thread termination)

스레드 종료는 TCB가 시스템에서 제거되고 TCB가 연결된 링크들이 해제되는 것으로, 매우 단순 종료 후에도 스레드 코드 등 스레드에게 할당된 영역은 프로세스 영역 내에 그대로 남아있다. 스레드가 실행했던 함수의 코드는 그대로 존재, 동적 할당받고 해제하지 않은 메모리는 할당된 상태로 남아 있다. 다른 스레드가 이 주소를 알고 사용하고 있으면 모르지만 그렇지 않으면 프로세스가 종료할 때ㄷ까지 다른 스레드에게 할당할 수 없는 상태가 됨 스레드 스택과 TLS 영역은 다른 스레드가 생성될 때 할당될 것이다. main() 함수에서 호출시 main 스레드만 종료, 프로세스에 속한 마지막 스레드가 종료되면 그 때 프로세스 종료

스레드 조인(thread join)

스레드가 다른 스레드의 종료를 기다리는 행위

스레드 양보(thread yield)

실행 중인 스레드가 다른 스레드에게 CPU를 양보하기 위해 스스로 실행을 중단하는 행위

3.4 스레드 컨텍스트와 스레드 제어 블록(TCB)

스레드 컨텍스트(thread context)

스레드가 현재 실행중인 일체의 상황

CPU가 스레드를 실행되고 있을 때 CPU의 레지스터 값들이다.

스레드 제어 블록(TCB, Thread Control Block)

구분요소설명
스레드 정보tid스레드 ID, 스레드가 생성될 때 부여된 고유 번호
^state스레드의 상태 정보, 실행, 준비, 블록, 종료
컨텍스트PCCPU의 PC 레지스터 값, 스레드가 다음에 실행할 명령의 주소
^SPCPU의 SP 레지스터 값, 스레드 스택의 톱 주소
^다른 레지스터들스레드가 중지될 때의 여러 CPU
스케줄링우선순위스케줄링 우선순위
^CPU 사용 시간스레드가 생성된 이후 CPU 사용시간
관리를 위한 포인터들PCB 주소스레드가 속한 프로세스 제어 블록(PCB)에 대한 주소
^다른 TCB에 대한 주소프로세스 내 다른 TCB들을 연결하기 위한 링크
^블록 리스트/준비 리스드 등입출력을 대기하고 있는 스레드들을 연결하는 TCB 링크, 준비 상태에 있는 스레드들을 연결하는 TCB 링크(스레드 스케줄링 시 사용) 등
프로세스와 스레드 관리

준비 리스트와 블록 리스트

Ready 상태의 스레드와 Blocked 상태의 스레드를 관리하기 위해, 준비 리스트(ready thread list)블록 리스트(blocked thread list) 의 두 링크드 리스트로 TCB들을 연결하여 관리한다.

3.5 스레드 컨텍스트 스위칭

현재 CPU가 실행 중인 스레드를 중단시키고 CPU에게 새 스레드를 실행시키는 과정

스레드 스위칭이 발생하는 경우

시스템 호출이나 인터럽트 서비스를 실행하는 도중에만 이루어짐

  1. 스레드가 자발적으로 다른 스레드에게 양보하는 경우(시스템 호출 내)
    • 스레드가 직접 yield()로 양보
    • sleep() 또는 wait() 등의 시스템 호출한 경우
  2. 스레드가 I/O 작업을 요청하는 시스템 호출 시 블록되는 경우(시스템 호출 내)
  3. 스레드가 타임 슬라이스를 소진한 경우(인터럽트 서비스 루틴 내)
  4. I/O 장치로부터 인터럽트가 걸린 경우(인터럽트 서비스 루틴 내)

스레드 스위칭이 이루어지는 위치

시스템 호출을 처리하거나 인터럽트 서비스 루틴의 실행 도중 커널 코드에서 이루어진다.

스레드 스위칭 과정

  1. CPU 레지스터 저장 및 복귀
  2. 커널 정보 수정 TCB-A와 TCB-B에 스레드의 상태 정보와 CPU 사용 시간 등을 수정하고, TCB 리스트를 조작

3.6 컨텍스트 스위칭의 오버헤드

동일한 프로세스의 다른 스레드로 스위칭 하는 경우

  1. 컨텍스트 저장 및 복귀 시간
  2. TCB 리스트 조작 시간
  3. 캐시 플러시 및 채우기 시간

다른 프로세스의 스레드로 스위칭되는 경우

주소 공간이 완전히 바뀌는 큰 변화가 일어난다. 이에 따라, 앞서 동일한 프로세스의 스레드로 스위칭되는 오버헤드에다 다음 2개의 시간이 추가된다.

  1. 메모리 관련 오버헤드
    • 프로세스의 논리 주소를 물리 주소로 매핑하는 MMU(Memory Management Unit) 장치 내에 들어 있는 이전 맵 테이블(페이지 테이블)을 제거하고 새로운 프로세스의 맵 테이블을 적재, 이 과정에서 페이지 폴트 발생 가능
    • CPU 내에 현재 프로세스의 TLB(Translation Look-aside Buffer, 고속 주소 변환 장치)를 모두 비우고, CPU에 새 프로세스의 TLB를 채우는 작업이 필요
  2. 추가적인 캐시 오버헤드
    • 프로세스가 바뀌기 때문에 현재 CPU 캐시에 담긴 프로세스의 코드와 데이터도 무효화(cache invalidate) 시켜야 한다.
    • 이후 새 프로세스의 스레드가 실행을 시작하면 CPU 캐시에 계속 캐시 미스(cache miss)가 발생하고 캐시가 채워지는 과정이 진행

스레드 스위칭 오버헤드 줄이기

  • 프로세스를 특정 CPU 코어에 배치하여, CPU 코어가 여러 프로세스에 걸쳐 스레드를 실행하지 않도록 함
  • CPU와 TCB 사이에 칸텍스트를 이동하는 작업을 없애기 위해 CPU에 스레드 별로 레지스터 셋을 따로 두어, 컨텍스트 스위칭 시 현재 실행 중인 스레드의 CPU 레지스터들을 TCB에 저장하지 않는 방법을 사용하기도 함

4 커널 레벨 스레드와 사용자 레벨 스레드

4.1 커널 레벨 스레드와 사용자 레벨 스레드

커널 레벨 스레드

TCB가 커널 코드에 의해 커널 공간에 만들어지고 커널에 의해 스케줄 되는 스레드이다.

사용자 레벨 스레드

스레드 라이브러리에 의해, 사용자 공간에 생성되고, 관리되고, 스케줄돠는 스레드를 사용자 레벨 스레드라고 한다.

4.2 커널 레벨 스레드와 사용자 레벨 스레드 사례

4.3 사용자 레벨 스레드의 유용성

커널 레벨 스레드는 컨텍스트 스위칭에 많은 시간이 소요되어 시스템 성능을 떨어뜨리는 원인이 되었다. 이를 해결하기위해 커널의 도움 없이 스레드를 만들고 스케줄링할 수 있는 스레드 라이브러리가 개발되고 그와 함께 사용자 레벨 스레드가 도입되었다.

4.4 사용자 레벨 스레드와 커널 레벨 스레드의 비교

항목사용자 레벨 스레드커널 레벨 스레드
정의스레드 라이브러리에 의해 스케줄되는 스레드커널에 의해 스케줄되는 스레드
구현스레드 라이브러리에 의해 구현되고 다루어짐커널에 의해 구현, 커널 API 필요
스레드 스위칭사용자 모드에서 스레드 라이브러리에 의해 실행커널 모드에서 커널에 의해 실행
컨텍스트 스위칭 속도커널 레벨 스레드보다 100배 이상 빠르다고 알려짐커널 내에서 상당 시간 지연
멀티스레드 응용프로그램스레드 라이브러리를 이용하여 작성하기 쉽고, 스레드 생성 속도 빠름시스템 호출을 이용하여 스레드 생성, 스레드 생성 속도 느림
이식성(protability)운영체제 상관없이 작성 가능하므로 높은 이식성, 스레드를 지원하지 않는 운영체제에서도 가능스레드를 생성하고 다루는 시스템 호출이 운영체제마다 다르므로 이식성 낮음
병렬성(parallelism)멀티 CPU 컴퓨터나 멀티 코어 CPU에서 멀티스레드의 별렬처리 안 됨높은 병렬서으 커널 레벨 스레드들이 서로 다른 CPU나 서로 다은 코너에서 별렬 실행 가능
병렬성의 종류concurrency(동시성)parallelism(병렬성)
블록킹(blocking)하나의 사용자 레벨 스레드가 시스템 호출 도중 입출력 등으로 인해 중단(Blocked)되면 프로세스의 모든 사용자 레벨 스레드가 중단됨하나의 커널 레벨 스레드가 시스템 호출도중 입출력 등으로 인해 중단(Blocked)되어도 해당 스레드만 중단
커널 부담없음커널 코드의 실행 시간 증가, 시스템 전체에 부담
스레드 동기화스레드 라이브러리에 의해 수행시스템 호출을 통해 커널에 의해 수행
관리의 효율성커널 부담 없음커널 부담
최근 경향멀티 코어 CPU에 적합하지 않아 줄고 있는 추세멀티 코어 CPU에서 높은 병렬성을 얻을 수 있어 많이 사용되는 추세

5 멀티스레드 구현

5.1 배경

응용프로그램에서 생성된 사용자 레벨 스레드의 코드가 CPU에 의해 실행되려면, 궁극적으로 커널에 의해 스케줄되어야 한다. 커널은 커널 레벨 스레드밖에 모르기 때문에, 필연적으로 사용자 레벨 스레드와 커널 레벨 스레드가 연계(매팽, mapping)되어야 한다. 대표적으로

  • N:1
  • 1:1
  • N:M 3가지 모델이 있다. 위 3가지 모델은 어디까지나 이론이며 이들의 실현은 스레드 라이브러리와 스레드를 다루는 커널의 구현에 달려있다.

5.2 N:1 매핑

하나의 응용프로그램(프로세스)에 속한 모든 사용자 스레드를 하나의 커널 스레드로 매핑하는 개념 N개의 U-TCB를 1개의 TCB에 매핑 사용자 레벨 스레드와 커널 레벨 스레드의 N:1 매핑(3:1 매핑) 3개의 사용자 레벨 스레드가 1개의 커널 레벨 스레드 TCB4 공유

장점

단일 코어 CPU에서 멀티스레드 응용프로그램이 실행되는 속도가 전반적으로 빠르다.

단점

별렬성을 얻을 수 없다 응용프로그램 내에 한 사용자 스레드가 시스템 호출을 시행하여 입출력을 기다리는 등의 이유로 블록 샅애가 되면, 나머지 사용자 스레드 모두 실행시킬 수 없어 응용프로그램의 실행이 중단되는 문제점이 있다.

5.3 1:1 매핑

사용자 레벨 스레드 하나당 커널에 의해 스케줄 가능한 엔터티 즉 TCB 하나를 연계시킨다. 사용자 레벨 스레드와 커널 레벨 스레드의 1:1 매핑

장점

구현이 쉽다. 높은 병렬성을 제공한다. N:1 모델과 달리, 스레드 중 하나가 블록 상태가 되어도, 응용프로그램 전체가 중단되는 일은 없다.

단점

운영체제와 커널의 비용 부담이 크다.

5.4 N:M 매핑

응용프로그램에 속한 사용자 레벨 스레드 중 N개의 스레드들을 M개의 커널 레벨 스레드에 연계시키는 방법 사용자 레벨 스레드와 커널 레벨 스레드의 N:M 매핑(3:2 매핑)

장점

1:1 매핑에 비해 커널 레벨 스레드의 개숙가 적어 커널의 부담이 줄어들었다.

단점

매핑과 스케줄링 과정이 복잡하여 현대의 운영체제에서는 거의 사용하지 않는다.

6 멀티스레딩에 관한 이슈

6.1 프로세스와 스레드 리뷰

  1. 프로세스는 여러 스레드들의 코드와 데이터, 힙, 스택이 생성되고 실행되는 주소 공간이며 공유 공간이다. 그러므로 스레드들끼리 프로세스의 데이터, 힙, 스택 영역을 이용하여 쉽게 정보를 주고받을 수 있다.
  2. 프로세스는 운영체제가 응용프로그램을 적재하는 단위이며, 실행 단위는 스레드이다.
  3. PCB에 저장된 정보를 환경 컨테스트(environment context)라고 하고, TCB에 저장된 정보를 실행 컨텍스트(execution context)라고 하는데, PCB에 저장되는 내용은 모든 스레드가 공유하는 프로세스의 정보가 저장되고, TCB에는 현재 실행중인 실행 단위의 정보만이 저장되기 때문이다.
  4. 동일한 프로세스 내에 속한 스레드 사이의 컨텍스트 스위칭은 서로 다른 프로세스에 걸쳐 있는 스레드들 사이의 컨텍스트 스위칭 속도보다 빠르다.
  5. 프로세스에 속한 모든 스레드가 종료할 때 프로세스도 종료된다. 프로세스를 종료시키는 exit()시스템 호출이 실행되면 모든 스레드가 종료된다.

6.2 멀티스레딩으로 응용프로그램을 작성하는 장점

높은 실행 성능

사용자에 대한 우수한 응답성

서버 프로그램의 우수한 응답성

시스템 자원 사용의 효율성

응용프로그램 구조의 단순화

작성하기 쉽고 효율적인 통신

6.3 멀티스레딩에 있어 주의할 점

  1. 프로세스에 여러 개의 스레드가 있을 때, 한 스레드가 fork() 시스템 호출을 이용하여 자식 프로세스를 생성하는 경우 자식 프로세스에서는 fork()를 호출한 스레드만 살아 main 스레드가 만들고, 나머지는 종료
  2. 스레드가 exec() 시스템 호출을 실행하는 경우 모든 스레드를 종료시키고 프로세스의 주소 공간에 새로운 응용프로그램을 적재하여 실행시킨다.
  3. 스레드 사이의 동기화 문제

Ref.

황기태, “명품 운영체제”, (주)생튼출판사(2021)