[정글] WEEK08 - WIL : 정글끝까지 PintOS Proj_1 Thread 회고

Jayden·2022년 5월 25일
0

정글

목록 보기
10/13

PintOS Proj_1 Thread

Alarm Clock

Interrupt는 Hardware에 의해 발생한다. (division by zero 같은 경우 제외)
컴퓨터에서의 시간은, Timer Hardware가 특정 시간동안 특정 Frequency로 보내는 Interrupt를 Count하는 방식으로 계산된다. PintOS 에서는 Timer Hardward에 의해 1초에 100번씩 Timer Interrupt가 발생되며, 이때마다 전역변수로 선언된 ticks를 1씩 올려줌으로써 시간을 계산한다.

Timer Interrupt Handler는 다양한 일을 수행한다. 단순하게 ticks를 올려주어 시간 계산을 할 수 있을뿐만 아니라, 이에 따라 각 프로세스의 동작 시간을 계산할 수 있으며, 현재 진행중인 thread가 cpu를 점유하고 있는 시간을 계산하여 스케쥴링을 하기도 한다.

(PintOS에서)1초에 100번 Interrupt Handler가 작동한다는 것이 사람의 생각으로는 굉장히 자주 handler가 작동되는 것 같아보여서 다른 일은 언제하나 싶어보인다. 하지만 10ms라는 시간은 컴퓨터에게는 굉장히 긴 시간인 듯 하다. 생각해보면 그 느리다는(상대적으로) 파이썬으로 알고리즘을 풀어도, 잘 짠 코드는 코드를 수행하는데 1ms가 채 안걸린다. 따라서 10ms마다 interrupt가 발생하고, 짧은 시간을 계산하여 다양한 process나 thread를 scheduling함으로써 동시에 많은 일을 처리하도록 할 수 있다.

Priority Scheduling

Round robin (RR) 방식의 Scheduling이란 ready queue에 작업이 들어온 순서대로 작업을 처리하는 방식이다. 초기 PintOS는 RR방식으로 구현되어있다. 하지만 현실은 중요한 작업들이 있고 상대적으로 덜 중요한 작업들이 존재한다. 그래서 우선순위priority를 고려한 Scheduling이 필요하다.

PintOS는 thread를 통해 동시성을 구현하고있다. Process는 각각의 Virtual Memory(VM) 영역을 가지지만, Thread는 Process의 VM영역 안에서 만들어지는 것으로 Process의 VM을 공유하고 있다. 이때 문제가 되는 것이 공유자원이다. Thread는 VM을 공유하기 때문에 VM의 stack, heap 등에 접근할 수 있다. 문제는 동시적(실제로 동시에 일어나지는 않으나, 아주 짧은 시간 간격을 두고 스케줄링함으로써 구현되는)으로 작업이 이루어지다보니, 여러개의 Thread가 동일한 data에 접근을 하면 data가 잘못 변경될 수 있는 것이다. 이를 해결하기 위해 semaphore, lock, monitor(condition variables) 등의 기법이 사용된다.

Priority Scheduling은 높은 우선순위의 작업을 우선 진행하여 작업의 효율성을 높인다. 하지만 공유자원을 관리하기 위해 semaphore 등이 도입된 경우 문제가 발생한다. 낮은 우선순위의 thread가 semaphore를 보유하였으나 이후 높은 우선순위의 thread가 동일한 자원에 접근하는 경우 해당 semaphore를 보유할 수 있을 때까지 해당 thread는 block 상태가 되는데, 어중간한 우선순위를 갖는 thread가 ready queue에 있을 경우 semaphore를 보유한 thread보다 이 thread가 먼저 scheduling되어 높은 우선순위의 thread가 장기간 대기하게 되는 경우이다. 이런 상황을 Priority Inversion Problem 이라고 한다. 이 경우는 높은 우선순위를 가진 thread가 필요한 semaphore를 보유하고있는 thread에게 자신의 priority를 임시로 빌려줌으로써 해결할 수 있으며 이를 Priority Donation이라고 한다.

Advanced Scheduling (mlfqs)

