메모리 관점에서 본 쓰레드

이를 보기 전에 먼저 메모리 관점에서 본 프로세스편을 보고 오기를 추천한다.

각각의 프로세스는 메모리 공간에서 독립적으로 존재한다.

위의 그림은 프로세스를 구성하는 메모리 공간의 모습이다. 각각의 프로세스는 자신만의 이런 메모리 구조를 가진다. 프로세스 A, B, C가 존재한다면 각각의 프로세스는 모두 위와 같은 구조의 메모리 공간을 가진다.

독립적인 만큼 다른 프로세스의 메모리 공간에 접근할 수도 없다. A가 B의 메모리 공간에 접근하게 된다면 재앙이 발생할 수도 있다.
예를 들면, Chrome이 windows 메모리 공간에 접근한다면 안전성이 보장되지 않는 문제가 발생할 것이다.

운영체제의 메모리 공간에 접근하여 뭔가를 변경한다면 심각한 문제가 발생할 수 있다.(물론 운영체제의 메모리 공간에 접근하는 것은 원천적으로 불가능하다.) 그러므로 프로세스의 안전성을 보장하기 위해서는 프로세스는 각각 독립된 메모리 공간을 가져야 한다.

그렇다면 프로세스 A에서 연산한 결과를 프로세스 B가 받아서 사용하고 싶다면 어떻게 해야할까??

IPC

(inter process communication)

A의 메모리 공간에 B가 직접 접근하지 못하기 때문에 프로세스간의 통신을 하는 특별한 방법이 존재하는데 메일슬롯, 파이프 등이 바로 프로세스 간의 통신 즉, IPC의 예라고 할 수 있다.

IPC에 대한 설명은 프로세스부분을 참고하면 된다.

여기서 중요한 점은 프로세스는 독립적인 메모리 공간을 지니기 때문에 IPC를 통하지 않고서는 통신할 수 없다는 사실이다. 그리고 프로세스가 여럿이 병렬적으로 실행되기 위해서는 필연적으로 Context Switching이 발생할 수밖에 없다.

1
2
독립적인 메모리 공간으로 컨텍스트 스위칭이 발생한다.
프로세스 간 통신하기 위해서는 IPC가 필요하다.

위의 두 가지 프로세스가 지니는 문제점을 한 번에 해결할 수 있는 녀석이 쓰레드이다.

메모리 공간에서의 쓰레드

위 그림은 프로세스와 쓰레드의 메모리 구조의 차이점을 보여주는 그림이다. 왼쪽의 프로세스는 앞에서 설명했고, 오른쪽의 쓰레드의 메모리 구조를 확인해보자.

쓰레드는 프로세스 안에 존재하는 실행 흐름이다. 메모리 구조 역시 그러하다. 특이한 점은 쓰레드는 프로세스의 heap, stack, code 영역 등을 공유한다는 사실이다. 각각의 프로세스가 독립적인 stack, heap, data, code 영역을 가진 반면에 한 프로세스에 속한 쓰레드는 stack 영역을 제외한 메모리 영역은 프로세스의 메모리 영역을 공유해서 사용한다.

쓰레드가 code 영역을 공유하기 때문에 한 프로세스 내부의 다른 쓰레드들은 프로세스가 가지고 있는 함수를 자연스럽게 호출할 수 있다.

뿐만 아니라 쓰레드는 data, heap 영역을 공유하기 때문에 IPC 없이도 쓰레드 간의 통신이 가능하다. 동일한 프로세스 내부에 존재하는 쓰레드 A, B가 통신하기 위해 heap 영역에 메모리 공간을 할당하고 두 쓰레드가 자유롭게 접근한다고 생각하면 된다.

쓰레드는 프로세스처럼 스케줄리으이 대상이다. 이 과정에서 컨텍스트 스위칭이 발생한다. 하지만 쓰레드는 공유하고 있는 메모리 영역 덕분에 컨텍스트 스위칭이 발생하는 오버헤드가 프로세스에 비해 작다.

