[SW사관학교 정글] Week11. PintOS Virtual Memory

Youngeui Hong·2023년 10월 23일
0

SW사관학교 정글 7기

목록 보기
12/16

💚 mmap

📌 mmap이란?

void *mmap(void addr[.length], size_t length, int prot, int flags, int fd, off_t offset);

리눅스의 mmap은 파일이나 I/O 장치를 가상 메모리에 매핑할 때 사용되는 시스템콜이다.

위 코드의 인자들을 살펴보면 가상주소공간의 addr부터 length 바이트 길이만큼 매핑하겠다는 의미이다. 파일을 매핑하는 경우 fd에 매핑하고자 하는 파일의 디스크립터를 넣어주면 되고, 파일의 중간부터 매핑을 하려면 offset에 메모리 매핑을 시작할 지점을 넣어주면 된다.

이제 이 시스템 콜을 pintos에서 직접 구현해보자.

📌 do_mmap

do_mmapload_segment와 유사한 방식으로 구현할 수 있다. 파일의 내용을 읽어와서 페이지를 초기화한다는 점에서 거의 동일하기 때문이다.

파일이 여러 페이지에 걸쳐 매핑되었다면 나중에 이 페이지들을 모두 해제해줘야 하기 때문에, 몇 페이지에 걸쳐 파일이 매핑이 되었는지 알 수 있도록 우리 조는 page_infopage_count 필드를 추가해주었다.

그리고 mmap에서 file과 관련해서 유의해야 할 점은 파일을 닫은 후에도 메모리 매핑이 유지되어야 한다는 점이다. 우리는 파일이 닫힌 후에도 mmap이 문제 없이 파일을 읽고 쓸 수 있도록 file_reopen() 함수를 사용했다. 그리고 파일의 중간부터 매핑하는 경우를 위해 file_seek() 함수를 사용해서 파일울 읽기 시작하는 지점을 offset으로 변경하였다.

void *do_mmap(void *addr, size_t length, int writable, struct file *file, off_t offset)
{
	uint8_t *upage;
	off_t ofs;
	uint32_t read_bytes, zero_bytes;
	int pg_count;

	struct supplemental_page_table *spt = &thread_current()->spt;
	if (spt_find_page(spt, addr) != NULL)
		return NULL;

	struct file *reopened_file = file_reopen(file);
	file_seek(reopened_file, offset);

	upage = addr;
	ofs = offset;
	read_bytes = file_length(reopened_file);
	zero_bytes = (ROUND_UP(read_bytes, PGSIZE) - read_bytes);
	pg_count = (length / PGSIZE);

	while (read_bytes > 0 || zero_bytes > 0)
	{
		size_t page_read_bytes = read_bytes < PGSIZE ? read_bytes : PGSIZE;
		size_t page_zero_bytes = PGSIZE - page_read_bytes;

		struct page_info *page_info = (struct page_info *)malloc(sizeof(struct page_info));
		page_info->file = reopened_file;
		page_info->ofs = ofs;
		page_info->upage = upage;
		page_info->read_bytes = page_read_bytes;
		page_info->zero_bytes = page_zero_bytes;
		page_info->writable = writable;
		page_info->page_count = pg_count;

		/* Allocate page */
		if (!vm_alloc_page_with_initializer(VM_FILE, upage, writable, lazy_load_segment, page_info))
			return false;

		read_bytes -= page_read_bytes;
		zero_bytes -= page_zero_bytes;
		upage += PGSIZE;
		ofs += page_read_bytes;
	}

	return addr;
}

📌 mmap과 lazy loading

do_mmap 또한 lazy loading 방식을 사용한다. 즉, mmap은 가상 메모리와 파일을 매핑만 해놓고, 메모리에 로드하는 작업은 따로 하지 않는 것이다. 파일의 내용을 실제로 메모리에 로드하는 것은 파일의 지점에 엑세스를 하려고 할 때 발생한다.

mmap 이후 어떻게 데이터가 로드되는지 코드의 흐름을 살펴보면, mmap된 가상주소를 통해 파일을 읽으려고 접근하면 아직 물리 메모리가 없어서 page fault가 발생하게 된다. 그러면 page_fault()vm_do_claim_page() 함수를 호출해서 물리 메모리를 할당 받고 vm_do_claim_page()file_backed_swap_in()을 호출해서 가상 주소에 파일의 내용을 읽어오게끔 한다.

/* Swap in the page by read contents from the file. */
static bool file_backed_swap_in(struct page *page, void *kva)
{
	struct file_page *file_page = &page->file;

	file_read_at(file_page->file, page->va, file_page->read_bytes, file_page->offset);

	return true;
}

0개의 댓글