위 단계까지는 priority가 thread가 생성될 때 초기화되고 변하지 않는 경우였다. 하지만 현실은 시간이 지남에 따라 각 thread는 특성이 변할 수 있다. 실시간 응답이 필요하여 우선순위가 높았던 thread가 어느 순간에는 입출력이 필요하여 우선순위가 낮아질 수 있다(입출력은 상대적으로 많이 느린 작업이므로). 또한 priority가 변하지 않는 기존 방식에서는 높은 우선순위의 thread가 계속 투입되면 낮은 우선순위의 thread는 진행이 필요함에도 불구하고 진행되지 않을 수 있다. 이러한 점을 개선한 방식이 mlfqs(MultiLevel Feedback Queue Scheduling)이다.

mlfqs 방식은 우선순위 level에 따라 queue를 따로 두어 thread를 관리한다. 또한 모든 thread의 우선순위는 정해진 시간마다 재계산된다. 우선순위가 재계산 되는 방식은 다양하게 있지만, PintOS에서는 4.4BSD 방식(?)을 사용한다. 우선순위는 최근에 CPU를 점유한 정도를 나타내는 recent_cpu와 1분당 cpu에서 진행된 평균 process수를 의미하는 load_avg를 활용한 수식으로 계산된다. 이를 통해 우선순위가 낮아 진행되지 못하던 thread들도 골고루 scheduling되어 진행될 수 있게 된다. 또한 한 thread가 CPU를 오래 점유하지 않도록 scheduling하기 위해 특정 시간마다 해당 thread의 우선순위를 재계산하며(PintOS에서는 4ticks, 즉 40ms마다), thread의 특성 변화에 따라 골고루 scheduling하기 위해 특정 시간마다 load_avg와 전체 thread의 recent_cpu, priority를 재계산한다(PintOS에서는 1초마다).

회고

가장 어렵다는 PintOS를 시작하며 궁금했던 OS를 깊게 공부하고 이해할 수 있다는 점에서 기대가 많이 되었지만 한편으론 얼마나 어려울까싶어서, 그리고 정글 프로그램안에서는 첫번째 협업이라 덜컥 겁이 났었다. 하지만 팀원들과 협업하면서 결국 이번 주차의 목표치를 온전히 이루었을뿐만 아니라, 많은 부분을 배울 수 있었고 일주일 전보다 훨씬 성장할 수 있었다.

이번 프로젝트는 여러가지 면에서 너무나 성공적이었다.

첫번째. Teamwork이 좋았다

여러모로 Teamwork이 좋아서 synergy가 잘 났다. 모두가 프로젝트를 끝까지 완성하고자하는 욕심이 있었고, 직접 코드를 구현해보고자하는 욕심이 있었다. 그렇기에 각 단계마다 deadline을 정하여 각자 공부하고, 구현할 코드를 할당하고, 실제 구현하고 debugging하는 모든 과정에서 버거울 수도 있었지만 모두 열심을 가지고 해낼 수 있었다.
또한 앎에 대한 욕구가 모두에게 있었다. 누구 한명이 이해되지 않는 부분을 공유하면 함께 고민하고 googling하고 코드를 파고들어서 결국은 알아내어 함께 깨달음의 기쁨을 나누었다.

두번째. 협업 Tool을 잘 활용했다

