[TIL/06.06]PintOS VM - Anonymous Page

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

KRAFTON JUNGLE 8th

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

์ฐธ๊ณ  ์ž๋ฃŒ

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

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

Anonymous Page๋ž€?

ํ•€ํ† ์Šค์•ˆ์—์„œ ๋Œ์•„๊ฐ€๋Š” ํ”„๋กœ์„ธ์Šค์˜ ์ฃผ์†Œ ๊ณต๊ฐ„์„ ๋„์‹ํ™” ํ•˜๋ฉด ์œ„์™€ ๊ฐ™๋‹ค.
๊ทธ๋ฆผ์„ ์ž์„ธํžˆ๋ณด๋ฉด Disk ์˜์—ญ๊ณผ ๋งคํ•‘๋˜๋Š” ํŒŒ๋ž‘(text), ์ดˆ๋ก์ƒ‰ ์˜์—ญ(Data)์ด ์žˆ๊ณ ,
Disk ์˜์—ญ๊ณผ ์•„๋ฌด๋Ÿฐ ๊ด€๋ จ์ด ์—†๋Š” ๋นจ๊ฐ•(Stack), ์ฃผํ™ฉ(BSS) ์˜์—ญ์ด ์žˆ๋‹ค.

๊ฒฐ๋ก ๋ถ€ํ„ฐ ๋งํ•˜์ž๋ฉด Stack, BSS ์˜์—ญ๊ณผ ๋งคํ•‘๋œ ๋นจ๊ฐ•, ์ฃผํ™ฉ์ƒ‰ ํŽ˜์ด์ง€๊ฐ€ ๋ฐ”๋กœ Anonymous Page.
์ฆ‰ ์ง€์ •๋œ ํŒŒ์ผ ์†Œ์Šค๊ฐ€ ์—†๊ธฐ ๋•Œ๋ฌธ์— ์ต๋ช…์ด๋ผ๊ณ  ๋ถ€๋ฅธ๋‹ค.

์Šคํƒ๊ณผ ํž™ ๊ณผ ๊ฐ™์€ ์‹คํ–‰ํŒŒ์ผ์— ์‚ฌ์šฉ๋˜๋ฉฐ, ํ•„์š”ํ•œ ๋ฉ”๋ชจ๋ฆฌ ๊ณต๊ฐ„์„ ๋™์ ์œผ๋กœ ํ• ๋‹นํ•˜์—ฌ ํ”„๋กœ๊ทธ๋žจ์˜ ์š”๊ตฌ์‚ฌํ•ญ์— ๋งž๊ฒŒ ๋ฉ”๋ชจ๋ฆฌ๋ฅผ ํ™œ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

Lazy Loading๊ณผ ๋ฌด์Šจ ๊ด€๊ณ„?

ํ”„๋กœ์ ํŠธ 3์—์„œ ๋งˆ์ฃผํ•˜๋Š” ์ฒซ๋ฒˆ์งธ ์žฅ์• ๋ฌผ์€ Lazy Loading์ด๋‹ค.
์ด๊ฑธ ๊ตฌํ˜„ํ•˜๊ธฐ ์œ„ํ•ด ์ด์ „์— SPT์™€ Frame ๊ด€๋ฆฌ ํ•จ์ˆ˜๋ฅผ ๊ตฌํ˜„ํ–ˆ๋‹ค.

Lazy Loading์˜ ๋ชฉ์ ์€ ์‹คํ–‰ํŒŒ์ผ์„ ๋กœ๋“œํ• ๋•Œ ๋ถˆํ•„์š”ํ•œ ๋””์Šคํฌ ์ ‘๊ทผ์„ ์ค„์ด๊ธฐ ์œ„ํ•ด ๊ตฌํ˜„ํ•˜๋Š” ๊ฒƒ.
๋งŒ์•ฝ ํŒŒ์ผ ๊ธฐ๋ฐ˜ ํŽ˜์ด์ง€๋ผ๋ฉด, ๋ฌผ๋ฆฌ ํ”„๋ ˆ์ž„ ํ• ๋‹น -> ๋””์Šคํฌ์—์„œ ํŒŒ์ผ ์ฝ๊ธฐ(I/O)
์˜ ์ž‘์—… ์ˆœ์„œ๋ฅผ,

