PintOS P3 #5 : SPT를 비롯한 구현 #2

한 제목에 P3 이면서 #5 이면서 #2 인게 참 헷갈리만도 하겠지만, 너가 다루고 있는 PintOS의 PML4는 이런 구조이다. Project 3 의 5번 글의 2번째 시리즈 글이라고 영역전개를 해야 슬슬 알아들을만하죠? 우린 모두 헷갈림의 시대에 살고있다.


2025.06.03 - [구현하기] - PintOS P3 #4 : SPT를 비롯한 구현

 

PintOS P3 #4 : SPT를 비롯한 구현

기존 글에서 구현 해야 할 네 가지를 다루었다. 이 네 가지는 너무 극 요약 한 것에 불과해서, 실제 구현에서만 다룰 수 있는 이게 뭐야 싶은 상황도 보게 될 것이다. 하지만 언제 그랬냐는듯 너

hyeonistic.tistory.com

이 글에서 이어진다.

 

우린 기존 작업에 이어 Project 2 테스트 케이스의 모든 작동을 확인해야한다. 그 대상들은 Fork, Wait, Exec이다. 그리고 상대적으로 살인적인 난이도를 자랑했던 그 특유의 구현과 달리 여기서는 그 기반을 깔아주기 위해선 그렇게 많은걸 해줘야할게 없다.

그도 그럴것이, 이러한 자식 쓰레드를 만드는 과정에서 이용되는 코드 중 VM으로 넘어오면서 빈 칸이 생긴건 한 칸이기 때문이다. 그 주인공은 spt_copy 함수이다.

#ifdef VM
	supplemental_page_table_init (&current->spt);
	if (!supplemental_page_table_copy (&current->spt, &parent->spt))
		goto error;
#else
	if (!pml4_for_each (parent->pml4, duplicate_pte, parent))
		goto error;
#endif

이 내용은 process.c의 __do_fork 함수 내에 포함되어있다. Project 2까지는 pml4_for_each 가 대신 역할 했고, 지금은 위에 위치한 spt 계열의 함수들만이 작동하게 되고, spt_copy에 한정해선 유일하게 작동하는 곳이다. 이 곳을 완성하기 위한 내용을 다루겠다.

 

supplemental_page_table_copy 함수는 인자로 복사 할 곳, 복사 받을 곳을 받는다. 그리고 복사 성공 여부를 반환한다.

bool supplemental_page_table_copy
	(struct supplemental_page_table *dst, struct supplemental_page_table *src) {
복사 할게 없는 경우, 어쨌든 복사는 된거잖아? 복사 완료를 반환한다.
if(hash_empty(&src->main_table)) return true;

 

테이블 자체가 아예 잘못 되었을거라는 전제는 하지 않는다. 이건 양 테이블이 다 멀쩡하겠거니 전제하고 작동하는 것이기 때문이다. 이제 바로 반복문을 돌것이다. PintOS의 해시 테이블은 반복문에 활용 할 수 있는 구조체를 제공 하고 있다. hash_iterator가 그 내용이다.
	struct hash_iterator i;
    hash_first (&i, &src->main_table);
	while (hash_next (&i))
	{
		// src->main_table이라는 해시테이블에서 아이템을 특정 할 때마다 뭘 시킬건가요??
	}
	return true;

 

여기서부터는 while문 안에 있는 내용이다.

좋다.
이 테이블에 들어있는 것은 어쨌든 Page이기 때문에, 원소를 넣을 수 있는 형태로 특정 할 수 있어야한다.
이어서, Page의 형태에 따라 해줘야 할 것도 조금은 달라 질 수 있다. 그러니 분기점을 위한 정확한 기준을 사전에 마련하자.
struct page *srcPage = hash_entry(hash_cur (&i), struct page, page_hashelem);
enum vm_type type = page_get_type(srcPage);
void *upage = srcPage->va;
bool writable = srcPage->writable;

 

type을 기준으로 갈라지는 내용은 다음과 같다.. 근데, VM_FILE에 대해선 당장 다루지 않아도 된다.
if(type == VM_UNINIT)
{
	//vm_initializer *init = srcPage->uninit.init;
	//void *aux = srcPage->uninit.aux;
}
else if(type == VM_ANON)
{
	if(!vm_alloc_page(type, upage, writable)) return false;
	if(!vm_claim_page(upage)) return false;
	struct page *newPage = spt_find_page(dst, upage);
	if(srcPage->frame != NULL)
		memcpy(newPage->frame->kva, srcPage->frame->kva, PGSIZE);
}

왜냐하면, VM_FILE은 mmap으로만 생성되는데, 이 copy 행위는 어쨌든 fork 과정에서 일어난다. 근데 mmap 영역은 fork시 SPT를 통해 따라가지 않는다. 각 프로세스는 알아서 mmap()을 호출해서 써야한다는 것이다. 그래서 특별히 복사가 필요없다고 할 수 있는 것이다. 심지어 UNINIT 조차 빼버렸는데 이건 좀 실수같기도 하고.. 어쨌든 작동이 되니까 줄일 수 있는 코드이니 좋은거 아닐까?..

 

합쳐진 하나의 SPT_copy는 이런 모양이다 :
bool supplemental_page_table_copy
(struct supplemental_page_table *dst,struct supplemental_page_table *src ) {
	// src에서 dst로 supplemental_page_table 복사하기.
	
	if(hash_empty(&src->main_table)) return true; // 복사할게 없네용 : true 반환
	struct hash_iterator i;
	hash_first (&i, &src->main_table);
	while (hash_next (&i))
	{
		struct page *srcPage = hash_entry(hash_cur (&i), struct page, page_hashelem);
		enum vm_type type = page_get_type(srcPage);
		void *upage = srcPage->va;
		bool writable = srcPage->writable;

		if(type == VM_UNINIT)
		{
			//vm_initializer *init = srcPage->uninit.init;
			//void *aux = srcPage->uninit.aux;
		}
		else if(type == VM_ANON)
		{
			// 실제 작동 여기만 함 . 딴데 보지 마세요
			if(!vm_alloc_page(type, upage, writable)) return false;
			if(!vm_claim_page(upage)) return false;
			struct page *newPage = spt_find_page(dst, upage);
			if(srcPage->frame != NULL)
			memcpy(newPage->frame->kva, srcPage->frame->kva, PGSIZE);
		}
	}
	return true;
}

이정도면 Fork, Exec, Wait를 예전과 같이 복구 했다. 다음 글 부터는 Project 3 테스트 케이스에 대해 본격적인 작동을 논해보자.

'구현하기' 카테고리의 다른 글

PintOS P3 #7 : 메모리 교체와 Swap  (1) 2025.06.09
PintOS P3 #6 : Stack Growth  (0) 2025.06.09
PintOS P3 #A : Clock Algorithm  (0) 2025.06.04
PintOS P3 #4 : SPT를 비롯한 구현  (0) 2025.06.03
PintOS P3 #3 : 뭐부터 할지 논하기  (1) 2025.06.03