전산학/운영체제

[OS] 프로세스와 스레드의 차이

lindeblad 2024. 10. 29. 22:34

프로세스 Recap:

프로세스는 프로그램의 실행을 의미하는 아주 기본적인 단위이다.

 

운영체제는 여러 프로세스를 독립적으로 운영하기 위해서 각 프로세스마다 다음의 정보가 존재한다. 이런 정보들은 프로세스 제어 블록(Process Control Block, PCB)이라는 단위로 운영체제에서 관리된다.

  • 메모리: 해당 프로그램이 읽기/쓰기를 하는 영역 (정확히는, 가상 메모리가 하드웨어 메모리에서 어떤 부분을 쓰는 지에 대한 정보)
    • 코드 세그먼트: 프로그램의 코드로부터 만들어진 명령의 나열
    • 데이터 세그먼트: 전역변수 등의 초기값들
    • 스택: 함수 실행 정보(호출 규약 참조)와 지역변수
    • 힙: 동적(malloc, ...)으로 할당된 공간
  • I/O 상태: 프로세스에 할당된 I/O 장치들의 상태
    • File Descriptor
      • 각 파일의 현재 읽기/쓰기 위치
      • 실제 해당 파일의 위치
    • I/O 버퍼: 파일의 읽기나 쓰기가 이루어지기 전에 사용되는 메모리
  • CPU 상태: 프로그램 카운터, 레지스터 등 프로그램을 실행하면서 CPU가 저장하는 정보

CPU의 코어 하나는 레지스터나 캐시 등이 하나의 프로세스를 위해 설계된 컴퓨팅 단위이기 때문에, 코어에서는 기본적으로 한 프로세스만 실행이 가능하다.

 

프로세스가 실행될 때 명령이 멈추고 I/O를 기다리는 상황이 자주 발생하는데, 이 때 운영체제 남는 CPU 자원을 활용하기 위해 현재 프로세스를 재우고(sleep), 다른 프로세스를 불러온다. 그러면서 프로세스를 실행하기 위해 필요한 정보인 명령, 메모리, I/O 상태, CPU 상태 등을 새로 다 불러와야 하는데, 이를 컨텍스트 스위치(context switch)라고 한다.

 

스레드

동시에 여러 프로세스를 실행할 때 I/O를 기다릴 때마다 컨텍스트 스위치를 하는 것은 비용이 많이 들고, I/O나 메모리에 대한 보호가 필요하지 않다면 PCB의 모든 정보를 굳이 교체할 필요는 없다. 예를 들어, 아주 많은 양의 요청을 처리하는 웹 서버를 가동한다면, 매번 요청이 올때마다 처리하는 정보를 새로운 프로세스를 만들면서까지 각 요청에 대한 독립성을 보장하는 것보다, 그냥 같은 I/O 상태와 메모리를 사용하면서 성능을 높이고자 할 수도 있다.

 

반면, 스레드는 한 프로세스 내에서 여러 개 존재할 수 있는 실행 단위로, I/O와 메모리의 보호를 포기하고 CPU의 상태만 교체하면서 훨씬 더 빠른 컨텍스트 스위치 속도를 가진다. 또한, 스레드끼리는 메모리가 공유되기 때문에 스레드 간의 통신이 프로세스 간의 통신보다 훨씬 쉽게 이루어지기도 한다.

 

스레드의 종류

그렇지만, 스레드는 어떻게 관리될까? 어떤 CPU의 코어에서 어떤 프로세스를 실행할지는 운영체제에서 결정하는데, 스레드는?

 

레지스터 등 CPU 상태만 바꾸면 된다면, 스레드라는 개념 자체는 운영체제의 개입(시스템 콜) 없이도 구현될 수 있다. 하지만, 현대의 스레드는 운영체제의 지원이 필수적이다. 지금까지는 프로세스 하나는 단일 코어에서 실행되고, 따라서 그 프로세스에서 소환된 스레드들은 그 프로세스가 점유하는 단일 코어에서 실행하는 맥락처럼 보인다. 하지만 실제로는 프로세스 사이에도 메모리를 공유하거나, 코어 사이의 커뮤니케이션을 하면서 여러 스레드들이 여러 코어에서 실행될 수도 있다.

 

따라서, 기본적으로 운영체제에 시스템 콜을 하여 스레드를 만들 수 있도록 지원하고(커널 레벨 스레드), 이렇게 만들어진 커널 레벨 스레드를 유저 레벨에서 관리하는 하위 개념의 스레드(유저 레벨 스레드)도 존재한다.

  • 유저 레벨 스레드
    • 사용자 레벨 라이브러리를 통해 구현된 스레드
    • 사용자 수준에서의 스레드는 보통 유저 레벨 스레드를 만들고, 스레드 라이브러리가 유저 레벨 스레드와 커널 레벨 스레드를 잇는다.
  • 커널 레벨 스레드
    • 커널이 직접 스레드와 관련된 모든 작업을 처리한다.
    • 프로세스처럼 운영체제에 의해 관리된다.
    • clone 시스템 콜로 만들 수 있다. 일반적인 함수 콜에 비해 시스템 콜이 느리기 때문에, 스레드 관련 명령에 시간이 오래걸린다.

유저 레벨 스레드를 커널 레벨 스레드에 어떻게 대응시킬 지에 대한 멀티스레딩 모델은 다음과 같다. 일반적으로 프로그래밍을 하면서 사용되는 스레드는 one-to-one 스레드에 해당한다고 보면 된다.

  1. Many-to-one
    • 여러 유저 스레드가 하나의 커널 스레드에 매핑된다.
    • 유저 공간에서 관리되기 때문에 생성과 스위칭이 빠르다.
    • 스케쥴링 알고리즘을 자유롭게 수정할 수 있다.
    • 코어가 여러 개 있어도 결국 매 시간 하나의 코어에서만 실행된다.
  2. One-to-one
    • 하나의 유저 스레드가 하나의 커널 스레드와 매핑된다.
    • 여러 코어를 통해 병렬 실행이 가능하다.
    • 하나의 스레드가 중지(block)되어도 다른 스레드에 영향을 주지 않는다.
    • Python의 threading이나 C의 <pthread.h>가 이에 해당된다.
  3. Many-to-many
    • 여러 유저 스레드를 여러 커널 스레드로 매핑한다.
    • 구현이 복잡하다. (실제로 쓰는 경우가 별로 없다)

Process vs. Thread

마지막으로, 프로세스와 스레드의 차이를 정리해보았다.

 

  프로세스 스레드
제어 블록의 내용 메모리, I/O 상태, CPU 상태 CPU 상태
컨텍스트 스위치 비용 높음 낮음
프로세스(스레드) 간 통신 복잡함 (InterProcess Communication) 단순함 (shared memory)
관리 주체 커널 커널, 유저