-

[운영체제 Step 6] - 스레드 관리 본문

CS/운영체제

[운영체제 Step 6] - 스레드 관리

흣차 2021. 12. 7. 01:58
728x90
반응형

지난 시간에 배운건 프로세스 관리에 대해 배웠습니다.

그 과정에서 컨텍스트 스위치를 이야기 했었고 컨텍스트 스위칭은 비용이 크기 때문에 가능한 줄이는 것이 좋다고 했었고 그 과정에서 바로 스레드가 있었습니다.

스레드의 뜻은 '실' 과 같습니다.

그럼 스레드가 운영체제에서는 어떻게 다루어지는지 이번 시간에 한 번 자세히 알아보겠습니다.

스레드를 얘기하려면 먼저 프로세스를 이해할 필요가 있습니다.

프로세스는 여러 형태로 정의가 되었었죠.

어떤 작업을 하는데 있어서 자원이 필요하기 때문에 그 자원을 제어하는 것. 그것이 프로세스의 역할입니다.

그렇다면 프로세스는 자원과 제어라는 두 가지로 나누어서 생각을 할 수 있습니다.

이 때 제어 부분을 따로 때놓고 고려할 때 이것을 스레드라고 할 수 있습니다.

보통 실 그림으로도 그릴 수 있는데요.

만약 논문이나 어느 자료에서 " ~ " 모양이 나오면 스레드라고 이해해도 되겠습니다.

이 스레드는 여러 개가 있을 수 있습니다.

즉, 하나의 프로세스 안에 스레드가 여러 개 있음으로서 자원을 제어할 수 있습니다.

 

  • 스레드

오른쪽을 보시면 우리가 짠 코드가 있을 것인데, 프로그램 카운터가 내가 어디를 실행하고 있는지 가르키고 있다고 지난 시간에 설명했습니다. 이 정보를 엑세스할 것이고, 전역 데이터라는 static 데이터가 있습니다. 그리고 프로세스가 사용하는 메모리 공간. 그 중에서도 전역 공간을 힙(Heap) 공간이라고 하는데 이것들은 자원에 속합니다.

그리고 이것들을 제어하는 프로세스가 있습니다.

이 프로세스에는 여러 정보를 갖고 있습니다.

자원을 제어하는 제어 정보와 더불어서 지역 데이터와(전역이 아닌 지역 데이터 -> 제어를 위해 일시적으로 사용) 이 지역 데이터들은 스택영역에 저장이 되기 때문에 이것들을 제어의 영역으로 나뉘어 집니다.

그런데 제어 정보만 때어놓았을 때 스레드라고 했습니다.

이처럼 프로세스 안에 여러 스레드가 있을 수도 있다고 이해하시면 되겠습니다.

그리고 각각 자기들만의 제어를 할 수 있는 기능을 가지고 있고 이 프로세스가 할당 받은 리소스를 공유한다고 이해할 수 있습니다.

따라서 같은 프로세스의 스레드들은 동일한 주소 공간을 공유하게 됩니다.

이 힙부터 코드까지 파란색 부분이 리소스가 되겠고요.

그리고 각각의 스레드가 작업 영역에 해당합니다.

그 각각의 스레드 내부에서 지역 데이터를 만들고 그 안에서 작업을 하게 됩니다.

또한 공유된 소스 코드 안에서 각 PC영역(Program Counter)을 가지고 자신이 어디에서 하고 있는지, 자신의 프로그램 코드를 제어하게 되겠습니다.

그래서 스레드는 가벼운 프로세스라고도 불립니다.

그 이유는 프로세스는 자원하고 제어를 각각 가지고 있어야 하는데, 스레드는 자원을 공유하고 제어만 가지고 있기 때문에 상대적으로 가벼운 프로세스(Light Weight Process -> LWP)라고 불립니다. 

그리고 프로세서를 활용할 때의 기본 단위라고도 합니다. (프로세서는 CPU라고 이해해도 무방)

만약 스레드가 여러 개면 동시에 CPU를 사용할 수도 있습니다.

스레드의 구성은 스레드ID, PC와 SP같은 Register set, 그리고 Stack 영역이 있습니다(지역 데이터)

그러므로 제어 요소들은 각각 개인들이 가지고 있고 데이터 및 자원들은 공유가 되어 프로세스 내의 다른 스레드와 공유할 수 있습니다. 

또한 옛날 방식의 전통적 프로세스는 자원을 공유하지 않고 제어 영역만 가지기 때문에 단일 스레드 프로세스라고도 부를 수 있습니다.

 

그래서 이 싱글 스레드와 멀티 스레드를 그림으로 한 번 이해해보겠습니다.

싱글 스레드
멀티 스레드

위 사진이 싱글 스레드, 밑의 사진은 멀티 스레드입니다.

단순히 봐도 프레세스가 여러개 있으면 멀티 스레드가 되겠습니다.

 

