Chap 4. Threads

hgh1472·2024년 7월 11일
0

운영체제

목록 보기
4/11

프로그램 크기가 어느정도 이상이 되면 하나의 프로세스로 프로그램을 작성하는 경우는 거의 없다.

동시에 작동을 해야하는 상황도 존재하고 실행을 빠르게 하기 위해 쓰레드를 사용한다.

  • ex) 인터넷 검색

프로세스

  • 자원 할당
  • 프로텍션

쓰레드

  • 실행

멀티 쓰레드 시스템이란 프로세스 안에 실행이 가능한 여러 개의 쓰레드들을 서포트 하는 시스템

프로세스 안에 들어있는 쓰레드들은 자원을 공유한다. 따라서 메모리 안의 명령어나 데이터들에 접근이 가능하다. 단, A 프로세스의 공간을 B 프로세스가 접근할 수는 없다.

Process in a multithreaded environment

  • 자원 할당의 단위이면서 프로텍션의 단위
  • 프로세스 이미지 관리
    • Virtual Adress Space = 프로그램 코드와 데이터
  • 프로세서, 다른 프로세스, IO 리소스에 대한 프로텍션의 단위

Thread

  • 실행 상태(Running, Ready, Blocked…)
  • 실행을 멈출 때 쓰레드 컨텍스트(중단 지점에 CPU 안에 있는 레지스터 값) 저장
  • 스택은 다 각자 관리
  • 스태틱 변수
    • 프로세스 기반 시스템에서 전역변수와 스태틱 변수는 데이터 영역이다. 하지만 멀티 쓰레딩 시스템에서 스태틱 변수는 쓰레드마다 서로 다른 방식으로 업데이트 할 수 있다. 따라서 스태틱 변수는 쓰레드 단위로 관리된다.

한 프로세스의 메모리, 리소스들은 공용으로 접근할 수 있다.

  • 10개의 쓰레드 ⇒ 프로그램 코드의 카피 10개 필요 X
    • 프로그램 코드와 데이터는 바뀌지 않는 부분이므로 각각 쓰레드들이 같은 코드의 서로 다른 영역을 실행시킨다.
  • 스택은 실제로 실행을 하면서 쌓아가는 부분
    • 각 쓰레드마다 관리
  • PCB와 TCB
    • 멀티 쓰레드 시스템에서는 프로세스 기반의 PCB를 둘로 나누어서 PCB, TCB로 관리
    • PCB
      • 프로세스 ID 정보
      • Process Control Information 중 자원과 관련된 정보(메모리 관련 정보,리소스 ownership, utilization)
      • 프로세스의 권한(시스템 안의 자원을 access할 수 있는지 없는지)
      • 자료 구조
    • TCB
      • Processor State Information(프로그램이 실행을 하다가 중단됐을 때 CPU안의 레지스터 값들)
      • Process Control Information 중 실행과 관련된 정보(스케쥴링, state information)
      • 자료구조

쓰레드를 사용하는 이유

Remote Procedure Call : A 시스템에서 B 시스템으로 함수 호출 요청을 보내면 실제 그 함수에 대한 작업은 B 시스템에서 실행하고 결과값만 A 시스템에 있는 프로세스로 되돌려준다.

그림처럼 두 개의 쓰레드를 만들어서 첫 번째 쓰레드가 첫번째 작업 요청을 한다음 기다리는 동안, 두번째 쓰레드를 만들어서 두번째 쓰레드가 다시 작업 요청을 하고 기다린다.

이처럼 2번 기다리는 것이 아닌 한 번만 기다리기 때문에 훨씬 빠르게 작업할 수 있다.

예시로 웹페이지에서 각각의 정보들은 여러 개의 쓰레드로 구성되어 처리된다.

Benefits of Threads

여러 개의 프로세스를 만들지 않고 여러 개의 쓰레드를 만드는 이유

  • 새로운 프로세스를 만드는 것보다 새로운 쓰레드를 만드는 것이 시간이 적게 걸린다.
    • 프로세스를 카피하면 더 많은 양을 복사해야 한다.
  • 프로세스를 종료하는 시간보다 쓰레드를 종료하는 시간이 더 적게 걸린다.
  • 스위칭을 할 때 시간이 적게 걸린다.
    • 프로세스 안에서 스위칭을 하면 PCB는 수정할 필요없고 TCB만 저장한다.
  • 한 프로세스 안에 있는 쓰레드들은 메모리, 파일, 전역변수와 같은 것들을 공유한다.
    • 커널을 포함하지 않고 통신 가능하다. 커널이 끼어들지 않고 전역변수를 사용하므로 OS가 끼어들 필요가 없다.