์ต๋ช…ํŽ˜์ด์ง€๋ผ๋ฉด, ๋ฌผ๋ฆฌ ํ”„๋ ˆ์ž„ ํ• ๋‹น -> ํ•ด๋‹นํ”„๋ ˆ์ž„ 0์œผ๋กœ ์ฑ„์šฐ๊ธฐ(CPU์—ฐ์‚ฐ)
๋””์Šคํฌ I/O๋„ ์—†๊ณ , ๋น„์šฉ์ด ๋งŽ์ด ๋“œ๋Š” ์ž‘์—…์„ ์‹ค์ œ ์‚ฌ์šฉ ๋˜๋Š” ์ˆœ๊ฐ„์œผ๋กœ ๋ฏธ๋ฃฌ๋‹ค.

Ex. BSS Segment

BSS segment๋Š” ์ดˆ๊ธฐํ™”๋˜์ง€ ์•Š์€(uninitialized) ์ „์—ญ/์ •์  ๋ณ€์ˆ˜๋ฅผ ์œ„ํ•œ ์˜์—ญ์ด๋‹ค.
ํ”„๋กœ๊ธ€๋žจ ์‹œ์ž‘์‹œ ๋ชจ๋“  ๋‚ด์šฉ์ด 0์ด์–ด์•ผ ํ•œ๋‹ค.

์ง€์—ฐ ๋กœ๋”ฉ์ด ์—†๋‹ค๋ฉด ํ”„๋กœ๊ทธ๋žจ ์‹œ์ž‘์‹œ ์ปค๋„, ๋ฌผ๋ฆฌ ํ”„๋ ˆ์ž„์„ ๋ชจ๋‘ ํ• ๋‹นํ•˜๊ณ  memset์„ ์ด์šฉํ•ด์„œ 0์œผ๋กœ ์ฑ„์›Œ์ฃผ์–ด์•ผ ํ•จ.

๋ฐ˜๋ฉด ์ง€์—ฐ๋กœ๋”ฉ์ด ์žˆ๋‹ค๋ฉด,
ํ”„๋กœ๊ทธ๋žจ ์‹œ์ž‘์‹œ ์ปค๋„์€ ๋ฉ”๋ชจ๋ฆฌ์— ์•„๋ฌด๋Ÿฐ ์ž‘์—…์„ ํ•˜์ง€ ์•Š๊ณ ,
SPT์— ์ด VA๋Š” ๋‚˜์ค‘์— ์ ‘๊ทผ์ด ์ผ์–ด๋‚˜๋ฉด 0์œผ๋กœ ์ฑ„์›Œ์ง„ Anonymous Page ๊ฐ€ ๋˜์–ด์•ผ ํ•œ๋‹ค๊ณ  ์•ฝ์†๋งŒ ๊ธฐ๋กํ•ด๋‘ .

