본문 바로가기
공부, 취준/운영체제

[운영체제] pintOS 프로젝트 - Alarm Clock (Sleep-Wakeup 구현하기)

by 린레몬 2021. 3. 4.

안녕하세요 레몬입니다.

정말 오랜만에 전자공학과 카테고리 글을 쓰게됐다.

사실 전공과목 소개 글을 다 마치고 여태 해온 프로그래밍 과제들을 정리할 생각이었는데, 전공 소개 글을 두개 써보니 여간 귀찮은 일이 아니라는걸 깨달았다.

원래 취지는 수강신청 전 고민중인 후배들을 위해 간략히 어떤걸 배우는지만 소개해서 나도 빨리빨리 쓰고 치우려 한건데, 자꾸 중요한 내용들을 이것저것 넣다보니까 쓸데없이 디테일한 내용이 들어가고 무엇보다 글쓰면서 내가 과제하는 느낌이 들어서...

전공 소개 글 자체를 더이상 쓰지 않겠다는건 아니지만, 이대로면 학교 다니면서 해왔던 과제들 정리는 한참 뒤에나 시작할 수 있을 것 같아서 부랴부랴 핀토스 프로젝트부터 시작하게 됐다.


pintOS 프로젝트

학교에서 운영체제 과목을 수강하면서 겪게된 핀토스 프로젝트 과제다.

직접 겪어보기 전에는 핀토스라는 이름만 얼핏 들어본 정도였는데, 과제 해결을 위해 엄청나게 구글링을 해보니 우리나라 뿐 아니라 전세계적으로 악명높은 프로젝트 과제인 것 같다..

실제로 검색해보니 많은 사람들이 같은 프로젝트를 해결하기 위해 코드를 짜고 공유해온 흔적이 많이 보인다.

거기에 나도 숟가락 얹어보고 내가 진행한 과제 정리도 할 겸 포스팅을 하려고 한다.

esos.hanyang.ac.kr/files/courseware/undergraduate/PINTOS/Pintos_all.pdf

위 링크가 현재 더이상 작동하지 않아, 오픈되어있는 KAIST 실습자료 링크로 대체합니다.

http://broadpeak.kaist.ac.kr/wiki/dmcweb/download/%EC%9A%B4%EC%98%81%EC%B2%B4%EC%A0%9C_%EC%8B%A4%EC%8A%B5_%EA%B0%95%EC%9D%98%EC%9E%90%EB%A3%8C.pdf

위에서 간단히 언급한대로, 이 핀토스 프로젝트는 굉장히 범용적으로 제출되는 과제다.

첨부한 링크는 한양대학교의 핀토스 프로젝트 안내 자료인데, 실제로 내가 다니는 학교에서도 위 자료의 내용을 발췌하여 과제가 나왔다. (내용이나 사진이 똑같음)

위 자료를 보면 알겠지만 내용이 매우 방대한데, 나는 이중에 몇몇 프로젝트만 수행하게 됐다. 분량을 보아하니 학부생 수준에서 위 프로젝트 전체를 과제로 내는 교수님은 흔치 않을 것 같다..

어쨌든 과제를 진행하면서 몇몇 문제는 우리나라 말로 쓰인 블로그 자료도 있어서 수월하게 진행할 수 있었는데, 후반부 문제는 접근성 좋은 자료가 없어서 꽤 애를 먹었다..

인터넷에 조금이나마 더 정보를 남기기 위해 내가 진행한 프로젝트 문제에 한해서라도 내가 이해한 내용을 동반하여 포스팅을 하게 됐다.

 

1. 과제 개요

Pintos는 기본적으로 Thread가 sleep 상태일 때 계속해서 자신이 다시 CPU를 점유해도 되는지 확인하는 busy waiting 방식을 사용한다. 이는 sleep중인 Thread가 계속해서 CPU를 점유하게 되는 상황이다. 이러한 sleep 방식을 수정하여 sleep-wakeup 방식으로 다시 구현한다.

이제부턴 그냥 내가 제출한 레포트 내용 따라서 쭉 쓸 예정이다.