Thread States

쓰레드는 5개의 상태를 갖는 상태도로 표현할 수 있다.

  • Spawn
  • Ready
  • Running
  • Blocked
  • Finish

5개 상태를 가지는 프로세스 상태도와 동일한 그림이다.

프로세스 상태 vs 쓰레드 상태

Suspend

Suspend 상태는 쓰레드의 상태가 될 수 없다. Suspend 상태는 프로세스가 중단되고 하드디스크의 swapping area로 쫓겨난 상황이다. 그런데 쓰레드 하나만 Suspend 시켜서 하드디스크로 쫓아낼 수 없다.

OS가 프로세스를 Suspend 시키면 쓰레드들의 상태와 관계 없이 프로세스를 Suspend 시킬 수 있다.

Termination

Termination도 프로세스 단위의 상태이다. OS가 프로세스를 Terminate한다면 모든 쓰레드들은 다 같이 Terminate가 된다.

멀티 쓰레드 구현 방식

User-Level Thread

시스템의 OS가 멀티 쓰레딩을 지원하지 않는다.

유저 레벨의 쓰레드 라이브러리에서 유저 프로그램을 만들어내는 쓰레드를 관리한다. 유저 레벨 쓰레드 라이브러리를 이용해서 멀티 쓰레딩 방식으로 작업한다.

원인에 따라 쓰레드가 Block 되면 다른 쓰레드를 실행시킬 수 있다.

쓰레드 라이브러리 안에는 쓰레드 생성, 종료, 통신, 스케쥴링, 컨텍스트 저장, 리스토어 하는 작업을 한다. ⇒ 쓰레드 스위칭을 할 때 커널 작업을 하는게 아니라, 유저 레벨 쓰레드 라이브러리에서 작업 ⇒ 스위칭, 스케쥴링 같은 작업에 시간이 적게 소요된다.

OS는 프로그램 하나가 실행된다고 생각한다.

쓰레드를 여러개 만든 경우에 커널에서는 프로세스로 관리된다. 하나의 프로세스로 쓰레드 라이브러리에서만 쓰레드가 여러 개이다.

쓰레드 1, 쓰레드2 는 쓰레드 라이브러리에서 저장된 상황이다.

a

프로세스 B는 커널에서 러닝 상태이고, 1번 쓰레드는 레디 상태, 2번 쓰레드가 실행하고 있는 상태이다.

2번 쓰레드를 실행하고 있고, 커널에서는 프로세스 B가 실행을 하고 있다고 인식한다.

a → b

2번 쓰레드가 IO 작업 호출 → 시스템 입장에서 프로세스 B가 입출력 요청 → 프로세스 B Block, 하지만 쓰레드의 상태는 그대로 러닝

💡 쓰레드 2번이 Running 상태로 표시되어있지만 실제로는 Blocked 상태인 이유

쓰레드 라이브러리에서 쓰레드의 상태를 바꿔야 하는데, 프로세스 B가 멈췄기 때문에 바꿀 수 없다. 즉, 러닝 상태로 보이는 것 뿐이지 러닝하고 있다는 것이 아니다.

a → c

쓰레드 2가 타임아웃이 되는 순간 타이머 인터럽트가 걸리고 커널은 프로세스 B를 레디 상태로 옮긴다. → 쓰레드의 상태를 바꿀 수 있는 시간이 없다.

a → d

쓰레드 2가 동기화 때문에 Blocked 상태가 된다면 프로그램 전체를 멈출 필요가 없다. 2번 쓰레드를 멈추고 1번 쓰레드를 실행시킨다. → 스케쥴링 작업은 쓰레드 라이브러리에서 진행

쓰레드 라이브러리에서 진행하기 때문에 커널에서는 보이지 않는다. 따라서 CPU가 여러 개 있다 하더라도 커널 레벨에서는 프로세스 1개 이기 때문에 1, 2번 쓰레드를 서로 다른 곳에서 실행시킬 수 없다.

Kernel-Level Thread

프로그램에서 쓰레드를 생성할 때마다 실제 커널에서 쓰레드가 하나씩 생긴다.

쓰레드랑 관련된 모든 것은 커널에서 관리한다.

멀티 프로세서 시스템으로 여러 개의 프로세서를 갖는 시스템이면, 한 프로세스 안에서 만들어진 여러 개의 쓰레드가 여러 CPU에서 동시에 실행될 수 있다.