Page Fault ์ฒ˜๋ฆฌ + Lazy Loading

  1. ํŽ˜์ด์ง€ ํดํŠธ ๋ฐœ์ƒ
    1. ํ”„๋กœ๊ทธ๋žจ, ์•„์ง ์‹ค์ œ ๋‚ด์šฉ์ด ๋กœ๋“œ ๋˜์ง€ ์•Š์€ VM_UNINIT ํŽ˜์ด์ง€์— ์ ‘๊ทผ โ‡’ Page Fault ๋ฐœ์ƒ
  2. ์ œ์–ด ์ „๋‹ฌ
    1. userprog/exception.c์˜ page_fault() โ‡’ vm/vm.c์˜ vm_try_handle_fault() ๋กœ ์ œ์–ด ์ด๋™
  3. Fault ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ
    1. vm_try_handle_fault() โ‡’ ์ด ํดํŠธ๊ฐ€ ์œ ํšจํ•œ ํŽ˜์ด์ง€ fault(์ง€์—ฐ๋กœ๋”ฉ, ์Šค์™‘ ์ธ)์ธ์ง€ ํ™•์ธ
  4. ๋‚ด์šฉ ๋กœ๋“œ
    1. ์œ ํšจํ•œ fault๋ผ๋ฉด ์ปค๋„, ํ•ด๋‹น ํŽ˜์ด์ง€์— ํ•„์š”ํ•œ ๋‚ด์šฉ์„ ๋กœ๋“œ, ์‚ฌ์šฉ์ž ํ”„๋กœ๊ทธ๋žจ์œผ๋กœ ์‹คํ–‰ ์ œ์–ด๋ฅผ ๋ฐ˜ํ™˜
  • ์ง€์—ฐ ๋กœ๋”ฉ ํŽ˜์ด์ง€ Fault ์˜ ์ฒ˜๋ฆฌ ๊ณผ์ •

    • Page Fault ๊ฐ€ ์ง€์—ฐ ๋กœ๋”ฉ์„ ์œ„ํ•œ ๊ฒƒ โ‡’ ์ปค๋„, vm_alloc_page_with_initializer ํ•จ์ˆ˜์—์„œ ์ด์ „์— ์„ค์ •ํ•ด๋‘” ํŠน์ • ์ดˆ๊ธฐํ™” ํ•จ์ˆ˜ ํ˜ธ์ถœ
      • Lazy Loadingํ•˜๋Š” ์ž‘์—… ์ˆ˜ํ–‰
      • lazy_load_segment ํ•จ์ˆ˜๋ฅผ ๊ตฌํ˜„ํ•ด์•ผํ•จ โ‡’ ํŠน์ • ์„ธ๊ทธ๋จผํŠธ๋ฅผ ์‹ค์ œ๋กœ ๋กœ๋”ฉํ•˜๋Š” ์—ญํ•  ๋‹ด๋‹น
  • Anonymous Page Flow

vm_alloc_page_with_initializer ๊ตฌํ˜„

  1. ํŽ˜์ด์ง€ ์ƒ์„ฑ
  2. ํƒ€์ž…์— ๋”ฐ๋ฅธ ์ดˆ๊ธฐํ™” ํ•จ์ˆ˜ ๊ฐ€์ ธ์˜ค๊ธฐ
  3. UNINIT์œผ๋กœ ์ดˆ๊ธฐํ™”
  4. ํ•„๋“œ ์ˆ˜์ •
  5. SPT์— ํŽ˜์ด์ง€๋ฅผ ์ถ”๊ฐ€
/* ํŽ˜์ด์ง€ ๊ตฌ์กฐ์ฒด๋ฅผ ์ƒ์„ฑํ•˜๊ณ  ์ ์ ˆํ•œ ์ดˆ๊ธฐํ™” ํ•จ์ˆ˜๋ฅผ ์„ค์ •*/
bool vm_alloc_page_with_initializer(enum vm_type type, void *upage, bool writable,
																		vm_initializer *init, void *aux)
{

	ASSERT(VM_TYPE(type) != VM_UNINIT)

	struct supplemental_page_table *spt = &thread_current()->spt;

	/* Check wheter the upage is already occupied or not. */
	if (spt_find_page(spt, upage) == NULL)
	{
		/* TODO: Create the page, fetch the initialier according to the VM type,
		 * TODO: and then create "uninit" page struct by calling uninit_new. You
		 * TODO: should modify the field after calling the uninit_new. */

		/* TODO: Insert the page into the spt. */
		// 1. ํŽ˜์ด์ง€ ์ƒ์„ฑ
		struct page *p = (struct page *)malloc(sizeof(struct page));
		// 2. ํƒ€์ž…์— ๋”ฐ๋ฅธ ์ดˆ๊ธฐํ™” ํ•จ์ˆ˜ ๊ฐ€์ ธ์˜ค๊ธฐ
		bool (*page_initializer)(struct page *, enum vm_type, void *);
		switch (VM_TYPE(type))
		{
		case VM_ANON:
			page_initializer = anon_initializer;
			break;
		case VM_FILE:
			page_initializer = file_backed_initializer;
			break;
		}
		// 3. uninit ํƒ€์ž…์˜ ํŽ˜์ด์ง€๋กœ ์ดˆ๊ธฐํ™”
		// uninit_new -> uninit ํƒ€์ž…์œผ๋กœ ์ดˆ๊ธฐํ™”ํ•ด์ฃผ๋Š” ํ•จ์ˆ˜
		uninit_new(p, upage, init, type, aux, page_initializer);

		// 4. ํ•„๋“œ ์ˆ˜์ •
		p->writable = writable;

		// 5. ํŽ˜์ด์ง€ spt ์ถ”๊ฐ€
		return spt_insert_page(spt, p);
	}
err:
	return false;
}