과제 목표는 결국 pintOS Thread의 sleep-wakeup 방식을 구현하는 것이다.

 

2. 아이디어

기존 busy waiting 방식에서는 sleep 상태의 Thread들이 ready_list에 들어가 전달된 tick만큼의 시간동안 while문을 돌면서 계속 CPU를 점유한다. 이를 방지하기 위해 완전히 sleep 상태에 들어갈 Thread를 저장할 sleep_list를 구현한다. thread_sleep() 함수를 호출하면 해당 Thread를 sleep_list에 넣고, 이 Thread를 깨워야할 때 thread_awake() 함수를 호출한다. 깨어난 Thread를 다시 ready_list로 옮기는 식으로 구현하면 sleep_list에 있는 Thread는 CPU를 점유하지 않고 자신이 깨어나야 할 때까지 기다릴 수 있을 것이다.

교수님과 조교님께 제출하는 레포트에선 이렇게 간략하게 써놔도 알아들으실 테지만, 이제 과제를 시작하는 사람 입장을 생각하면 너무 무성의한 글이 될 것 같다..

일이 너무 커지는건 바라지 않으니 진짜!!!!! 필요한 부분만 간략히 설명하고 넘어가자

위 그림은 Thread의 일생을 나타내고 있다.

메이플 하면서 유튜브도 보고 카톡도 하고 인강도 틀어놓는 우리 입장에선 컴퓨터가 동시에 여러 작업을 하고 있는 것으로 보이지만, 실제로는 그렇지 않다.

굉장히 짧은 시간 단위로 주어진 여러 작업들을 시분할하여 몇 ms동안 Thread A, 다음 몇 ms는 Thread B, 다시 몇 ms동안 Thread A... 이런 식으로 동시에 여러 작업을 하는 것 처럼 보이도록 바쁘게 동작한다.

여기서 중요한건, 결국 하나의 Thread가 프로세서에 도착해서 처음부터 끝까지 프로세서를 차지하며 볼일 다 마치고 가는게 아니라는거다.

옆에서 기다리고 있는 Thread B의 수행을 위해 Thread A가 자리를 양보하고 기다리는 경우가 굉장히 자주 발생한다는 뜻이다.

그럼 이렇게 기다리고있는(= ready_list 에 들어간) Thread들이 자기 차례가 되었음을 어떻게 알 수 있을까?

기존 pintOS에서는 busy-waiting 방식으로 이 기능이 구현되어 있다.

이름만 봐도 알 수 있듯, 기다리는 동안 바쁘게 일한다는 뜻이다.

내 차례가 올 때 까지 기다리긴 기다리는데, 그동안 내 차례가 왔는지를 본인이 주기적으로 확인을 하고 있다. 듣기만 해도 굉장히 비효율적인 방식이다.

프로세서 입장에서도 각각의 Thread의 순서가 왔는지 안 왔는지를 체크해줘야 하기 때문에, 이건 뭐 말이 CPU 점유를 내려놓고 기다리는거지 사실상 계속해서 CPU 연산을 잡아먹고 있는 상황이나 다름없다.

이를 해결하기 위해, sleep-wakeup 방식을 구현하는 것이 이번 프로젝트의 목표다.

이 역시 이름을 보면 대충 기능을 알 수 있다.

기다리는 동안 계속 눈치를 보면서 CPU를 잡아먹는 busy-waiting 방식과 달리, 기다리면 아예 자버리고 누군가 네 차례가 왔다며 깨워줄 때 까지 아무 것도 하지 않는다는 것이다.

이러면 진짜 CPU 점유를 내려놓고 '기다릴' 수 있을 것이다.

 

3. 설계

- thread 자료 구조 수정

Thread 자신이 깨어나야 할 tick을 저장하는 wakeup_tick 변수를 추가한다.

~/pintos/src/threads/thread.h

앞으로 모든 소스코드 파일 경로는 맨 위 상단바 참조!!!

