[TIL/06.05] PintOS VM - Memory Management

CHO WanGiยท2025๋…„ 6์›” 5์ผ

KRAFTON JUNGLE 8th

๋ชฉ๋ก ๋ณด๊ธฐ
65/89

์ฐธ๊ณ  ์ž๋ฃŒ

https://casys-kaist.github.io/pintos-kaist/project3/introduction.html
https://oslab.kaist.ac.kr/pintosslides/

  • ๐Ÿšจ ๋‘๋ฒˆ์งธ ๋งํฌ์ธ Kaist EE ์ž๋ฃŒ๋Š” 64๋น„ํŠธ๊ฐ€ ์•„๋‹Œ 32๋น„ํŠธ ๊ธฐ์ค€์œผ๋กœ ์„œ์ˆ ๋˜์–ด ์žˆ์Œ

์š”๊ตฌ์‚ฌํ•ญ

AS-IS

  • ํ˜„์žฌ ์‹คํ–‰ ๊ฐ€๋Šฅํ•œ ํŒŒ์ผ ์ „์ฒด๊ฐ€ ๋ชจ๋‘ ๋ฉ”๋ชจ๋ฆฌ์— ์ ์žฌ
    • ์‹คํ–‰ํ•˜์ง€ ์•Š๋Š” ๋ถ€๋ถ„๋„ ๋ฉ”๋ชจ๋ฆฌ์— ์ ์žฌ๋˜์–ด ๋น„ํšจ์œจ ๋ฐœ์ƒ
  • ํ”„๋กœ์„ธ์Šค๊ฐ€ ์Šคํƒ์„ ๋‹ค ์ฑ„์›Œ Overflow๊ฐ€ ๋ฐœ์ƒํ•˜๋ฉด ํ”„๋กœ์„ธ์Šค๋ฅผ Kill

TO-BE

  • page fault ํ•ธ๋“ค๋Ÿฌ ํ•จ์ˆ˜๋ฅผ ์ˆ˜์ •, Demand Paging(lazy loading) ๊ตฌํ˜„
  • Supplemental Page Table ๊ตฌํ˜„
  • Frame Table & Swap Table ๊ด€๋ฆฌ

PintOS Page Fault Flow Chart

  • ์š”์•ฝ : Page Fault ๋ฐœ์ƒ -> vm_try_fault_hander -> uninit_initialize -> lazy_load_segment -> pml4_set_page

์ด๊ฑธ ์ข€๋” ์‰ฝ๊ฒŒ ๊ทธ๋ฆผ์œผ๋กœ ๊ทธ๋ ค๋ณด๋ฉด

์›๋ž˜๋ผ๋ฉด kill ํ•˜๋Š” ๊ฒƒ์„
1. ์ฒซ๋ฒˆ์งธ Page Fault ๊ฐ€ ๋ฐœ์ƒ
2. SPT(Supplement_page_table)์„ ํƒ์ƒ‰ํ•˜์—ฌ ๋ฉ”๋ชจ๋ฆฌ์— ์˜ฌ๋ผ๊ฐˆ ๋ฐ์ดํ„ฐ๊ฐ€ ์žˆ๋Š”์ง€๋ฅผ ํƒ์ƒ‰
3. Demand Paging

  • ํ•„์š” ๋ฉ”๋ชจ๋ฆฌ๊ฐ€ ๋ฌผ๋ฆฌ ๋ฉ”๋ชจ๋ฆฌ์— ์˜ฌ๋ผ๊ฐ€ ์žˆ๋‹ค๋ฉด Page Fault
  • struct page ์—์„œ ํ•ด๋‹นํ•˜๋Š” ํ•„์š” ๋ฉ”๋ชจ๋ฆฌ ์žˆ๋Š”์ง€๋ฅผ ํ™•์ธ ํ•˜๊ณ  ์ฃผ์†Œ ํ™•์ธ
  • ์žˆ๋‹ค๋ฉด Physical Frame์„ ํƒ์ƒ‰ํ•˜์—ฌ ํ• ๋‹น
  • disk์—์„œ data๋ฅผ ๋กœ๋”ฉ => lazy_load_segment
  • kernel Virtual Address์™€ ๋ฌผ๋ฆฌ ๋ฉ”๋ชจ๋ฆฌ๋ฅผ 1:1 ๋งคํ•‘,
  • pml4 ์— ์ถ”๊ฐ€ ํ•œ๋‹ค.

