[가상메모리] PintOS의 메모리 관리(feat. SPT)

Serye·2023년 5월 15일
1

운영체제

목록 보기
6/7
post-thumbnail

서론

1주차의 발표를 준비하는데 아직 첫번째 단계인 Memeory Management를 구현 중이었고, 개념마저도 확실하게 알고 있지 못한 상태였다. 그래서 주제를 SPT로 알아보는 페이지 적중과 페이지 오류의 과정으로 정했다. 처음에는 SPT가 사용된 설명 자료를 찾기 힘들어 진도가 나가지 않았다. 오후 1시에 시작해서 6시 전에 끝내겠다는 계획이 무색하게 발표 준비는 10시쯤에 끝이 났다. 검색을 해보면 보조 테이블은 페이지 테이블에 없는 정보를 담고 있다고 했고, 확실히 이해가 되지 않았다. 그래서 근거를 찾아 PintOS의 코드를 하나 하나 찾아보기 시작했고, 결론을 내릴 수 있었다.

PintOS의 메모리 관리

핀토스는 위와 같은 구조를 가지고 가상 메모리와 물리 메모리를 매핑한다. 가상 메모리와 물리 메모리의 매핑은 pml4라는 페이지 테이블 구조를 사용하여 이뤄진다. pml4는 Page Map Level 4의 준말로, 4단계의 페이지 테이블로 이뤄진 트리 구조이다. 가상 주소를 이용해 물리 메모리에 접근하는 과정은 가상 주소의 구성 요소인 Virtual Page Number와 Virtul Page Offset을 사용하여 진행되며, 최종적으로 물리 메모리에 도달하게 된다. 이렇게 도달한 물리 메모리에서 원하는 데이터를 읽어올 수 있다.

페이지 적중(Page Hit)

PintOS의 thread 구조체에는 pml4라는 멤버가 있고 해당 멤버는 4계층 중 한 단계인 pml4 테이블을 가리킨다.

struct thread
{
#ifdef USERPROG
	uint64_t *pml4; // <---------------
#endif
#ifdef VM
	struct supplemental_page_table spt;
#endif
	struct intr_frame tf; 
	unsigned magic;		  
};

메모리 접근 요청이 있을 때, 페이지 테이블에서 해당 가상 주소에 대응하는 물리 주소를 찾게 된다. 이를 페이지 적중이라 하며, 이를 통해 원하는 물리 주소에 접근할 수 있게 된다.

페이지 오류(Page Fault)

프로세스가 현재 메모리에 존재하지 않는 페이지를 액세스하려 할 때, 즉 페이지 테이블에서 해당 페이지에 대한 정보를 찾을 수 없을 때, 페이지 폴트가 발생한다.

페이지 폴트가 발생하면 페이지 폴트가 발생한 원인을 파악한다. 1) 원인이 메모리에 존재하지 않는 페이지에 접근하려 한 것이라면, 핸들러는 해당 페이지를 메모리로 로드한다. 2) 원인이 권한 문제 등 다른 이유라면, 핸들러는 예외 처리를 수행하게 된다.

전자일 경우 페이지 폴트 처리기는 SPT(Supplemental Page Table)를 확인하여 페이지 폴트에 대한 추가 정보를 수집한다.

thread 구조체 안에는 spt를 구현하기 위한 hash 구조체가 있다.

struct thread
{
#ifdef USERPROG
	uint64_t *pml4; 
#endif
#ifdef VM
	struct supplemental_page_table spt; // <---------------
#endif
	struct intr_frame tf; 
	unsigned magic;		  
};
struct supplemental_page_table {
	struct hash hash_table;
};

이 hash 구조체에는 buckets라는 연결 리스트가 존재한다.

struct hash {
	size_t elem_cnt;            
	size_t bucket_cnt;          
	struct list *buckets; // <---------------
	hash_hash_func *hash;       
	hash_less_func *less;       
	void *aux;                 
};

아래 코드를 참고하면 bucket은 buckets의 요소라는 것을 알 수 있다.

void hash_apply (struct hash *h, hash_action_func *action) {
	...

	for (i = 0; i < h->bucket_cnt; i++) {
		struct list *bucket = &h->buckets[i]; // <---------------
		struct list_elem *elem, *next;

		...
	}
}

그리고 아래 코드를 보면 bucket은 hash_elem은 list_elem으로 이뤄진 연결리스트라는 것을 알 수 있다.

struct page {
	const struct page_operations *operations;
	void *va;              
	struct frame *frame;   

	struct hash_elem hash_elem; // <---------------

	...
	};
};
struct hash_elem {
	struct list_elem list_elem;
};
static void insert_elem (struct hash *h, struct list *bucket, struct hash_elem *e) {
	h->elem_cnt++;
	list_push_front (bucket, &e->list_elem); // <---------------
}

hash_elem을 포함하고 있는 page 구조체에는 va가 있습니다.

struct page {
	const struct page_operations *operations;
	void *va; // <---------------              
	struct frame *frame;   

	struct hash_elem hash_elem;

	...
	};
};

이러한 구조의 보조 페이지 테이블에서 페이지 상태를 확인하고, 필요한 경우 새로운 페이지를 할당하거나 기존 페이지와 교체합니다. 이를 통해 물리 메모리에 요청한 페이지를 로드하고, 페이지 테이블 엔트리를 업데이트합니다. 페이지 폴트 처리가 완료되면 해당 프로세스의 명령을 재실행하여 원래 요청한 작업을 계속합니다. 이렇게 보조 페이지 테이블은 가상 메모리와 물리 메모리 사이의 매핑을 보조하는 중요한 역할을 수행하게 됩니다.

profile
🎤 📷 ❄️ 🌊

0개의 댓글