uninit_initialize ๊ตฌํ˜„

VM_UNINIT ํŽ˜์ด์ง€์— ์ฒซ๋ฒˆ์งธ ํŽ˜์ด์ง€ ํดํŠธ ๋ฐœ์ƒ์‹œ ์ด ํ•จ์ˆ˜๊ฐ€ ํ˜ธ์ถœ.
ํŽ˜์ด์ง€๋ฅผ ์‹ค์ œ ์‚ฌ์šฉ ๊ฐ€๋Šฅ ์ƒํƒœ๋กœ ๋ฐ”๊พธ๋Š” ์—ญํ• ์„ ์ˆ˜ํ–‰ํ•œ๋‹ค.

/* Initalize the page on first fault */
static bool
uninit_initialize(struct page *page, void *kva)
{
	struct uninit_page *uninit = &page->uninit;

	/* Fetch first, page_initialize may overwrite the values */
	vm_initializer *init = uninit->init; // lazy_load_segment
	void *aux = uninit->aux;						 // lazy_load_arg

	/* TODO: You may need to fix this function. */
	return uninit->page_initializer(page, uninit->type, kva) &&
				 (init ? init(page, aux) : true);
}

load_segment ํ•จ์ˆ˜ ๊ตฌํ˜„

  • ํ”„๋กœ์„ธ์Šค๊ฐ€ ์‹คํ–‰๋ ๋•Œ ์‹คํ–‰ํŒŒ์ผ์„ ํ˜„์žฌ ์Šค๋ ˆ๋“œ๋กœ ๋กœ๋“œํ•˜๋Š” ํ•จ์ˆ˜์ธ Load ํ•จ์ˆ˜์—์„œ ํ˜ธ์ถœ
  • ํŒŒ์ผ์˜ ๋‚ด์šฉ upage ์— ๋กœ๋“œํ•˜๋Š” ํ•จ์ˆ˜
  • ๋‚ด์šฉ์„ ๋กœ๋“œ์‹œ lazy_load_segment๋ฅผ ๋„˜๊ฒจ์ฃผ์–ด์•ผ ํ•˜๋ฉฐ, ๊ฐ™์ด ํ•„์š”ํ•œ ์ •๋ณด๋„ ๋„˜๊ฒจ์ฃผ์–ด์•ผ ํ•จ