๋”ฐ๋ผ์„œ ์œ„ ๊ทธ๋ฆผ์ฒ˜๋Ÿผ SPT ๊ตฌํ˜„ -> Frame ๊ด€๋ฆฌ -> Anonymous Page & lazy loading์œผ๋กœ ์ง„ํ–‰ํ•œ๋‹ค.

SPT ๊ตฌํ˜„

SPT ์ •์˜

//vm.h
/* Representation of current process's memory space.
 * We don't want to force you to obey any specific design for this struct.
 * All designs up to you for this. */
struct supplemental_page_table
{
	struct hash spt_pages;
};

supplemental_page_table ๊ตฌ์กฐ์ฒด์— ํ•ด์‹œ ์ž๋ฃŒ๊ตฌ์กฐ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ SPT๋ฅผ ์ •์˜ํ•œ๋‹ค.

Key๋Š” VA๊ฐ€ ๋ ๊ฒƒ์ด๊ณ , Value๋Š” hash_elem์ด ๋  ๊ฒƒ.
๊ทธ๋Ÿผ ์ด hash_elem์€ ์–ด๋””๋‹ค๊ฐ€ ์„ ์–ธํ•˜๋ƒ๋ฉด Page ๊ตฌ์กฐ์ฒด์— ์„ ์–ธํ•œ๋‹ค.

// vm.h
/* The representation of "page".
 * This is kind of "parent class", which has four "child class"es, which are
 * uninit_page, file_page, anon_page, and page cache (project4).
 * DO NOT REMOVE/MODIFY PREDEFINED MEMBER OF THIS STRUCTURE. */
struct page
{
	const struct page_operations *operations;
	void *va;						 /* Address in terms of user space */
	struct frame *frame; /* Back reference for frame */

	/* Your implementation */

	/* Hash ์ž๋ฃŒ๊ตฌ์กฐ ํ™œ์šฉ ์œ„ํ•œ ๋ฉค๋ฒ„๋ณ€์ˆ˜ ์ถ”๊ฐ€ */
	struct hash_elem hash_elem; /* Hash table element. */
	bool writable;
	/* Per-type data are binded into the union.
	 * Each function automatically detects the current union */
	union
	{
		struct uninit_page uninit;
		struct anon_page anon;
		struct file_page file;
#ifdef EFILESYS
		struct page_cache page_cache;
#endif
	};
};

SPT ์ดˆ๊ธฐํ™”

ํ•ด์‹œ ์ž๋ฃŒ๊ตฌ์กฐ์ด๊ธฐ ๋•Œ๋ฌธ์— GitBook์˜ APPENDIX์˜ HASH FUNCTION ๋ถ€๋ถ„์„ ์ฐธ๊ณ ํ–ˆ๋‹ค.


/* Returns a hash value for page p. */
unsigned
page_hash(const struct hash_elem *p_, void *aux UNUSED)
{
	const struct page *p = hash_entry(p_, struct page, hash_elem);
	return hash_bytes(&p->va, sizeof p->va);
}

/* Returns true if page a precedes page b. */
bool page_less(const struct hash_elem *a_,
							 const struct hash_elem *b_, void *aux UNUSED)
{
	const struct page *a = hash_entry(a_, struct page, hash_elem);
	const struct page *b = hash_entry(b_, struct page, hash_elem);

	return a->va < b->va;
}

/* Initialize new supplemental page table */
void supplemental_page_table_init(struct supplemental_page_table *spt UNUSED)
{
	// ์ธ์ž๋กœ ๋ฐ›์€ spt์˜ pages ํ•ด์‹œ ํ…Œ์ด๋ธ”์„ ์ดˆ๊ธฐํ™”
	hash_init(spt, page_hash, page_less, NULL);
}