각각의 쓰레드들은 완전 독립적으로 실행된다.

쓰레드 스위칭도 커널에서 관리한다.

커널 레벨 쓰레드 간에도 커널을 통하지 않고 효율적인 통신이 가능하다. 멀티 쓰레딩 시스템에서는 프로세스 안에 있는 데이터 공간을 공유하기 때문이다.

따라서 커널 레벨 쓰레드나 유저 레벨 쓰레드 둘 다 비슷한 시간이 걸린다.

User Level Thread vs Kernel Level Thread

  • User Level
    • 쓰레드 스위칭할 때 실제로 커널 작업 X, 쓰레드 라이브러리에서 작업한다. 즉, 실제 스위칭이 아니다.
    • 어떤 OS에서나 다 실행시킬 수 있다.
  • Kernel Level
    • 여러 개의 CPU를 가진 시스템에서 동시에 실행할 수 있다.
    • 쓰레드 하나가 Blocked 돼도 다른 쓰레드를 계속 실행할 수 있다.
    • OS도 멀티 쓰레딩을 이용하여 작성된다. ⇒ OS의 속도가 빠르다.

멀티 쓰레딩 시스템의 OS가 프로세스 기반 시스템의 OS보다 더 빠른 것은 아니다.

Combined Approach

여러 개의 유저 레벨 쓰레드를 작거나 같은 레벨로 실제 커널 레벨 쓰레드로 매핑한다.

커널 레벨 쓰레드를 사용하기 때문에 멀티 프로세서 시스템에서는 동시에 쓰레드를 실행시킬 수 있다.

쓰레드 라이브러리를 사용하기 때문에 어떤 경우에 커널의 도움 없이 스케쥴링, 스위칭을 한다.

불필요한 쓰레드 생성을 막기 위해 쓰레드 라이브러리에서 걸러낸다.

Relationship Between Threads and Processes

  • 1:1 = 하나의 프로세스 안에 하나의 쓰레드만 실행. traditional한 UNIX 시스템
  • M:1 = 멀티 쓰레드 시스템. 윈도우즈, 솔라리스 등등
  • 1:M
    • 쓰레드가 다른 프로세스로 카피해간다. T1이라는 쓰레드가 P1 프로세스 안에서 실행 하다가 P2, P3 프로세스로 옮겨가면서 실행한다. TCB와 유저 스택을 카피해서 P2에 복사해서 P1 → P2 로 이동한다.
    • 하나의 컴퓨터 시스템안에 다 있는 경우 의미가 없다. A, B, C 컴퓨터가 네트워크로 연결 되어 있고 각각의 컴퓨터에 프로세스가 존재하고 쓰레드가 A → B → C 컴퓨터로 옮겨가며 작업을 하는 경우이다.
  • M:N

Windows Processes and Threads

  • 마이크로 커널 아키텍쳐
  • 멀티 쓰레딩 시스템
    • 커널 레벨 쓰레드를 지원
    • 모든 쓰레드들이 커널에서 생성, 관리, 스케쥴링된다.
  • Object-Oriented 디자인
    • 프로세스 오브젝트 안에 쓰레드 오브젝트들이 있다.
  • 프로세스는 쓰레드 하나를 가진 채로 만들어진다.
  • Ready 큐에 줄을 서는 것은 프로세스가 아닌 쓰레드이다.
  • 프로세스와 쓰레드는 전부 빌트인 동기화 기능들을 가지고 있다.

Windows Process Object Attributes

프로세스는 실제 실행이 아니라 프로세스 안에서 실행되는 쓰레드들의 자원 사용과 관련된 부분들을 관리한다.

  • Execution time, I/O Counters, VM operation counters : 프로세스안에 속해있는 쓰레드들이 실행 하면서 CPU를 얼마나 썼는지, I/O 작업을 몇 번 했는지, 메모리 관련 작업은 몇 번 했는지 등을 나타낸다.
  • Quota limits : 시스템 안에 있는 자원들을 프로세스에 속한 쓰레드들이 얼만큼 사용할 수 있는지 최대치를 표시한다.
  • priority : 스케쥴링에서의 우선순위. 프로세스를 실행시키는 것이 아니지만 프로세스에 우선순위가 있다. 유저 프로세스, OS 프로세스가 있는데 OS 프로세스 내에서도 어떤 작업을 하는지에 따라 우선순위가 나뉜다. I/O 작업 프로세스는 메모리 작업 프로세스보다 우선순위가 뒤쳐진다.
    • Base priority : 프로세스가 어떤 프로세스인지에 따라 주어지는 기본 우선순위. 쓰레드는 이 값을 기준으로 자신의 우선순위를 결정한다.
  • Default processor affinity : 프로세스 안에 쓰레드가 실행될 CPU의 집합. 윈도우 시스템은 멀티 프로세서 시스템을 가정한다. 따라서 10개의 CPU 중 주로 실행하는 CPU를 정해놓는다.