์œ„ ์‚ฌ์ง„์— AUX๋กœ ๋ฌถ์–ด๋†“์€ ์ € ์ •๋ณด๋ฅผ ๋‹ค ๋„˜๊ฒจ์ฃผ๋ฉด ๋œ๋‹ค.

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); // read_bytes + zero_bytes๊ฐ€ ํŽ˜์ด์ง€ ํฌ๊ธฐ(PGSIZE)์˜ ๋ฐฐ์ˆ˜์ธ์ง€ ํ™•์ธ
	ASSERT(pg_ofs(upage) == 0);											 // upage๊ฐ€ ํŽ˜์ด์ง€ ์ •๋ ฌ๋˜์–ด ์žˆ๋Š”์ง€ ํ™•์ธ
	ASSERT(ofs % PGSIZE == 0)												 // ofs๊ฐ€ ํŽ˜์ด์ง€ ์ •๋ ฌ๋˜์–ด ์žˆ๋Š”์ง€ ํ™•์ธ;

	while (read_bytes > 0 || zero_bytes > 0) // read_bytes์™€ zero_bytes๊ฐ€ 0๋ณด๋‹ค ํฐ ๋™์•ˆ ๋ฃจํ”„๋ฅผ ์‹คํ–‰
	{
		/* Do calculate how to fill this page.
		 * We will read PAGE_READ_BYTES bytes from FILE
		 * and zero the final PAGE_ZERO_BYTES bytes. */
		size_t page_read_bytes = read_bytes < PGSIZE ? read_bytes : PGSIZE; // ์ตœ๋Œ€๋กœ ์ฝ์„ ์ˆ˜ ์žˆ๋Š” ํฌ๊ธฐ๋Š” PGSIZE
		size_t page_zero_bytes = PGSIZE - page_read_bytes;

		/* TODO: Set up aux to pass information to the lazy_load_segment. */
		// loading์„ ์œ„ํ•ด ํ•„์š”ํ•œ ์ •๋ณด๋ฅผ ํฌํ•จํ•˜๋Š” ๊ตฌ์กฐ์ฒด๋ฅผ ๋งŒ๋“ค์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
		struct lazy_load_arg *lazy_load_arg = (struct lazy_load_arg *)malloc(sizeof(struct lazy_load_arg));
		lazy_load_arg->file = file;									 // ๋‚ด์šฉ์ด ๋‹ด๊ธด ํŒŒ์ผ ๊ฐ์ฒด
		lazy_load_arg->ofs = ofs;										 // ์ด ํŽ˜์ด์ง€์—์„œ ์ฝ๊ธฐ ์‹œ์ž‘ํ•  ์œ„์น˜
		lazy_load_arg->read_bytes = page_read_bytes; // ์ด ํŽ˜์ด์ง€์—์„œ ์ฝ์–ด์•ผ ํ•˜๋Š” ๋ฐ”์ดํŠธ ์ˆ˜
		lazy_load_arg->zero_bytes = page_zero_bytes; // ์ด ํŽ˜์ด์ง€์—์„œ read_bytes๋งŒํผ ์ฝ๊ณ  ๊ณต๊ฐ„์ด ๋‚จ์•„ 0์œผ๋กœ ์ฑ„์›Œ์•ผ ํ•˜๋Š” ๋ฐ”์ดํŠธ ์ˆ˜
		// vm_alloc_page_with_initializer๋ฅผ ํ˜ธ์ถœํ•˜์—ฌ ๋Œ€๊ธฐ ์ค‘์ธ ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.
		if (!vm_alloc_page_with_initializer(VM_ANON, upage, writable, lazy_load_segment, lazy_load_arg))
			return false;

		/* Advance. */
		// ๋‹ค์Œ ๋ฐ˜๋ณต์„ ์œ„ํ•˜์—ฌ ์ฝ์–ด๋“ค์ธ ๋งŒํผ ๊ฐ’์„ ๊ฐฑ์‹ ํ•ฉ๋‹ˆ๋‹ค.
		read_bytes -= page_read_bytes;
		zero_bytes -= page_zero_bytes;
		upage += PGSIZE;
		ofs += page_read_bytes;
	}
	return true;
}

Lazy_load_segment

  • ์‹คํ–‰ ํŒŒ์ผ์˜ ๋‚ด์šฉ์„ ํŽ˜์ด์ง€๋กœ ๋กœ๋”ฉํ•˜๋Š” ํ•จ์ˆ˜, ์ฒซ๋ฒˆ์งธ ํŽ˜์ด์ง€ fault๊ฐ€ ๋ฐœ์ƒ์‹œ ํ˜ธ์ถœ
  • ๋ฌผ๋ฆฌ ํ”„๋ ˆ์ž„์— ๋‚ด์šฉ์„ ์ฑ„์šฐ๋Š” ์ž‘์—…๋งŒ ์ˆ˜ํ–‰ํ•˜๋ฉด ๋