hash_initํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•œ ํ—ฌํผ ํ•จ์ˆ˜๋ฅผ ๋‘๊ฐœ ์„ ์–ธํ•ด์ฃผ๊ณ ,
SPT์˜ ์ดˆ๊ธฐํ™”๋ฅผ ๋‹ด๋‹นํ•˜๋Š” supplemental_page_table_init ํ•จ์ˆ˜์— ํ™œ์šฉํ•˜์˜€๋‹ค.

SPT ๊ด€๋ จ ํ•จ์ˆ˜

  • spt_find_page
    spt์—์„œ ์ธ์ž๋กœ ๋“ค์–ด์˜ค๋Š” VA์— ํ•ด๋‹นํ•˜๋Š” ํŽ˜์ด์ง€๋ฅผ ํ•ด์‹œ ์ž๋ฃŒ๊ตฌ์กฐ์ธ SPT์—์„œ ์ฐพ๋Š” ํ•จ์ˆ˜.
    hash_find๋ฅผ ์‚ฌ์šฉํ–ˆ๊ณ , ํ‚ค๋กœ ํ™œ์šฉํ•  ์ž„์‹œ ํŽ˜์ด์ง€๋ฅผ ์„ ์–ธํ•˜์˜€๋‹ค.
    ์ด๋•Œ pg_round_down๋ฅผ ์‚ฌ์šฉํ•ด์„œ ์ •๋ ฌํ•ด์„œ ์ฝ์–ด์ฃผ๋Š” ๊ฒƒ์ด ์ค‘์š”ํ•˜๋‹ค.

  • spt_insert_page
    ์ฒ˜์Œ์— ๋”ฐ๋กœ ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ๊ฐ€ ํ•„์š”ํ•œ์ค„ ์•Œ๊ณ  spt_find_page๋ฅผ ํ–ˆ๋Š”๋ฐ, hash_insert์— ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ ๋กœ์ง์ด ์žˆ์–ด ๊ทธ๋Œ€๋กœ ๊ฐ€์ ธ๋‹ค ์ผ๋‹ค.

/* Find VA from spt and return page. On error, return NULL. */
/*
	์ฃผ์–ด์ง„ spt์—์„œ ์ฃผ์–ด์ง„ va์— ํ•ด๋‹นํ•˜๋Š” struct page ์ •๋ณด๋ฅผ ํƒ์ƒ‰
*/
struct page *
spt_find_page(struct supplemental_page_table *spt UNUSED, void *va UNUSED)
{
	// struct page *page = NULL;
	struct page p_key;
	// page = malloc(sizeof(struct page));
	struct hash_elem *e;

	// va์— ํ•ด๋‹นํ•˜๋Š” hash_elem ์ฐพ๊ธฐ
	p_key.va = pg_round_down(va);
	e = hash_find(&spt->spt_pages, &p_key.hash_elem);

	// ์žˆ์œผ๋ฉด e์— ํ•ด๋‹นํ•˜๋Š” ํŽ˜์ด์ง€ ๋ฐ˜ํ™˜
	return e != NULL ? hash_entry(e, struct page, hash_elem) : NULL;
}


/* Insert PAGE into spt with validation. */
bool spt_insert_page(struct supplemental_page_table *spt UNUSED,
										 struct page *page UNUSED)
{
	/* TODO: Fill this function. */
	return hash_insert(&spt->spt_pages, &page->hash_elem) == NULL ? true : false; // ์กด์žฌํ•˜์ง€ ์•Š์„ ๊ฒฝ์šฐ์—๋งŒ ์‚ฝ์ž…
}

Frame Management

Frame ๊ตฌ์กฐ์ฒด

/* The representation of "frame" */
struct frame {
  void *kva;
  struct page *page;
};

ํ˜„์žฌ ์ด ํ”„๋ ˆ์ž„์— ๋“ค์–ด๊ฐ€์žˆ๋Š” page์˜ ํฌ์ธํ„ฐ๊ฐ€ ์žˆ์–ด์„œ ์„œ๋กœ ์ฐธ์กฐ์™€ ์—ญ์ฐธ์กฐ๊ฐ€ ๊ฐ€๋Šฅํ•˜๋‹ค.
์—…๋กœ๋“œ์ค‘..