구현 위해 코드 건든 부분에는 밑줄 + 주석(Pintos Project#)으로 표시했음!

(코드는 옮겨적기 귀찮아서 그냥 이미지로만..ㅎ 단순 복붙보다는 그래도 직접 타이핑 해보는게 조금이나마 도움이 됩니다)

thread 구조체에 wakeup_tick 변수를 추가했다. thread 관련 코드에서 tick 값을 int64_t로 선언하여 사용중인 것을 확인하고 같은 자료형을 사용했다.

 

- 전역변수 추가

thread.c 코드에 sleep중인 Thread가 들어갈 sleep_list와 다음에 깨어날 listwakeup_tick (=최소값)을 저장할 next_tick_to_awake 변수를 추가했다.

 

- 구현할 함수 선언

헤더 파일에 구현할 함수들을 선언했다.

 

- thread_init() 함수 수정

리스트들을 초기화하는 thread_list 함수 내에 위에서 만든 sleep_list도 초기화 하도록 코드를 추가했다.

 

- thread_sleep() 함수 구현

기존 busy_waiting을 유발하는 while문을 주석처리하고 thread_sleep 함수를 대신 호출하도록 하였다. os가 시작된 후부터 지난 틱을 리턴하는 timer_ticks() 값을 저장한 start 변수를 이용했다.

 

- thread_sleep() 함수 구현

threadsleep 상태로 만드는 thread_sleep 함수를 구현했다. 현재 Threadthis로 저장하여 사용했다. 우선 현재 Threadidle thread인지를 체크하여 idle thread라면 중지한다. idle thread가 아니면 interrupt를 중지한 뒤 현재 threadsleep_list에 넣는 과정을 수행한다. 다음으로 깨어나야 할 threadtick 값을 갱신하고, 현재 thread sleep_list에 넣은 뒤 block한다. 이후 intrerrupt를 다시 받도록 한다.

 

- timer_interrupt() 함수 수정

tick마다 sleep_list를 체크하며 깨어날 thread가 있는지 확인할 수 있도록 timer_interrupt 함수를 수정하였다. 다음으로 깨어나야 할 Threadtick 값과 현재의 ticks값을 비교하도록 한다.

 

- thread_awake() 함수 구현

next_tick_to_awake 변수를 초기화하고, sleep_listhead에 있는 threadsleeping 변수로 가져온다. sleep_list를 순회하며 깨워야 할 thread sleep_list에서 제거하고 unblock하는 과정을 수행한다.

다음으로 깨어나야 할 threadtick 값을 최소값으로 갱신하는 update_next_tick_to_awake 함수를 추가하였다. 현재 ticks 값과 비교하여 더 작은 값을 가질 수 있도록 하였다.
get_next_tick_to_awake 함수는 현재 next_tick_to_awake 값을 리턴한다.

 

4. Test Programs 비교

기존 busy waiting 방식을 이용하는 pintos 환경에서 alarm-single, alarm-multiple, alarm-priority, alarm-zero, alarm-negative를 실행한 결과이다. busy waiting 방식을 사용중이기 때문에 각 Threadsleep 상태에서도 CPU를 점유하고 있어 idle tick이 모두 0인 것을 확인할 수 있다.

busy waiting 방식을 수정하여 sleep-wakeup을 구현한 뒤 위 프로그램을 실행한 결과이다. Threadsleep 상태일 때 CPU를 점유하지 않아 idle tick이 늘어난 것을 확인할 수 있다.


쓰다보니까 진짜 또 귀찮아져서 실제 내용은 그냥 레포트 복붙으로 때웠다.

그래도 나름 내가 이해한 바를 필요한 만큼 설명해놨고 실제로 만점도 받았으니까..!

어느정도 이해해가며 과정을 따라갈 수 있을 것이라고 기대한다.

만약 막히는 부분이 있다면 댓글에 작성해주면 기억을 더듬어서 어느정도 해결에 도움을 줄 수도 있긴한데.. 환경도 제각각이고 버그가 참 생각지도 못한 곳에서 튀어나오는 경우가 많다보니 정말 도움이 될 수 있을지는 모르겠다.

아무튼 sleep-wakeup 구현은 끝!!

댓글