그렇다면 이 스레드는 왜 갑자기 등장했을까요?

스레드의 장점을 알아보겠습니다.

여기서 중요한 부분은 자원을 공유한다는 부분입니다.

왜 자원을 공유하는데 효율성이 증가할까요?

우리 프로세스를 P1, P2 두 개가 있을 때 A자원을 공유한다고 가정하겠습니다.

이 프로세스가 A를 사용하고 있는 동안에 P2는 A를 사용하지 않습니다.

그러다가 P1이 다 쓰고 나면 P2가 A를 사용하겠죠? 

이렇게 번갈아가면서 사용을 할텐데, 프로세스가 번갈아 가면서 사용이 된다는 뜻은 앞에서 얘기했던 컨텍스트 스위칭(Context Switching)이 자주 일어난다는 뜻이기 때문에(가능한 피하는게 좋습니다) 비용이 증가합니다.

하지만 스레드에선 얘기가 다릅니다. 

스레드 T1, T2가 있고 이것이 하나의 프로세스 P1안에 속해있고 동시에 A자원을 할당했다고 해보겠습니다.

그럼 T1과 T2 둘 다가 A를 사용하므로 컨텍스트 스위칭이 일어나지 않게되어 비용을 절감시킬 수 있습니다.

물론 T1과 T2가 둘 다 어떻게 쓰는지는 둘이 잘 조율을 해야겠지만 위의 방식처럼 개별적으로 작업하는 것보다 동시에 사용할 수 있게 해주는 것이 훨씬 비용절감 측면에서 좋습니다.

또한 자원 공유를 한다는 것은 커널의 개입도 줄일 수 있습니다.(하나의 프로세스만 사용하므로)

그리고 경제적인 측면에서도 훨신 효율적이고, 멀티 프로세서에도 활용할 수 있는데 이는 여러 개의 CPU 코어를 동시에 사용할 수 있다는 의미이므로 더 높은 성능을 얻을 수 있습니다. (병렬 처리를 통한 성능 향상)

그 다음 마지막으로 소개 안해드렸던 사용자 응답성이란 내용은 뒤의 예제에서 한 번 보여드리겠습니다.

자 우리가 배틀그라운드를 하고 있다고 가정해봅시다.

그럼 마우스 좌클릭을 하면 총알이 발사될테고 각종 단축키를 통해 우리는 게임을 플레이할 수 있습니다.

하지만 이 배틀그라운드가 단일 스레드로 이루어졌다면 어떻게 될까요?

분명 적이 나타났을 때 그쪽을 보기 위해 마우스를 돌리는 순간 해당 작업을 처리하기 위해 화면이 잠시 멈추었다가 상대가 보이고 총을 발사되었을 때 또 화면이 멈추었다가 적한테 죽어있을 것입니다.

이처럼 단일스레드는 하나의 작업을 하는 중에 다른 작업을 할 수 없으므로 우리는 제대로 된 게임을 즐길 수 없습니다.

그렇기 때문에 멀티 스레드를 사용하여 우리가 원하는 작업이 동시에 이뤄지게끔 처리하는 것입니다.

이것이 "사용자 응답성" 입니다. 또한 일부 스레드의 처리가 지연되더라도 다른 스레드가 계속 작업을 처리할 수 있다는 장점도 있습니다.

지금까지 스레드의 개념을 잡아 보았구요.

그럼 스레드가 어떻게 구현되는지 알아보겠습니다.

스레드의 구현은 크게 2가지로 나타낼 수 있습니다.

  1. 사용자 수준 스레드 (User thread)
  2. 커널 수준 스레드 (Kernel thread) 

가 있습니다.

하나씩 살펴보겠습니다.

사용자 수준 스레드는 사용자 영역을 스레드를 라이브러리로 구현한 것입니다.

무슨 의미냐면 어떤 라이브러리가 스레드처럼 들어갈 수 있게 해준다는 의미입니다. 이 라이브러리가 스레드의 생성, 스케줄링을 담당할 수 있게 해줍니다.

대표적인 예로 POSIX thread, Wins32 threads, Java thread API등이 있습니다.

좀 더 간단한 도식으로 살펴보면 다음과 같습니다.

어떤 사용자가 프로세스 P1을 하나 생성하게 되면 커널에는 제어하기 위한 요소인 커널 수준 스레드가 하나 만들어지게 됩니다. 당연하겠지오? 프로세스가 하나이기 때문에 하나 만들어집니다.

이 때 내부적으로 볼 때 라이브러리를 통해 여러 개의 스레드를 사용하겠다 하는 것이 사용자 수준 스레드입니다.

그래서 스레드 라이브러리 영역에서 이 내부에서 생성된 스레드를 관리를 하게 될 것이고 그 내부에 스레드를 제어하기 위한 스레드 제어 블록(TCB)가 생성이 됩니다.