์ด๋Ÿฐ ์‹์œผ๋กœ ๊ตฌ์„ฑ๋˜์–ด์žˆ๋‹ค๊ณ  ๋ณด๋ฉด ๋œ๋‹ค.

Frame ๊ด€๋ จ ํ•จ์ˆ˜

  • vm_get_frame
    ์ƒˆ ๋ฌผ๋ฆฌ ํ”„๋ ˆ์ž„์˜ ๊ณต๊ฐ„์„ ํ™•๋ณดํ•˜๋Š” ๊ธฐ๋Šฅ์„ ๋‹ด๋‹นํ•˜๋Š” ํ•จ์ˆ˜.
    ์„ฑ๊ณต์ ์œผ๋กœ ํŽ˜์ด์ง€๋ฅผ ๋ฐ›์œผ๋ฉด ๊ตฌ์กฐ์ฒด๋ฅผ ์œ„ํ•œ ๋ฉ”๋ชจ๋ฆฌ๋ฅผ ํ• ๋‹นํ•ด์ค€๋‹ค.
    ์ด๋•Œ calloc์œผ๋กœ 1๋กœ ์ดˆ๊ธฐํ™”ํ•˜๋ฉด ๋ฌธ์ œ๊ฐ€ ์—†์ง€๋งŒ,

๋งŒ์•ฝ malloc์œผ๋กœ ์ดˆ๊ธฐํ™”ํ–ˆ๋‹ค๋ฉด ์“ฐ๋ ˆ๊ธฐ๊ฐ’์ด ๋“ค์–ด๊ฐˆ ์ˆ˜ ์žˆ๋‹ค.
frame->page = NULL ์„ ๊ผญ ๋„ฃ์–ด์ฃผ์ž.

/* palloc() and get frame. If there is no available page, evict the page
 * and return it. This always return valid address. That is, if the user pool
 * memory is full, this function evicts the frame to get the available memory
 * space.*/
/* ์‚ฌ์šฉ์ž ํ’€์—์„œ ์ƒˆ๋กœ์šด ๋ฌผ๋ฆฌ ํ”„๋ ˆ์ž„ ๊ฐ€์ ธ์˜ค๊ธฐ.
 * ํŽ˜์ด์ง€ ๊ต์ฒด ๋กœ์ง ์ถ”๊ฐ€ ์ „๊นŒ์ง€๋Š” ์‹คํŒจ ์‹œ PANIC. */
static struct frame *
vm_get_frame(void)
{
	struct frame *frame = NULL;
	/* TODO: Fill this function. */
	void *kva = palloc_get_page(PAL_USER); // user pool์—์„œ ์ƒˆ๋กœ์šด physical page๋ฅผ ๊ฐ€์ ธ์˜จ๋‹ค.

	if (kva == NULL) // page ํ• ๋‹น ์‹คํŒจ -> ๋‚˜์ค‘์— swap_out ์ฒ˜๋ฆฌ
		PANIC("todo"); // OS๋ฅผ ์ค‘์ง€์‹œํ‚ค๊ณ , ์†Œ์Šค ํŒŒ์ผ๋ช…, ๋ผ์ธ ๋ฒˆํ˜ธ, ํ•จ์ˆ˜๋ช… ๋“ฑ์˜ ์ •๋ณด์™€ ํ•จ๊ป˜ ์‚ฌ์šฉ์ž ์ง€์ • ๋ฉ”์‹œ์ง€๋ฅผ ์ถœ๋ ฅ

	frame = calloc(1, sizeof(struct frame));
	if (frame == NULL)
	{												 // malloc ์‹คํŒจ ์ฒ˜๋ฆฌ
		palloc_free_page(kva); // ์ด๋ฏธ ํ• ๋‹น๋ฐ›์€ kva๋Š” ๋ฐ˜ํ™˜
		PANIC("vm_get_frame: Malloc ํ• ๋‹น ์‹คํŒจ");
	}
	frame->kva = kva; // ํ”„๋ ˆ์ž„ ๋ฉค๋ฒ„ ์ดˆ๊ธฐํ™”
	// frame->page = NULL; // ๋ช…์‹œ์  ์ดˆ๊ธฐํ™” ์ถ”๊ฐ€

	ASSERT(frame != NULL);
	ASSERT(frame->page == NULL);
	return frame;
}
  • vm_do_claim_page
    ์ฃผ์–ด์ง„ page์— ๋Œ€ํ•ด ๋ฌผ๋ฆฌ ํ”„๋ ˆ์ž„์„ ํ• ๋‹น(claim) ํ•˜๊ณ  ์ด ๋‘˜์„ ์—ฐ๊ฒฐํ•˜๋Š” ๊ธฐ๋Šฅ์„ ๋‹ด๋‹น
    ์•ž์„œ ๊ตฌํ˜„ํ•œ vm_get_frame()์„ ํ˜ธ์ถœํ•ด์„œ ์‚ฌ์šฉ๊ฐ€๋Šฅํ•œ ํ”„๋ ˆ์ž„์„ ์–ป๊ณ ,
    MMU์— ์ด๋ฅผ ๋งคํ•‘์‹œํ‚จ๋‹ค.

