PintOS
의 세 번째 프로젝트, 가상 메모리 주차가 공식적으로 종료되었다. 벌써 PintOS
와 함께한지 한 달이 채 넘어간다. 이젠 정말 미운 정 고운 정 다 들어버린듯?
프로젝트 3에선 어떤 것을 배웠고, 어떤 문제점을 만났었는지 정리해보자.
PintOS
는 우리가 공부해왔던 OS의 이론적인 부분을 코드로서 보여주고, 개선해 나갈 수 있는 경험을 제공한다. 다만 여기서 우리가 알아야 할 점은 PintOS
는 분명히 교육용 OS이고, 실제 OS와의 괴리가 분명히 존재한다. 예를 들어 메모리 구조가 특이하다던가, MMU
가 소프트웨어로 구현되어 있는 것 같은 것들. 특히나 가상 메모리 프로젝트를 진행하면, 이러한 괴리를 뼈저리게 느낄 수 있다.
따라서 배워온 OS적 지식들을 곧이 곧대로 적용시키려 한다면 PintOS
에 적응하는 데에 골머리를 앓을 것이다. 우리 팀도 그러한 부분이 있어왔고, 많은 동기들이 그랬다. ㅋㅋㅋ PintOS
를 이제 막 시작하는 학생 or 정글러라면 위와 같은 부분을 리마인드 한다면 프로젝트에 도움이 되지 않을 까 싶다.
정글러의 바이블, CS:APP
에는 가상 메모리는 컴퓨터 시스템에서 가장 위대한 아이디어 중 하나이다 란 말이 적혀 있다. 이론을 공부할 때는 와~ 이거 누가 생각했는지는 몰라도 대단하다~ 이정도였다면, 지금은 이거 없던 시절에 대체 소프트웨어를 어떻게 개발했냐??!! 하는 생각이 든다 😂 그만큼 현대 컴퓨터 시스템에서 없어서는 안될 핵심적인 추상화이고, 이번 PintOS
를 통해 이 녀석이 실제로 어떻게 보이지 않는 곳에서 고생하고 있는지 알 수 있었다.
이번 프로젝트의 주 무대가 되는 곳은 무려 메모리와 페이지 폴트이기에, 디버깅이 특히 어려운 느낌을 받았다. 단순히 printf
를 찍어봄으로써 어느 정도 흐름을 익히는 것이 가능했던 이전 프로젝트와는 달리, PintOS
의 메모리 구조에 대한 이해와 C코드의 흐름을 읽어내는 능력이 특히나 중요했다. 특히나 저번 주차 발표 때 같은 반 동기 누님 한 분이 소개해주신 PintOS
에서의 gdb
사용법과 gdb plugin pwndbg
사용법을 익혀 놓은 것이 큰 도움이 됐다. 아마 계속 printf
만 찍어보면서 했다면 안그래도 더딘 디버깅 정말 기어가듯 했을 듯?!
악명높은 PintOS
의 세 번째 프로젝트답게, VM
은 정말 자비없는 난이도를 자랑한다. 내 체감상 정말 구현해야 할 모든 부분이 어렵다라고 느껴질 정도. 그중에서도 우릴 너무나 힘들게 했던 여러 문제를 소개하려 한다.
PintOS
에서 페이지 폴트가 발생한다면, 폴트의 종류를 구별하는 데에 도움을 주는 각종 인자를 세팅한 후, vm_try_handle_fault
함수로 진입하게 된다. 이 함수는 정말 가상 메모리를 구현하는 동안 주구장창 보게 된다. 요구 페이지부터 스택 그로우까지 이 녀석을 거치지 않는 부분이 없어서.. 아무튼! 이 함수를 처음 마주한다면 정말 너무나 당혹스럽다. 이해할 수 없는 함수 인자들 + 커널단으로의 컨텍스트 스위칭으로 인한 난해함 + 어떤 코드를 적어내려가야할 지 모르겠는 막막함 삼박자가 합쳐진 마치 악마같은 녀석이다.
나를 당혹시켰던 첫 문제는 바로 함수의 인자들. 처음 세 개의 bool
타입 인자를 마주하고 나서, 난 이 세개가 페이지 폴트의 타입을 표시하는 인자들인 줄 알았다. 페이지 폴트에는 not_present
와, user
, writ
가 있구나! 이렇게 생각했던 거지.. 알고보니 이 세 녀석은 어떠한 상태들을 true
or false
를 통해 알려주는 녀석들일 뿐, 페이지 폴트의 종류를 완벽히 표시해주는 녀석들은 아니었다. 이는 vm_try_handle_fault
를 호출하는 page_falut
의 주석에서 뒤늦게 확인할 수 있었다. 이럴 때 마다 '진작 좀 뒤져볼걸..'하는 생각은 어쩔 수 없는듯 😂
두 번째 벽은 의외로 예외처리! 프로젝트 3의 많은 테스트 케이스들은 바로 이 함수에서의 빡빡한 예외처리를 요하는데, 이게 상당히 어려웠다. 뜬금없는 주소가 들어온다면 걸러주는 등의 작업을 해주어야 하는데, 이걸 그냥 '뜬금없는 주소'로 치부하기엔 문제가 좀 있다. "대충 여기서부턴 들어오면 안되니까 이 범위를 예외처리하자~" 하면 얄짤없이 다른 테케가 박살난다. 때문에 여기서 팀원들과 PintOS
의 유저 메모리 구조에 대한 공부를 열심히 한 후에야 잡을 수 있었다 🫡
stack_growth
만큼 순수 코드 작업이 적으면서 우리를 괴롭힌 녀석이 없었다. 얘도 사실 개념 자체는 별거 아니긴 한데.. 진짜 코드로 옮기는 과정이 난해하다. rsp
와 페이지 폴트가 일어나는 주소인 addr
과 같은 인자들이 어떻게 움직이는지 알아내는 것, 또 어떤 조건에서 stack_growth
를 호출하여 페이지 폴트를 해결할 지 정하는 것이 특히나 어려웠던 것 같다.
또 디버깅에서도 상당히 애를 먹었는데, 마지막 테스트 케이스인 pt_grow_stk_sc
가 뭘 해도 잡히지 않았다. 테케를 요약하면 65536바이트의 긴~ 문자열을 시스템 콜을 통해 쓰고, 읽어보며 시스템 콜을 통해서도 stack_growth
가 잘 일어나는 지 확인하는 테스트 케이스다. 다같이 머리 박아가며 열심히 디버깅을 하다가 팀원들은 먼저 방으로 돌아가고, 새벽에 혼자 코드를 골똘히 쳐다보다가 로직적으로 큰 결함을 발견했다! 재밌었던 점은 우리가 로직적으로 이해되지 않던 부분이 이 결함 해결을 통해서 하나하나 퍼즐처럼 맞춰지기 시작한 것!! 너무 기뻐서 팀원들 단톡에 막 자랑했던 기억이 있다 ㅋㅋㅋㅋ
찾아낸 결함은 다름아닌 stack_growth
함수에서 페이지를 alloc
요청하는 주소의 문제였다. 단순히 스택 페이지가 부족하니 페이지 폴트 난 addr
기준으로 페이지를 요청하면 되겠지? 하고 그냥 addr
을 pg_round_down
한 값에 페이지를 요청한 것이 화근!! 이런 방식으로 진행하면 위의 테스트 케이스와 같이 rsp
와 addr
이 한 번에 한 페이지 이상 쭉~ 크게 내려오게 되는 경우에 문제가 발생한다. 바로 가운데 부분의 페이지들이 텅텅 비게 되는 것. 이를 발견하고 페이지를 alloc
요청하는 기준을 (스택 페이지 주소 - 페이지 사이즈)로 수정했다. 왜 이걸 놓치고 갔을까 땅을 치고 후회했다. 이런 치명적인 실수를 범해버리니 계속 페이지가 없어서 터져버렸고, 우리는 애꿎은 다른 함수들만 쳐다보고 있었으니 😂😂
팀원들과 계속 토론해보고, 실제 적용해보고, 터지고, 토론해보고, gitbook
도 보고... 무한반복 ♾️ 한 끝에야 어느 정도 가닥을 잡아낼 수 있었던 부분. 역설적으로 이 과제를 수행하는 동안 진행한 열띤 토론과, 디버깅의 참맛에 PintOS
에 다시금 흥미가 붙은 팀원도 있었다. 우리 팀과 애증의 관계라고도 할 수 있는 녀석!
실력과 개발자로서의 마인드셋에 있어서는 확실한 성장을 이룩하고 있다는 것이 느껴진다. 하지만 이와 동시에 PintOS
의 압도적인 난이도와 작업량 앞에 속칭 마음이 꺾일 뻔 한 적도 많다. 이럴 때 마다, 같은 것에 대해 고민하고 같이 나아가는 같은 반 동기들이 큰 도움이 되곤 한다. '아.. 정말 모르겠다 그만할까?' 싶을 때에도 '야 나 싹 다 밀고 처음부터 다시한다' 하는 친구가 옆에 있는데, 어떻게 그만둘 수 있겠어 😁
벌써 절반 넘게 정글을 헤쳐왔다. 지금까지 나의 정글에서의 행보가 100% 만족스럽지는 않지만, 그래도 나 치고는 나쁘지 않게 해왔다는 생각이 든다. 지금까지 고생했고, 앞으로 좀 더 고생하자.
이번 주에는 간만에 부모님과 동생을 만났다. 외로움을 별로 안탄다고 생각하는 사람인데도, 오랜만에, 그것도 고생한 후에 보니까 엄청 반갑더라고 😀 나만의 무기 만들기까지 이제 한 주 남았다. 남은 PintOS
과제들을 마무리하고, 슬 나만무를 위한 준비도 해야겠지!
😎멋진 글이네요. 읽고 힘이 났습니다! 화이팅입니다!!👍