Pintos project1의 목적은 쓰레드에 대한 이해와 CPU가 많은 쓰레드를 어떤 방식으로 관리하는지 이해하는 것이라 생각한다. 이번 글에서 나는 운영체제 강의, 서적, 그리고 pintos에 작성된 코드를 통해 내가 이해한 개념들을 정리해보고자 한다.
프로세스란 사용자가 프로그램을 실행시키면 그 프로그램을 실행 시키는 실행 주체를 의미한다. 하나의 프로세스는 무조건 하나의 실행주체만을 가지는 것이 아니라 여러개의 수행 단위를 가질 수 있는데 이를 쓰레드라고 한다. 즉, 쓰레드란 프로세스 내에서 실행되는 흐름의 단위이다. 각 프로세스는 별개의 메모리 공간을 사용하기 때문에 서로의 메모리에 영향을 줄 가능성이 없다. 하지만 쓰레드는 한 프로세스에서 작동하는 실행 주체이기에 프로세스 내의 메모리를 공유하여 사용하기에 문제가 발생할 수 있다는 단점이 있지만, 프로세스간의 문맥교환(context switch)에 드는 비용보다 더 작은 비용으로 문맥교환이 가능하는 장점이 있다.
Pintos에서 쓰레드는 총 4가지 상태(enum thread_status) 중 하나로 존재한다. 각 상태는 THREAD_RUNNING, THREAD_READY, THREAD_BLOCK, THREAD_DYING이다.
쓰레드가 CPU를 점거하고 프로세스를 진행하고 있는 상태이다. 이때 단일 CPU 컴퓨터일 경우 하나의 CPU에는 하나의 쓰레드만이 존재할 수 있다. 그러므로, 다른 쓰레드 혹은 프로세스를 진행하기 위해서는 RUNNING 상태로 존재하는 쓰레드를 READY 혹은 BLOCK 상태로 바꾸어주어야한다. pintos에서는 thread_yield( )가 쓰레드를 READY 상태로 만들어주고, thread_block( )가 BLOCK 상태로 만들어 준다. 그리고 thread_current( ) 는 현재 CPU에서 작동하고 있는 쓰레드를 반환해준다.
THREAD_READY는 쓰레드가 자신의 일을 수행할 준비가 된 상태이다. 하지만 단일 CPU일 경우 하나의 쓰레드만이 일을 할 수 있기 때문에, READY 상태의 쓰레드들은 자신의 차례가 올때까지 기다려야한다. 나는 이것이 마치 이어달리기에서 트랙위에서 바톤을 기다리는 주자같다고 생각했다. 하나의 CPU일 경우 한 사람만 달리니깐 성화 봉송이라고 해야하나..? 여하튼 READY 상태의 쓰레드를 RUNNING 상태로 바꿔주는 것은 schedule( ) 이다. 쓰레드가 CPU를 점거하기 위해서는 비어있어야 하기 때문에 schedule() 은 보통 thread_yield( )가 호출되고 그 내부 마지막에 호출된다.
항상 비슷한 의미이지만 확실히 다른 용도로 사용되는 개념들을 구분하는 것이 어려운 것 같다. 쓰레드가 BLOCK 상태라는 것은 일을 수행하기 전까지 대기하는 상태라는 의미이다. 이는 마치 앞에서 보았던 READY 상태처럼 보인다. 그러나 BLOCK과 READY 상태의 가장 큰 차이는 BLOCK 상태의 쓰레드들은 누군가 UNBLOCK을 시켜 READY상태로 만들어주기 전까지는 CPU를 점유할 수 없다는 것이다. 즉, 모든 READY 상태의 쓰레드들이 일을 끝내 CPU가 비어있어도 BLOCK상태의 쓰레드는 계속 대기 상태로 존재한다. 예를 들어, scanf()를 호출할 경우 사용자가 정보를 input하기 전 까지는 계속 대기 상태로 있어야하는 것과 같은 상황이다. BLOCK 상태의 쓰레드는 thread_unblock( ) 함수를 통해 READY 상태가 된다.
모든 일을 마친 쓰레드는 thread_exit() 함수를 통해 THREAD_DYING 상태가 되고 자신의 메모리를 반납한다.
앞서 언급했었지만 쓰레드는 한 프로세스내에서 작동되는 실행 주체이기 때문에 프로세스 내의 메모리를 공유한다. 그런데 만약 두 쓰레드가 동시에 한 메모리에 접근할 경우에 원하는 결과가 아닌 이상한 결과가 나올 수도 있다. 그렇기 때문에, 한 쓰레드가 공유 자원에 접근할 경우 다른 쓰레드는 그 공유 자원에 접근을 하지 못하게 해야한다. 좀 더 전문적인(?) 용어를 사용하면 공유 자원에 대한 쓰레드가 접근 가능한 영역인 Critical Section을 만들고 이에 대한 접근 권한을 따로 주는 동기화 방식을 사용해야 한다. 이를 구현하기 위한 도구로는 semaphore, lock, monitor가 있다. 처음에는 이 개념들이 서로 독립적인 방식이라고 생각했는데 막상 pintos 내부의 코드를 보면 연관이 있는 것처럼 보여서 구분이 어려웠다 (예를들어, lock 함수안에 sema_up, sema_down이 존재한다). 그래서 나는 각 방식의 정의보다는 그 방식이 다른 방식과 어떤 점이 다른지를 중점적으로 정리해보고자 한다.
추가 예정
추가 예정
추가 예정