์ฆ‰ pml4์— ๊ฐ€์ƒ ํŽ˜์ด์ง€์™€ ๋ฌผ๋ฆฌ ํ”„๋ ˆ์ž„์„ ๋งคํ•‘์‹œํ‚ค๋Š” ์ž‘์—…๊นŒ์ง€ ์ง„ํ–‰ํ•œ๋‹ค.

  • vm_claim_page
    SPT์—์„œ va์— ํ•ด๋‹นํ•˜๋Š” ํŽ˜์ด์ง€๋ฅผ ์ฐพ๊ฑฐ๋‚˜ ์—†๋‹ค๋ฉด ์ƒ์„ฑํ•˜๊ณ 
    ์ฐพ๊ฑฐ๋‚˜ ์ƒ์„ฑํ•œ page๋ฅผ ์ธ์ž๋กœ ํ•˜์—ฌ vm_do_claim_page๋ฅผ ํ˜ธ์ถœํ•œ๋‹ค.
/* Claim the page that allocate on VA. */
// va๋กœ page๋ฅผ ์ฐพ์•„์„œ vm_do_claim_page๋ฅผ ํ˜ธ์ถœํ•˜๋Š” ํ•จ์ˆ˜
static bool
vm_do_claim_page(struct page *page)
{
	struct frame *frame = vm_get_frame();

	/* Set links */
	frame->page = page;
	page->frame = frame;

	/* TODO: Insert page table entry to map page's VA to frame's PA. */
	// ๊ฐ€์ƒ ์ฃผ์†Œ์™€ ๋ฌผ๋ฆฌ ์ฃผ์†Œ๋ฅผ ๋งคํ•‘
	struct thread *current = thread_current();
	pml4_set_page(current->pml4, page->va, frame->kva, page->writable);

	return swap_in(page, frame->kva); // uninit_initialize
}
/* Claim the PAGE and set up the mmu. */
bool vm_claim_page(void *va UNUSED)
{
	struct page *page = NULL;
	/* TODO: Fill this function */
	// spt์—์„œ va์— ํ•ด๋‹นํ•˜๋Š” page ์ฐพ๊ธฐ
	page = spt_find_page(&thread_current()->spt, va);
	if (page == NULL)
		return false;
	return vm_do_claim_page(page);
}

๋‹ค์Œ์—” Anonymous Page ๊ตฌํ˜„์„ ์ •๋ฆฌํ•ด๋ณด๊ฒ ๋‹ค...

profile
์ œ Velog์— ์˜ค์‹  ๋ชจ๋“  ๋ถ„๋“ค์ด ์ž‘๋”๋ผ๋„ ์ธ์‚ฌ์ดํŠธ๋ฅผ ์–ป์–ด๊ฐ€์…จ์œผ๋ฉด ์ข‹๊ฒ ์Šต๋‹ˆ๋‹ค :)

0๊ฐœ์˜ ๋Œ“๊ธ€