두가지 tool을 활용했다.
과제를 공유하고 업무(?)를 나누고 각 단계를 마친 후 회고를 기록하기위해 notion을 활용했다. 실시간으로 서로가 작성하는 내용이 공유되기에, 서로의 업무 진행상황을 파악하기 수월했고, 각자가 구현한 코드를 공유하여 코딩중에 참고할 수 있었다.
작업을 관리하기 위해 git을 활용했다. 잘 구현된 코드, 개발중인 코드, 그리고 각자 구현중인 코드 파일들을 분리하여 관리하기 위해 master branch외에 thread/main branch(thread단계 메인branch), thread/dev branch(thread단계 개발진행용 branch), thread/* branch(팀원들의 각 branch) 를 나누어 관리하였다. 또한 각 기능단위로 각자 구현이 완료되면 PR(Pull Request)를 하고 함께 코드를 리뷰하여 수정이 필요한 부분들은 바로바로 수정하였다. 이러한 방법으로 debugging 시간을 크게 줄이는 등 작업의 효율을 높일 수 있었으며, 본인이 직접 구현하지 않은 코드에 대한 이해도도 높일 수 있었다.

세번째. 다른 사람들의 코드를 참고하지 않고 끝까지 구현했다

물론 어느정도 자료들을 찾아 solution을 참고하기는 했지만, 도저히 debugging이 어려운 상황이거나 과제가 이해되지 않는 경우가 아니라면 다른 사람들의 코드를 참고하지 않고 직접 구현했다. 직접 구현했기에 test case가 통과되었을 때 이루말할 수 없는 기쁨을 느꼈다. 또한 debugging 상황에 부딪히면서 각자의 개선점을 알게되고 개선할 수 있었으며, 해낼 수 있다는 자신감도 더 생길 수 있었다.

네번째. OS에 대해 이해가 조금은 생겼다

과제와 함께 제공된 git book과 keyword googling, 그리고 PintOS관련 ppt자료 등을 참고하고, PintOS에 이미 구현되어있는 코드를 이해한뒤 필요한 부분을 구현하는 방식으로 과제를 진행했다. 이 과정에서 컴퓨터에서는 시간이 어떻게 흐르며 어떻게 작업이 scheduling되는지 등 전체적인 흐름에 대해서 어느정도 이해할 수 있었다. 관련된 내용들을 deep하게 공부하기에는 시간이 부족했기에, 구현하는데 필요한 지식들 위주로 공부하고, 구현하는 과정에서 부딪히는 문제들에대해 고민하고 퍼즐을 맞춰가면서 이해도를 높였다(마치 BFS 알고리즘처럼 너비우선으로). 그래서 더 재미있고 속도감있게 과제들을 진행할 수 있었다.

이 부분에선 한편으론 아쉽기도 했다. 전체적인 흐름을 이해할 수 있는 점은 좋았지만, 더 개념적으로 깊이 공부하지 못한 부분이 너무 아쉬웠다. 물론 시간이 한정적인 자원이기에 앞으로도 이번과 같은 방식으로 진행해나가야 하겠다고 생각하지만, 가능하다면 조금 더 개념을 잡고 진행할 수 있다면 좋을 것 같다.

또한 개인적으로 느낀 점도 있었다.

1. 문제를 잘 정의하는 것이 이해도를 높인다. (내가 뭘 모르는걸까?)

이번 과제를 진행하면서 내 강점이라고 느낀 부분이 있었다. 나는 퍼즐이 맞추어지지 않으면 찝찝하고, 찝찝함이 남아있으면 진행이 되지 않는다. 그래서 찝찝한 부분을 해결하기 위해 필요한 부분을 파고들고, 고민하고, 사람들과 공유한다. 이러한 점이 문제를 명확하게 정의하게 하고, 이 이해를 바탕으로 코드를 빠르게 작성할 수 있게 한다. 내가 어느 부분에서 찝찝함이 남아있는지, 무엇을 모르는 것인지 고민하고 생각해내는 능력이 강점으로 작용하는 것 같다.

2. 협업의 중요성. 나 혼자 할 수 있는 일보다 훨씬 빠르게 더 많은 일을 할 수 있다.

협업은 어렵다. 살아온 인생이 다른 사람들이 모여 일을 나누어 해야하기 때문이다. 하지만 함께 문제를 이해하고 각자가 해야할일을 적당히 분담하고 필요에따라 충분히 소통이 이루어질 때 협업은 엄청난 힘을 발휘한다.

혼자 많은 줄의 코드를 모두 작성하려고 했다면 시간이 오래 걸렸을 뿐만 아니라 하나하나의 코드를 집중해서 작성하기 어려워 많은 debugging 요소가 발생했을 것이다. 또한 이 요소들은 발견하기 어려워 시간이 부족해졌을 것이다. 하지만 팀원들과 구현부들을 나누었기에 각자의 구현부에 집중할 수 있었고, 각자 더 깊이 이해하고 공유하여 모두가 더 깊이 이해할 수 있었다. 또한 코드리뷰를 통해 많은 debugging 요소들을 사전에 처리할 수 있었다.

일주일간 정말 많이 성장했다. OS를 배우는 것도, 과제를 진행하는 것도, 팀과 협업하는 것도 너무나 즐거웠고 의미있었다. 그렇기에 앞으로의 PintOS가 더욱 기대된다. 여전히 두려움이 있지만 이 기대감을 계속 유지해서 끝까지 해내고싶다.

profile
#코딩 #개발 #몰입 #꾸준함

0개의 댓글