Post

프로세스와 스레드

1. 프로세스

실행 중인 프로그램이라고 할 수 있다.

다음과 같은 C언어 프로그램이 있다고 가정해보자

1
2
3
4
5
6
7
8
9
10
11
12
# include <stdio.h>

int glob=3;  //global or static variable 

void Function_A (void)
{   
  int loc, *dynamic; 
  dynamic = malloc(1383);
  printf(“…. 
  ...
  return 0;
}

유닉스에서 프로그램이 구동되면 같은 소스라고 하더라도 다른 메모리 영역에 배치되게 된다. 여기서 glob 전역 변수는 정적 데이터와 함께 data 영역에 배치되고 loc와 같은 변수는 Stack 영역에, dynamic과 같은 동적 할당된 영역은 heap 영역에 그리고 prinf와 같은 코드는 Text 영역에 배치된다. (리눅스와 윈도우도 비슷하다. 하지만 맥의 경우 조금 다르다.)

그림으로 나타내면 다음과 같다.

img.png

이러한 하나의 구조 전체를 프로세스라고 한다. 이러한 프로세스는 페이징을 통해 전체가 메모리에 쪼개져서 올라가게된다. (페이징은 OS 카테고리의 페이징과 세그멘테이션 부분을 참고하기 바란다)

이런 프로세스를 관리하기 위해서는 프로세스에 대한 정보가 필요하며 이러한 정보는 PCB라는 구조체에 담겨서 관리되게 된다.

PCB에는 다음과 같은 정보가 담기게 된다.

  • PID (Process Identifier)
  • Priority
  • Waiting Event
  • State
  • Location of image in disk
  • Location of image in memory
  • open files
  • directory
  • state vector save area
  • tty
  • parent, child process
  • execution time

하지만 이러한 PCB는 너무 용량이 많기 때문에 메모리에 통째로 올려둘 경우 메모리 용량을 너무 많이 차지 않다. 그래서 프로세스 영역과 유저 영역으로 나누어 프로세스 영역은 메모리에 상주 시키고 유저 영역은 DISK에 넣어두어 필요할때 불러쓴다.

위의 PCB를 보면 알겠지만 프로세스 단위로 리소스를 할당하게 된다. 그렇기 때문에 프로세스는 리소스의 할당 단위이다. 또한 서로 다른 프로세스가 서로의 영역을 침범하지 못하게 메모리 보호가 걸려있다. 이 역시 프로세스 단위로 보호가 걸려있기 때문에 보호의 단위이기도 하다.

2. 스레드(Thread)

스레드는 사실상 프로세스의 하위 개념이다. 프로세스가 별도의 스레드를 만들지 않았다면 프로세스가 한 개의 스레드로 돌아가는 것이다. 그렇기 때문에 스레드는 작업의 단위이며 작업의 단위이기 때문에 곧 스케줄링의 대상이 된다.

다수의 스레드가 한 개의 프로세스에서 돌아간다고 가정하면 스레드 간에 코드와 데이터, heap은 공유되나 스레드의 스택이나 레지스터의 상태는 공유되지 않으며 별도의 프로그램 카운터(PC) 값을 갖는다. 그렇기 때문에 어떤 데이터를 공유하며 다른 작업을 요할 경우 멀티 스레드로 구동하는게 현명한 방법이다.

1) Multithreading

a. Multithreading의 종류

Multithreading에는 두 가지가 있다.
다중 코어 일 때의 Multithreading과 단일 코어 일 때의 Multithreading이다.

  • 다중 코어에서 Multithreading
    각 코어마다 thread를 실행하게 되므로 실질적인 병렬화(Parallelism)이다. 당연히 성능은 올라간다.

  • 단일 코어에서 Mutlthreading
    어차피 코어 하나에서 thread 들이 돌게 되는데, 이 경우 동시 실행(Concurrency)에 가깝다. 한 개의 core에서 time slice를 분할해서 각 thread가
    사용하게 되는데 이는 곳 동시에 실행하는 것과 마찬가지이다. 이 경우도 성능이 올라가게되는데, I/O와 같은 작업이 필요한 thread가 생기면 잠시 wait하고
    다른 thread가 실행되기 때문에 실질적으로 성능이 올라간 것과 같이 작동한다.

b. User-level vs Kernel-level thread

기본적으로 Mutlthreading는 아래와 같은 구조를 갖는다.

img.png

  • User-level thread는 User-level에서 구동되며 Thread library에 의해 관리된다.
  • Kernel-level thread는 Kernel-level에서 구동되며 Kernel에 의해 관리된다.

여기서 kernel의 역할이 매우 중요한데, 일단 다수의 kernel-level thread를 사용하기 위해서는 kernel에서 다중 스레드를 지원해야한다.
이러한 kernel을 multithreaded kernel이라고 하며, 현대의 대부분의 OS는 multithreaded kernel이다.
반면 예낫에 쓰던 MS-DOS와 같은 것들은 단일 커널 스레드로 구동하며 이를 non-multithreaded kernel이라고 한다.

Multithreaded kernel은 커널에서 user-level thread와의 mapping을 지원하는데 이 과정에서 many-to-one, many-to-many, one-to-one등 다양한 방식으로 지원하며, many-to-one의 경우 non-multithreaded kernel과 같다.

위와 같은 구조를 추상화하면 아래와 같이 그려볼 수 있다.

img_1.png

c. Thread Libraries

ⓐ User-level thread library

커널의 지원이 전혀 없이 user-level에서만 구동되는 라이브러리이다. 즉, 커널 스레드와 유저 스레드의 매핑이 전혀 없기 때문에 multithreaded kernel에서 구동해도 non-multithreaded kernel에서 구동하는 것과 동일하다.

ⓑ Kernel-level thread library

라이브러리가 커널의 지원을 받아 user-level thread와 kernel-level thread를 mapping 해준다. 라이브러리를 위한 코드와 데이터 구조가 커널 공간에 존재하며, API 호출은 대개 커널에 대한 시스템 호출(System Call)로 이어진다.

※ 주로 사용되는 Thread library 들

  • POSIX threads (Pthreads)
    Unix 계열 운영체제의 표준이며, C 프로그램과 링크하여 사용할 수 있다. 사용자 수준 또는 커널 수준 라이브러리 모두로 제공될 수 있는데, 기본으로 설치되어있는건 kernel level thread library이며 별도의 설치를 통해 user level thread library를 사용할 수 있다.

  • Windows threads
    Windows 시스템에서 제공하는 커널 수준 라이브러리로, 사용자 스레드와 커널 스레드가 일대일(one-to-one)로 매핑된다.

  • Java threads
    Java 언어 수준에서 지원되며, 일반적으로 해당 호스트 시스템(Windows나 Unix 등)에서 사용 가능한 스레드 라이브러리를 사용하여 구현된다.

참고 자료

  • Operating System Concept (written by Silberschatz, Galvin and Gagne)
  • 서강대학교 박성용 교수님 강의자료 - 병렬 분산 컴퓨팅
  • Peter S. Pacheco, An Introduction to Parallel Programming, Elsevier Inc. (Morgan Kaufmann), 2011, ISBN 978-0-12-374260-5
  • Gerassimos Barlas, Multicore and GPU Programming – An Integrated Approach, Elsevier Inc. (Morgan Kaufmann), 2015, ISBN 978-0-12-417137-4.
This post is licensed under CC BY 4.0 by the author.