Windows Thread Object Attributes

  • Thread Context : 쓰레드가 실행하다가 중단했을 때 CPU 안에 있는 레지스터 값. Process State Information에 해당한다.
  • Priority : 쓰레드는 레디 큐에 줄을 서고 OS는 쓰레드 중에서 우선순위가 가장 높은 쓰레드를 골라간다. Priority 는 스케쥴링 우선순위를 의미한다.
    • Base priority : 프로세스의 Base priority ±2 값을 가진다. 자신이 만든 쓰레드 중에서 더 급한 작업이 있고 아닌 작업이 있다.
    • Dynamic priority : 시스템 안에서 작업하다보면 OS가 쓰레드의 우선순위를 높여줄 수도 있고, 낮춰줄 수도 있다. 하지만 Base priority보다 낮출 수는 없다. 실제 Dynamic priority에 의해 쓰레드의 우선순위가 결정된다.
  • Thread processor affinity : 쓰레드가 작업되기를 원하는 CPU의 번호들. process affinity의 집합의 부분집합이거나 같다.

💡선호하는 CPU가 존재하는 이유

각각의 CPU는 각각의 캐시를 가지고 있다. 캐시가 존재하는 CPU에서 작업을 하는것이 훨씬 유리하다.

Windows Thread States

  • Ready : 실행할 준비가 돼있는 상태. 쓰레드의 상태는 레디 상태에서 시작한다.
  • Standby : Ready에서 Running 상태로 가기전에 Standby를 거친다. Standby는 다음에 실행할 쓰레드로 뽑혔다는 뜻이다. CPU가 작업을 중단했을 때 다음에 실행할 쓰레드를 선택하는 것이 아니라, 중간중간 OS가 시간이 남을 때 다음에 실행할 쓰레드를 미리 선택해서 Standby 상태로 만들어놓는다. 멀티 프로세서 시스템에서 큐에는 1개의 CPU만 접근해야 한다.(동기화 문제) 따라서 OS가 작업을 하다가 시간이 빌 때 미리 선택을 해서 빠르게 스위칭할 수 있게한다.
  • Running : running을 하다가 타임아웃이 되면 Ready 상태로 돌아간다.
    • Running → Ready : 타임아웃, 우선순위가 더 높은 쓰레드 존재. 우선수위가 더 높은 쓰레드가 나타나면 무조건 CPU를 뺏긴다.
  • Terminate : 프로세스의 Exit과 같다.
  • Waiting : Block과 Suspend를 구분하지 않는다. 여기서의 Suspend는 swap out이 아니라, 타이밍 또는 동기화의 경우에 의해 한동안 작업하지 않는 경우를 말한다. 하나의 쓰레드가 Suspend 한다고 해서 쓰레드를 포함하는 프로세스 전체를 swap out 시키지 않는다. 프로세스 안에는 실행을 하는 다른 쓰레드들이 포함되어 있을 수 있기 때문이다.
    • Waiting → Ready : blocking이나 suspension이 풀렸는데 쓰레드가 메인 메모리에 들어있는 경우
    • Waiting → Transition : blocking 문제는 해결이 됐지만, swapping area에 들어있는 경우
    • 프로세스의 swap과 쓰레드의 상태가 별개로 관리되기 때문이다.

Solaris Processes and Threads

Combined Approach

Solaris

프로세스 안에 유저 쓰레드가 있다.

유저 쓰레드는 유저 레벨의 쓰레드 라이브러리에서 만들어진 쓰레드이다. 유저 쓰레드는 프로세스 안에 들어있지만, 커널에서는 볼 수 없다.

커널에서는 LWP(Lightweight Process)를 볼 수 있다. 실제로 커널에서 관리되는 자료구조는 프로세스 안의 LWP로 관리된다.

커널 쓰레드는 프로세스 공간이 아니라 커널 공간에서 관리되는 쓰레드이다.

LWP는 커널 레벨 쓰레드이다.

Solaris의 프로세스 이미지 안에는 LWP를 위한 스택 공간이 존재한다. 커널 레벨 쓰레드가 여러 개 지원되므로 각각의 커널 레벨 쓰레드를 위한 스택 공간이 있어야 한다.

