Pintos Project 2: System Call 에서 배운 점들 + 회고

이형준·2023년 6월 12일
1

TIL

목록 보기
34/37

  • 결국 만날 수 있었던 All test passed.. 이때의 감동이란 👍

PintOS의 두번째 지옥 탈출 🔱

매주 점점 더 힘들어지는 PintOS 의 두번째 프로젝트를 마무리했다. 모든 테스트를 통과한 유일한 팀이 되는 엄청난 경험을 하기도, 정글 입소 후 가장 큰 벽을 만나기도 했던 Project 2 였다. 눈 코 뜰 새 없이 바빠서 미뤄뒀던 회고록과 함께, 어떤 문제를 만났고 배울 수 있었는 지 적어보자.

이제는 나도 관리자 😎

기존에 PintOS 를 진행하며 작성했던 모든 코드들은 OS 커널의 한 부분이었다. 커널의 일부로서 시스템에 중요한 부분에 접근할 수 있는 특권을 가지고 실행되었던 것! 우리가 OS위에서 작동하는 유저 프로그램을 실행하기 시작하면 더 이상 그런 특권은 없어야 한다. 이번 프로젝트는 이런 상황을 핸들링하는 System Call 을 구현하는 것이 목적이다.

프로그램이 다양한 시스템 콜을 요청하면, 커널 모드에서 시스템 콜 넘버에 알맞은 동작을 해 주어야 한다. 프로세스를 종료하고, 파일을 열고, 자식 프로세스를 만들고.. 이런 다양한 시스템 콜을 하나하나 구현해 나갔다.

쉬운 놈, 어려운 놈, 이상한 놈 👪

다양한 시스템 콜을 구현하다 보면, 각 시스템 콜의 구현 난이도가 정말 하늘과 땅 차이임을 느낄 수 있었다. 기존에 우리가 구현했던 함수나 만들어져 있는 함수를 띡~ 호출만 해도 구현 끝인 녀석도 있고, 매우 많은 노력을 필요로 했던 녀석도 있었다. 개중에 가장 악질적인 녀석들은 Exec, Fork, Wait 요 세 녀석. 특히 ForkWait 두 개를 구현하는 데에만 며칠밤을 새우게 되었다 😂 두 시스템 콜은 어느 정도 상통하는 부분이 존재하기에, 서로가 구현이 잘 되어 있지 않다면 테스트를 통과하는 데에 어려움을 겪게 된다. 덕분에 이거 구현해봤다, 저거 구현해봤다.. 많은 시간을 날리게 되었지.

결국 핵심은 세마포어 ❗


프로젝트 2를 마치고 나서 우리 코드의 일부를 캡처한 사진이다. 영롱한 세 개의 세마포어가 보이는가?! 세마포어를 덜 사용하고 구현하는 방법도 있다고는 하는데, 그런 것을 할 실력은 못 되기에 🥲 이렇게나 많은 세마포어를 선언해놓고, 사용하게 되었다. 사담으로 PintOS 를 처음 진행할 때에는 thread 구조체가 굉장히 간단했는데, 이제는 한 페이지에 다 담을 수 없을 정도로 뚱뚱해졌다 ㅋㅋㅋ 아마 프로젝트 3에 들어가면 더 뚱뚱해 질 수도? 아무튼 다시 본론으로 돌아와서, ForkWait 의 핵심 문제는 결국 세마포어를 얼마나 이해하고, 잘 사용하는 지 였던 것 같다. Project 1 에서 지겹도록 사용했던 세마포어를 완벽히 이해하고, 적재적소에 사용해야 한다.

세마포어를 한 마디로 요약하면 기다림 이다. 수 많은 세마포어는 코드 곳곳에서 각자의 역할을 하고 있지만, 공통점은 어떠한 시점까지 누군가를 기다리게 하고, 꺠워주는 것! 그럼 어떤 시점에 누구를, 언제까지 재울 건데? 그것이 바로 이번 프로젝트의 가장 핵심 문제.

꺼진 불도 다시 보자 🔥