bool lazy_load_segment(struct page *page, void *aux)
{
	/* TODO: Load the segment from the file */
	/* TODO: This called when the first page fault occurs on address VA. */
	/* TODO: VA is available when calling this function. */

	struct lazy_load_arg *lazy_load_arg = (struct lazy_load_arg *)aux;

	// 1) ํŒŒ์ผ์˜ position์„ ofs์œผ๋กœ ์ง€์ •ํ•œ๋‹ค.
	file_seek(lazy_load_arg->file, lazy_load_arg->ofs);
	// 2) ํŒŒ์ผ์„ read_bytes๋งŒํผ ๋ฌผ๋ฆฌ ํ”„๋ ˆ์ž„์— ์ฝ์–ด ๋“ค์ธ๋‹ค.
	if (file_read(lazy_load_arg->file, page->frame->kva, lazy_load_arg->read_bytes) != (int)(lazy_load_arg->read_bytes))
	{
		palloc_free_page(page->frame->kva);
		return false;
	}
	// 3) ๋‹ค ์ฝ์€ ์ง€์ ๋ถ€ํ„ฐ zero_bytes๋งŒํผ 0์œผ๋กœ ์ฑ„์šด๋‹ค.
	memset(page->frame->kva + lazy_load_arg->read_bytes, 0, lazy_load_arg->zero_bytes);

	return true;
}

vm_try_handle_fault

๋งˆ์ง€๋ง‰์œผ๋กœ ํŽ˜์ด์ง€ ํด๋“œ ๋ฐœ์ƒ์‹œ ํ•ธ๋“ค๋Ÿฌ ํ•จ์ˆ˜๋ฅผ ์ˆ˜์ •ํ•œ๋‹ค.

/* Return true on success */
bool vm_try_handle_fault(struct intr_frame *f UNUSED, void *addr UNUSED,
												 bool user UNUSED, bool write UNUSED, bool not_present UNUSED)
{
	struct supplemental_page_table *spt UNUSED = &thread_current()->spt;
	struct page *page = NULL;
	if (addr == NULL)
		return false;

	if (is_kernel_vaddr(addr))
		return false;

	if (not_present) // ์ ‘๊ทผํ•œ ๋ฉ”๋ชจ๋ฆฌ์˜ physical page๊ฐ€ ์กด์žฌํ•˜์ง€ ์•Š์€ ๊ฒฝ์šฐ
	{
		/* TODO: Validate the fault */
		// ํŽ˜์ด์ง€ ํดํŠธ๊ฐ€ ์Šคํƒ ํ™•์žฅ์— ๋Œ€ํ•œ ์œ ํšจํ•œ ๊ฒฝ์šฐ์ธ์ง€๋ฅผ ํ™•์ธํ•œ๋‹ค.
		void *rsp = f->rsp; // user access์ธ ๊ฒฝ์šฐ rsp๋Š” ์œ ์ € stack์„ ๊ฐ€๋ฆฌํ‚จ๋‹ค.
		if (!user)					// kernel access์ธ ๊ฒฝ์šฐ thread์—์„œ rsp๋ฅผ ๊ฐ€์ ธ์™€์•ผ ํ•œ๋‹ค.
			rsp = thread_current()->rsp;

		page = spt_find_page(spt, addr);
		if (page == NULL)
			return false;
		if (write == 1 && page->writable == 0) // write ๋ถˆ๊ฐ€๋Šฅํ•œ ํŽ˜์ด์ง€์— write ์š”์ฒญํ•œ ๊ฒฝ์šฐ
			return false;
		return vm_do_claim_page(page);
	}
	return fals
profile
์ œ Velog์— ์˜ค์‹  ๋ชจ๋“  ๋ถ„๋“ค์ด ์ž‘๋”๋ผ๋„ ์ธ์‚ฌ์ดํŠธ๋ฅผ ์–ป์–ด๊ฐ€์…จ์œผ๋ฉด ์ข‹๊ฒ ์Šต๋‹ˆ๋‹ค :)

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