쓰레드란?


동일한 일을 하는 부분이 많은 프로세스를 여러 개 생성하는 것은 메모리 낭비이다. 그래서 프로세스는 하나만 생성하고 Program Counter만 여러 개 생성한다. 즉, CPU 수행 단위를 여러 개를 두는 것이며 이를 쓰레드라고 한다.

각각 다른 Instruction을 수행하려면 Program Counter 값이 존재해야 하고, 메모리에 어떤 레지스터 값들을 세팅해야 할텐데, 쓰레드마다 Program Counter 값과 레지스터 값들이 들어가는 것이다. 이렇게 쓰레드를 생성하여 쓰레드마다 다른 부분의 코드를 실행할 수 있게 하면 된다.

  • 쓰레드란 프로그램(프로세스) 실행의 단위이며 하나의 프로세스는 1개 이상의 쓰레드로 구성이 가능하다.

  • 하나의 프로세스를 구성하는 쓰레드들은 프로세스에 할당된 메모리, 자원 등을 공유한다.

  • 프로세스와 같이 실행, 준비, 대기 등의 실행 상태를 가지며 실행 상태가 변할 때마다 쓰레드 문맥 교환(context switching)을 수행한다.

  • 각 쓰레드별로 자신만의 스택레지스터를 가진다.

  • 특징

    • 스레드는 프로세스 내에서 Stack만 따로 할당받고, 프로세스의 Code, Data, Heap 영역은 공유한다.
    • 스레드는 한 프로세스 내에서 동작되는 여러 실행의 흐름으로, 프로세스 내의 주소 공간이나 자원들(힙 공간 등)을 같은 프로세스 내에 스레드끼리 공유하며 실행된다.
    • 같은 프로세스 안에 있는 여러 스레드들은 같은 힙 공간을 공유한다. 반면에 프로세스는 다른 프로세스의 메모리에 직접 접근할 수 없다.
    • 각각의 스레드는 별도의 레지스터와 스택을 갖고 있지만, 힙 메모리는 서로 읽고 쓸 수 있다.
    • 한 스레드가 프로세스 자원을 변경하면, 다른 이웃 스레드(sibling thread)도 그 변경 결과를 즉시 볼 수 있다.

쓰레드를 생성하여 프로세스를 수행했을 때의 장점

  1. 응답시간이 줄어든다.(Responsiveness)

하나의 프로세스 안에 여러 쓰레드를 두게 되면 즉, 다중 쓰레드로 구성된 프로세스 내에서는 하나의 서버 쓰레드가 blocked(or waiting)상태인 동안에도 동일한 프로세스 내의 다른 쓰레드가 실행되어 보다 빠른 처리가 가능하다. 웹 브라우저에서 네트워크를 통해 웹 페이지를 읽어올 때 읽어오는 작업이 오래 걸리기 때문에, 웹 브라우저 상태가 blocked 상태가 된다. 하나의 쓰레드가 데이터를 읽어오는 동안에 다른 쓰레드가 읽어온 데이터를 화면에 뿌려주는 역할을 하면 되는 것이다.

  1. 시스템의 자원소모가 줄어든다.(Resource Sharing로 인해)

하나의 프로세스 안에 여러 개의 쓰레드를 두면 메모리 낭비를 줄일 수 있다. 비슷한 작업에 대해서는 리소스를 공유한 결과이다.

  1. Economy

생성하고 switch 하는 면에서 프로세스보다 오버헤드가 적고 빠르다. 이를 경제적이라고 표현한다.[아직 정확한 원리를 잘 모른다.]

  1. Utilization of MP(MultiProcessor) Architectures

쓰레드를 생성하게 되면 병렬성을 높일 수 있다. 이는 CPU가 여러 개인 컴퓨터에서만 얻을 수 있는 장점이지만 쓰레드를 통해 이 장점을 취할 수 있다.

