Computer Science/Operating System

[운영체제] Thread(쓰레드)

재바기 2022. 5. 15. 21:49
728x90

본 내용은 [Operating Systems : Three Easy Pieces] 및 부산대학교 안성용 교수님의 운영체제 수업을 참고하였습니다.

 

Three Easy Pieces 중 첫번째 piece는 Virtualization 즉 가상화였다.

하나의 물리적 CPU를 다수의 가상 CPU로 확장해서 마치 여러 개의 프로세스가 동시에 실행되는 듯한 환상을 만들고, 또한 개별적인 process가 모두 독립적으로 많은 가상 메모리를 가지는 것처럼 느끼게 하는 address space라는 개념을 배웠다.
즉, CPU와 Memory의 virtualization에 대해서 다뤘다.

 

이제는 병행성 즉 Concurrency에 대해서 다루게 된다.

 

Thread

소프트웨어가 진보하면서 하나의 프로그램에서 복잡한 동시 작업을 요구하기 시작했고, 이를 위해서는 하나의 프로그램이 여러개의 프로세스를 만들어야 했는데 프로세스 특성상 하나의 프로그램이 이러한 동시 작업을 수월하게 할 수가 없었다.

따라서 프로세스보다 더 작은 실행 단위 개념이 만들어지게 되는데 이게 바로 Thread이다.

Thread라는 개념에 대해서 다루게 되는데, 한 순간에 하나의 instruction을 실행하는 (단일 pc) 관점에서 벗어나, 멀티 쓰레드 프로그램은 하나 이상의 실행지점(여러 개의 pc 값, 단 각각은 독립적)을 가지고 있다.

 

이렇게 한 프로세스 내에서 여러 개의 쓰레드를 가지는 경우를 Multi thread라고 한다.
즉 하나의 프로세스가 여러 개의 쓰레드를 가지므로 여러 개의 실행지점을 가질 수 있다.

그래서 이 쓰레드란 개념은 프로세스와 매우 유사하나, 차이점이 존재한다.
- 쓰레드들은 address space를 공유하기 때문에 동일한 값에 접근 가능(페이지 테이블 그대로 사용 가능)
- 단일 쓰레드 프로세스는 스택이 하나만 존재, 멀티 쓰레드 프로세스는 각 쓰레드가 독립적으로 실행되며, address space에는 하나의 스택이 아니라 쓰레드마다 스택이 할당되어있음.

우측은 address space에 여러 개의 스택 할당

 

또 유사점이라고 하면, 
- pc와 연산을 위한 register를 가짐
- 만약, 두 개의 쓰레드가 하나의 프로세서에서 실행중이면 실행하고자 하는 쓰레드(T2)는 context switch를 통해서 실행중인 쓰레드(T1)과 교체되어야 함
- 쓰레드 간의 context switch 방식은 process의 context switch 방식과 유사
- 프로세스간의 context switch에서 프로세스의 상태를 PCB에 저장하듯 쓰레드들의 상태를 저장하기 위해서는 마찬가지로 TCB(Thread Control Block)이 필요

 

Kernel-level Threads vs User-level Threads

 

- Kernel-level Threads 는 운영체제가 thread를 관리하는 것이다. 즉, 모든 thread관련 operation은 kernel에서 일어나고, kernel에서 수행되므로 thread creation과 management는 system call을 필요로 한다. OS가 관리하므로 OS가 thread들의 schedule또한 책임진다.
한계점 : Kernel 에서 thread를 관리하면 꽤나 expensive하다고 할 수 있다.(user-level에서 thread 개념을 적용하는 것이 훨씬 더 빠름, 또한 kernel 에서는 모두 system call이므로) 또한, 모든 thread에 대해서 kernel state를 가지고 있어야 한다. 따라서 동시에 실행되는 thread의 개수에 제한을 두는 경우도 있다.

- User-level Threads 는 user level 에서 thread가 구현되는 것이다. 따라서 thread 지원을 위한 library를 import 하거나 구현해서 사용한다. User-level에서 thread가 구현되었으므로 OS는 thread의 존재에 대해 알지 못한다. System call을 통하지 않으므로 가볍고 빠르다.
한계점 : OS가 thread의 존재를 알지 못하므로 비효율적인 decision을 내릴 수 있다.(전체 process 를 block 하는 등) 또한 multi-core CPU의 장점을 살리지 못한다.

 

 

Race Condition

 

Race condition은 멀티 쓰레드가 거의 동시에 임계 영역(critical section)을 실행하려고 할 때 발생하는 것이다.

아래의 예를 보자.

 

counter = counter + 1 이라는 명령을 실행한다.(default는 50)

이 경우 우리는 결과로 52를 예상하지만 보다시피 결과로 51이 counter에 저장되었다.

 

여기서 Interminate라는 개념이 등장한다. 실행마다 프로그램의 결과가 다를 수 있음을 의미한다.
즉, 결과가 non deterministic 하다는 것이다.(모든 결과는 실행마다 달라지면 안되고 항상 결정적이어야 함)

 

위의 예에서 보면 counter = counter + 1 이라는 명령어를 어셈블리어로 따져보면 

이 세 줄과 같다.

0x8049a1c(counter변수의 주소)에서 값을 eax레지스터에 가져온 후, 1을 eax 레지스터에 더하고, eax 레지스터의 값을 다시 0x8049a1c로 가져오는 과정

 

따라서 위의 경우는 eax로 가져온 후 1을 더해줬는데, 거기서 interrupt가 발생하여 context switch가 일어났고 Thread2에서 다시 50을 가져와서, 1을 더하고 51을 저장했는데, T1에서 51을 다시 counter에 저장한 경우이다.

 

따라서 위의 세 줄의 명령어처럼 shared variable을 access 하는 코드의 부분을 Critical section(임계 영역)이라고 하고 이 영역을 하나의 단위로 여겨줄 필요가 있다.(atomicity)

 

또한, Mutual exclusion(상호 배제) 라는 개념 또한 알아둬야 한다. 하나의 thread가 critical section을 실행중에는 다른 thread가 그 critical section을 실행하는 것을 막는 것이다.

 

 

Thread의 개념이 조금 생소해서 다시 추후에 복습하면서 pthread_create와 pthread_join 함수까지 함께 살펴봐야겠다.

 

//문제제기 및 피드백 언제든지 감사히 받겠습니다.

728x90