따라서 커널 수준 스레드는 한 개인데, 사용자 수준 스레드는 여러 개인 다대일 매핑(N : 1)이 됩니다.

그로 인해 커널은 스레드의 존재를 모릅니다.

커널은 스레드가 생성되고 관리되는 것에 개입을 전혀 하지 않습니다.

즉 효율적이고 유연하게 관리를 할 수 있으며 커널 영역이 아닌 라이브러리 영역에서 관리를 해주기 때문에 오버헤드도 적어지게 됩니다.

그리고 이식성도 높은데요. 이게 라이브러리만 있는 시스템이라면 우리가 만든 프로그램을 그대로 사용할 수 있는 뜻이 되고 우리가 만든 멀티 스레드를 그대로 사용할 수 있습니다. 예를 들어 JVM을 사용할 때 우리가 다른 곳에 따로 JVM을 컴파일 하지 않아도 그대로 사용할 수 있다는 장점이 있습니다. 이를 이식성이 높다고 할 수 있습니다. (portability)

하지만 단점도 있습니다.

커널 수준이 한 개인데 사용자 수준 스레드가 여러 개인 경우를 살펴보겠습니다.

이 스레드가 일을 하다가 I/O가 필요해서 Block상태가 되었다고 해봅시다.

그럼 P1도 블록상태로 내려옵니다.

근데, 사용자 수준 스레드에서 한 개의 스레드만 I/O가 되고 나머지는 작업을 할 수 있는데 일대일 매핑이기 때문에 모든 스레드가 대기 상태가 되는 단점이 존재합니다.

그 이유는 커널은 프로세스 단위로 자원을 할당하기 때문입니다.

 

다음으로 커널 수준 스레드를 알아보겠습니다.

그림으로 먼저 살펴보겠습니다.

단일 프로세스 내에 스레드가 여러 개 생성되면 제어하기 위해 커널에도 제어하는 스레드를 같은 개수만큼 만들어 주겠다는 의미입니다. 

그래서 일대일 매핑이 될 것이구요.(1 : 1)

이를 관리하기 위한 스레드 제어 블록 (TCB)도 커널 영역에서 이루어 집니다.

커널 안에 제어 요소들이 여러 개 생기고 그것을 관리하는 것도 커널 영역에서 하기 때문에 자연스럽게 오버헤드(Overhead)도 커진다는 단점이 존재합니다.

즉, 부하가 커진다는 이야기가 되어버립니다.

우리가 프로세스일 때보다 적겠지만 얘네가 서로 하나의 CPU를 할당 받으면서 생기는 Context Switching으로 인해 생기는 오버헤드가 존재하므로 사용자 수준 스레드일 때보다 무겁다는 단점이 있습니다.

하지만, 커널 수준에서 각 스레드를 관리하기 때문에 하나의 스레드가 Block상태가 되어도 계속해서 작업을 진행할 수 있다는 장점도 존재합니다. (병행 수행 가능)

지금까지 공학자들이 일반적으로 접근하는 방법의 수준인 사용자 수준과 커널 수준 스레드에 대해 알아보았습니다.

이 둘이 다 좋은데 서로 장단점이 존재했다는 거 확인하셨지요??

그래서 등장한 것이 다대다 모델의 혼합형 스레드가 등장하게 됩니다.

사용자 수준 여러 개와 커널 수준 스레드 여러 개를 사용하자는 혼합형 스레드가 되겠습니다.

당연히 사용자 수준의 스레드가 커널 수준의 스레드보다 좀 더 많아야 합니다. (앞의 커널 수준 스레드 단점 때문에)

사용자는 원하는 수만큼 스레드를 사용할 수 있다는 장점이 있을 것이고, 커널 스레드는 자신에게 할당된 하나의 사용자 스레드가 Block상태가 되어도 다른 스레드를 수행할 수 있다는 장점이 있습니다. (병행 처리 가능)

즉, 사용자 수준 스레드와 커널 수준의 스레드의 장점을 합친 구조가 되겠습니다.

그림으로 살펴보면 다음과 같습니다.

혼합형 스레드

이처럼 사용자는 사용자 수준 스레드를 자유롭게 사용할 수 있고 커널 수준 영역에서는 사용자 수준 스레드보다 적겠지만 동적으로 자원을 쓸 수 있게 하나의 스레드가 Block되더라도 다른 스레드를 정상적으로 작동할 수 있게 돕습니다.

실제로 혼합형 스레드가 가장 많이 사용되고 있습니다.

오늘 배운 것을 정리해보겠습니다.

대부분 빼놓을 내용 없이 중요하지만 역시나 스레드의 개념이 가장 중요합니다.

각각의 자원을 제어하기 위해 자원이 공유가 되고 제어의 영역을 가진다는 점. 그것이 스레드가 되었고 그것을 CPU에 할당하기 위해 여러 가지 스레드 기법들을 알아보았습니다.

감사합니다.

728x90
반응형
Comments