TDD(Test Driven Developement) 는 SW 개발에서 상당히 핫한 방법론이다. 말 그대로 테스트 주도 개발이라는 뜻인데, 테스트 케이스를 작성하고, 이를 통과하기 위한 코드를 작성하는 일련의 개발 방법이다. 현업에서만큼 빡빡한 테스트는 아니지만, 작성되어 있는 테스트를 통과하기 위한 코드를 짜야 하는 것은 같기 때문에, 어떻게 보면 PintOSTDD 의 체험판이라고도 할 수 있다.

이번 프로젝트를 진행하면서 TDD 를 진행하며 놓치면 안 될 부분을 알 수 있었는데, 바로 테스트만을 위한 코드를 짜지 말 것! 테스트는 테스트고, 결국 SW개발자는 목표로 하는 동작을 완벽히 수행할 수 있는 코드를 짜내야 한다. 테스트만을 바라보면 테스트 통과를 위한 꼼수(?)와 같은 느낌의 코드를 짜게 될 수도 있고, 목표로 했던 기능에 구멍이 날 수도 있다는 것을 꼭 인지해야 할 것 같다.

실제로 우리 조도 Project 1의 테스트를 모두 통과했기에, 해당 구현부에는 문제가 없을 거라 생각했다. 따라서 모든 디버깅을 이번 과제에서 수정한 부분에서만 수행했는데... 생각나는 모~든 디버깅들을 수행해도, syn-read 테스트 케이스가 통과되지 않는 것 😂 결국 방법을 찾지 못한 우리는 이 테스트를 통과한 코드와 우리의 코드가 어느 곳이 다른가 찾아보기로 했고, lock_acquire 함수에서의 치명적인 결함이 있었다는 것을 발견했다.

당연히 이쪽 코드는 전혀 고려하지 않았던 우리였기에 그야말로 충격 그 자체 😮 덕분에 삽질도 많이 했지만, 큰 교훈을 얻을 수 있었던 경험이었다.

메모리는 무한한 자원이 아니다 💎

주어진 테스트들을 통과하기 위해 고군분투하다 보면, 어느새 메모리에 대한 관리를 신경쓰지 못하게 된다. 우리 팀도 그랬고, 다른 팀들도 그랬고. 결국 마지막 남은 multi-oom 테스트를 통과하기 위해서는 이러한 메모리 문제들을 해결해야만 했다.

생각보다 메모리 문제는 해결하기 쉽지 않았다. 까딱하면 다른 메모리 영역을 침범하기도, 메모리 영역 자체를 벗어나기도 하는 등.. 일례로 구현 중 파일 디스크립터들을 담아놓을 테이블이 필요한데, 우리 조는 처음에 이를 포인터 배열로 고스란~히 thread 구조체 안에 넣어주었다. 이렇게 되면 약 64개의 포인터를 담아놓을 메모리가 각 스레드마다 필요해지는 건데, 구현 당시에는 이게 큰 문제가 없을 줄 알았다. 실제로 지금까지 메모리를 빡빡하게 검사하는 테스트가 없기도 했고 😂 당연하게도 이는 해당 테스트에서 펑펑 터져나갔고, 개선한 후에서야 힘들게 pass 를 마주할 수 있었다. 다음 프로젝트의 주제가 가상 메모리인 만큼 이러한 개념에도 익숙해질 필요가 있겠지.

두번 째 산맥을 넘고 나서 ⛰️

프로젝트 주차의 마지막의 마지막날까지 디버깅만 주구장창 하다가, 오늘이 되서야 숨을 고를 시간이 생겼다. 매일 매일 풀던 알고리즘 문제도 못 풀고, 블로그 글도 못 쓰고.. 잠깐 리프레시 하고, 새로운 팀원들과, 새로운 프로젝트를 마주하러 가야겠지. 정글! 🫡

정글에서의 시간은 늘 부족하고 학습량은 많다. 단, 너만 그런 것은 아니다.🕐 - 정글에서 살아남는 방법 10가지 중

profile
저의 미약한 재능이 세상을 바꿀 수 있을 거라 믿습니다.

1개의 댓글

comment-user-thumbnail
2023년 6월 27일

"테스트만을 위한 코드를 짜지 말 것" 너무 좋은 말이네요! 한 수 또 배워갑니다.🫡

답글 달기