LWP = 프로세스 공간에서 관리되는 쓰레드

kernel thread = 커널 공간에서 관리되는 쓰레드. OS가 만들어놓은 비어있는 쓰레드이다. LWP가 만들어질때 마다 kernel thread와 매핑시키거나 kernel thread안에 담아서 OS에서 관리한다.

Solaris에서 LWP와 매핑되지 않는 커널 쓰레드들이 있다. 이 쓰레드는 시스템 작업을 하는 커널 코드이다.

시스템 작업을 하는 커널 코드들이 담겨있는 커널 쓰레드는 LWP와 매핑되지 않은채로 시스템 안에 존재한다.

시스템 작업을 하는 커널 쓰레드와 LWP가 매핑되어 있지 않다면, 커널 쓰레드와 유저 프로세스가 서로 다른 방식으로 관리된다. 그렇기 때문에 LWP를 빈 커널 쓰레드 안에 넣어서 관리한다. OS 입장에서는 전부 다 커널 쓰레드로 관리되는 것처럼 관리한다.

유닉스에서 발전된 시스템이기 때문에 커널작업을 하는데 프로세스 스위칭을 하지 않기 위해 커널 작업만 하는 쓰레드가 존재한다.

Solaris Thread States

  • IDLE : 아무런 LWP와 매칭되지 않은 비어있는 상태의 쓰레드
  • ONPROC : 러닝 상태
    • ONPROC → RUN : preempt
      • 타임아웃
      • 우선순위가 더 높은 쓰레드
  • SLEEP : Blocked 상태
    • SLEEP → RUN : 기다리던 이벤트 발생
  • STOP : 실행하다가 Suspend된 상태
    • 쓰레드의 Suspend = 동기화, 타이밍
    • 해결되면 STOP → RUN
  • ZOMBIE : UNIX의 좀비상태와 비슷하다.
  • FREE : 자원들을 전부 반납하고 마지막 테이블에 남아 있는 상태
  • PINNED : 인터럽트가 발생한 상태. 핸들링한 후 다시 실행할 수도 있기 때문에 다시 실행할 수 있다면 다시 실행한다. 불필요한 스위칭을 하지 않도록 PINNED 상태가 존재한다. 인터럽트가 발생하면 PINNED 상태로 일시정지 시켜놓고 커널 작업을 한 후 다시 실행해도 된다면 이 프로세스를 다시 실행한다.

Linux Tasks

리눅스는 프로세스보다는 Task라는 말을 주로 사용한다.

멀티 쓰레딩 시스템 X

Task는 다른 Task와 User Address Space(프로그램 코드와 데이터 영역)를 공유할 수 있다.

실행을 각각 해야하기 때문에 별도의 스택을 가지고 있어야 한다.

쓰레드는 스택과 TCB만 포함하고 있지만, Task는 다른 Task가 공유한 프로그램 코드와 데이터를 분명히 가지고 있다.

Linux Threads

  • 리눅스에서는 쓰레드랑 프로세스의 구분이 없다.
  • 유저 레벨 쓰레드는 커널 레벨 프로세스와 매핑된다. 커널에서는 이걸 쓰레드로 관리하지 않는다. 즉, 리눅스 커널 입장에서 보면 프로세스로 관리하고 쓰레드가 없다.
  • 새로운 프로세스는 기존에 있던 프로세스를 복사해서 만들어진다.
  • 새로운 프로세스는 리소스를 공유하는 클론으로 만들어질 수도 있다. ⇒ 유닉스 프로세스와 다른점
    • 클론은 리소스를 공유하는 별개의 스택 공간을 만들어낸다. 클론 명령을 사용하면 새로운 task는 기존 task와 PCB, User Address Space를 공유한다. 그리고 스택과 TCB(실행과 관련된 정보)만 따로 만들어 낸다. 즉, 실행과 관련된 정보들만 따로 만들어낸다.
    • 멀티 쓰레딩과 같은 효과를 낸다. 하지만 OS 입장에서는 프로세스가 늘어난 것이다.

Linux Process/Thread Model

  • Executing → Stopped : 동기화에 의해 Suspension을 해야 하는 경우
  • Stopped → Ready : 동기화 작업이 완료
  • Blocked 상태
    • Uninterruptible : 기다리고 있는 이벤트가 아니고 다른 시그널이 와도 깨어나지 않는다.
    • Interruptible : 기다리고 있는 이벤트가 아니라 다른 시그널이 와도 꺠어난다.

0개의 댓글