프로세스와 쓰레드의 차이

프로세스는 운영체제로부터 자원을 할당받는 작업의 단위이고
쓰레드는 프로세스가 할당받은 자원을 이용하는 실행의 단위이다.

  • 프로세스는 실행중인 프로그램으로 디스크로부터 메모리에 적재되어 CPU의 할당을 받을 수 있는 것을 말한다.

  • 하지만 프로세스의 생성은 많은 시간과 자원을 소모한다.

  • 쓰레드는 프로세스의 실행단위라고 위에서 설명했다.

  • 한 프로세스 내에서 동작하는 여러 실행 흐름으로 프로세스 내의 주소 공간이나 자원 등을 공유할 수 있다.

  • 이 경우 각각의 쓰레드는 독립적인 작업을 수행해야 하기 때문에 각자의 스택PC 레지스터 값을 갖고 있다.

쓰레드마다 스택을 독립적으로 할당하는 이유

스택은 함수 호출시 전달되는 인자, 복귀 주소값 및 함수 내에서 선언하는 변수 등을 저장하기 위해 사용되는 메모리 공간으로 스택 메모리 공간이 독립적이라는 것은 독립적인 함수 호출이 가능하다는 것이고 이는 독립적인 실행 흐름이 가능하게 한다.

따라서 독립적인 실행 흐름을 위한 최소 조건으로 독립된 스택을 할당한다.

쓰레드마다 PC Register를 독립적으로 할당하는 이유

PC 값은 쓰레드가 명령어의 어디까지 수행했는지를 나타내게 된다. 쓰레드는 CPU를 할당받았다가 스케쥴러에 의해 다시 선점당한다. 그렇기 때문에 명령어가 연속적으로 수행되지 못하고 어느 부분까지 수행했는지 기억할 필요가 있다. 따라서 PC 레지스터를 독립적으로 할당하는 것이다.

쓰레드의 장점

  • 쓰레드는 프로세스보다 생성 및 종료시간, 쓰레드간 전환시간이 짧다.
  • 쓰레드는 프로세스의 메모리 자원 ㄷ으을 공유하므로 커널의 도움 없이
    상호 간에 통신이 가능하다.
  • 프로세스 간의 통신 방법에 비해 쓰레드의 간의 통신 방법이 훨씬 간단하다.[별도의 자원을 이용하는 것이 아니라 전역 변수의 공간을 이용하여 데이터를 주고 받을 수 있다.]

쓰레드의 단점

  1. 동기화 문제

앞에서 언급한 쓰레드의 장점 중에서 쓰레드 간의 통신시 데이터를 주고 받는 방법은 메모리 공간을 공유하여 데이터 세그먼트, 즉 전역변수를 이용한다고 했다. 그런데 공유하는 전역 변수를 여러 쓰레드가 함께 사용하려면 충돌하는 문제가 발생한다.

따라서 쓰레드 간에 통신할 경우에는 충돌 문제가 발생하지 않도록 동기화를 통하여 문제를 해결해야 한다.

  1. 멀티 쓰레드를 이용할 때 주의 깊게 설계해야 한다.
  2. 프로그램 디버깅이 어렵다.(왜지?)
  3. 단일 프로세서 시스템에서는 효과를 기대하기 어렵다.

쓰레드의 종류

  1. kernel Thread(커널 쓰레드)

프레소스 내 스레드가 여러 개라는 것을 OS가 알고 있는 쓰레드이다. 그래서 커널이 쓰레드 스케쥴링을 맡아서 하게 된다.

  1. User Thread(유저 쓰레드)

라이브러리 차원에서 지원되는 쓰레드이다. 프로세스 안에 쓰레드가 여러 개 있다는 것을 OS가 모르기 때문에 커널 입장에서는 하나의 일반적인 프로세스로 인식된다.

참고