Page와 Frame을 대강 알 수 있었던 이전 글에서 이제 실질적으로 작동을 시키는 형태로 아이디어를 이용해보자. 운이 좋게도 우리는 기존 프로젝트를 도로 작동시켜야한다는 전제조건이 있다. 이걸 복구 하는 과정을 보자.
2025.05.30 - [구현하기] - PintOS P3 #2 : Page/Frame 정리
PintOS P3 #2 : Page/Frame 정리
VM을 구현하기 전에 Page와 Frame에 대해 좀 더 빡빡하게 굴어야한다는 말에 간만에 질문 수준을 높인 질의응답을 나의 새로운 삼촌 Per에게 도움을 받을 수 있었다. 깃북에서 파고 들기 위한 시도를
hyeonistic.tistory.com
이 글에서 이어진다.
_________________________________
< ULTRA TEAM-8TH ctOS Boot complete. >
---------------------------------
\ ^__^
\ (oo)\_______
(__)\ )\/\
||----w |
|| ||
기존 조의 코드는 모든 테스트 케이스를 완수하지 못했고, 또 어떻게 보면 야매로 넘어갔던 부분도 없지 않아 있어서, 이번에 구성된 조에서는 제일 완성도 있는 코드를 사용하기로 했다. 코드 패턴이 다를지언정, 그렇게 큰 염려가 날 정도로 못 읽을 수준도 아니라서 좋다. 내심 실어간다는게 아쉽다고 여기지만 이제 잘하면 된다.
자, 프로젝트가 2에서 3으로 변하면서 기존 프로젝트가 작동이 안된다. 그러면 기존 프로젝트랑 어떻게 달라졌는지를 봐야한다. VM 상태의 프로젝트를 열어서 바로 테스트를 돌리면 일단 처음엔 Fail to launch initd 를 바로 볼텐데, 그럼 이에 대한 초기화를 수행하는 process.c를 보자.
이 파일은 load_segment, setup_stack 두 메서드가 사실 두 개 선언되어있고 내용이 꽤 다르다. 우리는 VM에서 작성하고 있기 때문에, 기존 Proj 1, 2에서 다루었던 두 메서드의 모양과 다른 내용을 사용해야만 한다. 그게 뭐 어떤지 한번 보니..
static bool load_segment (struct file *file, off_t ofs, uint8_t *upage,
uint32_t read_bytes, uint32_t zero_bytes, bool writable) {
ASSERT ((read_bytes + zero_bytes) % PGSIZE == 0);
ASSERT (pg_ofs (upage) == 0);
ASSERT (ofs % PGSIZE == 0);
while (read_bytes > 0 || zero_bytes > 0) {
(...)
void *aux = NULL;
if (!vm_alloc_page_with_initializer (VM_ANON, upage,
writable, lazy_load_segment, aux))
return false;
(...)
}
return true;
}
/* Create a PAGE of stack at the USER_STACK. Return true on success. */
static bool setup_stack (struct intr_frame *if_) {
bool success = false;
void *stack_bottom = (void *) (((uint8_t *) USER_STACK) - PGSIZE);
// 니가 해
return success;
}
#endif /* VM */
심각하다. load_segment 그리고 스택의 초기화를 직접 해주게 되었다.
우리는 process_exec이 작동되고 이러쿵 저러쿵 하면 User Program이 작동하는 것을 안다. 그렇다는 것은 기존 process.c에서의 API를 호출하는 식의 코드를 크게 변경 할 것은 없고, 그 이름으로 불러진 내용들이 어떤게 디테일하게 바뀌었느냐를 고민해야한다는 것이다.
즉, 뭘 고민해야 할지 많은 부분에서 정해져있다. 의외로 뭘 해야 할지 이전의 프로젝트에 비교했을때는 덜 막막할 것이다 :
int process_exec (void *f_name) {
` ...
process_cleanup (); // 여기서 SPT_kill 불림
...
success = load (file_name, &_if); // 여기서 load_segment 불림. 좀 많이 직접 해야함
...
argument_stack(parse, count, &_if.rsp); // 여기서 파싱함
...
do_iret(&_if);
NOT_REACHED();
}
load는 load_segment를 부르고, 그 안에서 vm_alloc_page_with_initializer를 불러 페이지 상태에 따른 적절한 초기화 메서드로 타입이 정해지고 페이지가 실물을 갖게된다. 그렇게 만들어진 페이지는 SPT에 담기고, 머신의 실제 작동 과정에서의 page fault는 SPT에서 찾아보자! 라는 것과 동의어가 됐으니, SPT에서 찾아서 반환하는 내용도 필요하다. 마지막으로 필수적인 4KB의 스택을 할당하여 확장을 논하기 전 우선 급한 불을 끄자.
위 문장에 필요한게 다 들어갔다.
- load_segment는 페이지 크기 단위의 순회를 돈다. 페이지 하나가 가리켜질 때 정보 누락이 없이 남김없이 챙겨서 alloc_page_with_initializer에 넘기는걸 목표로 하자.
- SPT에 담기는 영역을 논하려면 SPT 초기화, SPT Insert, Find 해서 적어도 세 개는 구현해야겠다.
- page fault가 발생하면 SPT에서 찾는 내용으로 연결 시켜야겠다. 찾으면 프레임에 담아다가 올리는 영역도 필요할 것이다.
- 그리고 setup_stack을 이용한 스택 설정까지 마치면 진짜 정말 되는 것이다.
여기까지 하면 작동한다 너의 Operating System. 다음글에서 이 네 개를 구현 하겠다.
'구현하기' 카테고리의 다른 글
PintOS P3 #A : Clock Algorithm (0) | 2025.06.04 |
---|---|
PintOS P3 #4 : SPT를 비롯한 구현 (0) | 2025.06.03 |
PintOS P3 #2 : Page/Frame 정리 (3) | 2025.05.30 |
PintOS P3 #1 : Virtual Memory 서론 (0) | 2025.05.29 |
PintOS P2 #6 : System Call : Fork, Exec, Wait (0) | 2025.05.28 |