lazy_load_segment? uninit_initialize?

spt관련 함수들을 구현 후 anon관련 함수들 등 유저프로그램 테스트 통과를 위해 남은 함수들을 보던와중.......

lazy_load_segment?......uninit_initalize?

나만 헷갈려?

그래서 써보는 흐름글


lazy_load_segment 흐름

lazy_load_segment의 호출 시점 :

페이지 폴트 발생 후, 해당 va의 페이지가 UNINIT 타입일 때 → uninit_initialize() 내부에서 호출

하는 역할:

해당 페이지를 디스크에서 읽어 메모리에 적재하는 함수

 

1. aux로 전달된 정보 기반으로

  • 어떤 파일에서 (file)
  • 어느 오프셋부터 (offset)
  • 몇 바이트를 읽고 (read_bytes)
  • 나머지를 0으로 패딩할지 (zero_bytes)

를 파악함

 

2. page->frame->kva

  • 주소에 실제 데이터를 로드
  • 디스크에서 파일의 내용을 읽고 (file_read_at)
  • 나머지는 memset(..., 0)을 통해 0으로 채움.

3. 페이지 초기화를 완료

  • 성공적으로 로드했다면 true 반환 → 이 페이지는 이제 valid하게 사용 가능해짐
[load()] -> [load_segment()] 
          -> [vm_alloc_page_with_initializer(..., lazy_load_segment, aux)]
              ↓
         [uninit_new()] - 생성한 struct page에 lazy_load_segment 예약
              ↓
[page fault 발생] 
              ↓
[uninit_initialize()] 호출됨
              ↓
[lazy_load_segment()] 수행 
              ↓
⇒ 파일 내용을 메모리로 로드 (lazy load 완성)

load함수에서 load_segment호출

/* FILE의 OFS 오프셋에서 시작하여 UPAGE 주소에 세그먼트를 로드합니다.
 * 총 READ_BYTES + ZERO_BYTES 바이트의 가상 메모리가 다음과 같이 초기화됩니다:
 *
 * - UPAGE에서 READ_BYTES 바이트는 FILE에서 OFS 오프셋부터 읽어옵니다.
 *
 * - UPAGE + READ_BYTES 위치에서 ZERO_BYTES 바이트는 0으로 초기화됩니다.
 *
 * 이 함수로 초기화된 페이지는 WRITABLE이 true이면 사용자 프로세스가 수정할 수 있으며,
 * 아니라면 읽기 전용입니다.
 *
 * 메모리 할당 오류나 디스크 읽기 오류가 발생하면 false를 반환하고,
 * 성공하면 true를 반환합니다.
 */
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);
	ASSERT(pg_ofs(upage) == 0);
	ASSERT(ofs % PGSIZE == 0);

	while (read_bytes > 0 || zero_bytes > 0)
	{
		/* 이 페이지를 어떻게 채울지 계산합니다.
		 * FILE에서 PAGE_READ_BYTES 바이트를 읽고
		 * 남은 PAGE_ZERO_BYTES 바이트는 0으로 초기화합니다. */
		size_t page_read_bytes = read_bytes < PGSIZE ? read_bytes : PGSIZE; // 4KB까지만 읽어라
		size_t page_zero_bytes = PGSIZE - page_read_bytes;					// 0 패딩 사이즈는 4KB - read_byte

		/* TODO: Set up aux to pass information to the lazy_load_segment. */
		void *aux = NULL; // 전달해야할 인자
		if (!vm_alloc_page_with_initializer(VM_ANON, upage,
											writable, lazy_load_segment, aux))
			return false;

		/* Advance. */
		read_bytes -= page_read_bytes;
		zero_bytes -= page_zero_bytes;
		upage += PGSIZE;
	}
	return true;
}

여기서 lazy_load_segment는 예약 느낌

vm_alloc_page_with_initializer로 넘어감

/* 초기화 함수와 함께 대기 중인 페이지 객체를 생성합니다. 페이지를 직접 생성하지 말고,
 * 반드시 이 함수나 `vm_alloc_page`를 통해 생성하세요. */
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;

	/* 이미 해당 page가 SPT에 존재하는지 확인합니다 */
	if (spt_find_page(spt, upage) == NULL)
	{
		/* TODO: VM 타입에 따라 페이지를 생성하고, 초기화 함수를 가져온 뒤,
		 * TODO: uninit_new를 호출하여 "uninit" 페이지 구조체를 생성하세요.
		 * TODO: uninit_new 호출 후에는 필요한 필드를 수정해야 합니다. */
		bool (*page_initializer)(struct page *, enum vm_type, void *kva);
		struct page *page = malloc(sizeof(struct page));
		page->writable = writable;

		switch (VM_TYPE(type))
		{
		case VM_ANON:
			page_initializer = anon_initializer;
			break;
		case VM_MMAP:
			/* 매핑 카운트를 추가해두자
			   mmap_list로 mmap 페이지를 관리할거면 필요 x */
		case VM_FILE:
			page_initializer = file_backed_initializer;
			break;
		default:
			free(page);
			goto err;
			break;
		}

		uninit_new(page, upage, init, type, aux, page_initializer);

		/* TODO: 생성한 페이지를 spt에 삽입하세요. */
		hash_insert(&spt->spt_hash, &page->hash_elem);
	}
err:
	return false;
}

여기서 init == lazy_load_segment / page_initializer는 페이지타입 별 함수로

그 정보 가지고 uninit_new로 감

/* DO NOT MODIFY this function */
void
uninit_new (struct page *page, void *va, vm_initializer *init,
		enum vm_type type, void *aux,
		bool (*initializer)(struct page *, enum vm_type, void *)) {
	ASSERT (page != NULL);

	*page = (struct page) {
		.operations = &uninit_ops,
		.va = va,
		.frame = NULL, /* no frame for now */
		.uninit = (struct uninit_page) {
			.init = init,
			.type = type,
			.aux = aux,
			.page_initializer = initializer,
		}
	};
}

여기서 한 페이지에 대한 필드가 완성됨

그리고 페이지 폴트가 나서 해당 va의 페이지가 uninit타입이라면

uninit_initialize로 감

/* 첫 번째 폴트 시 페이지를 초기화합니다 */
static bool
uninit_initialize (struct page *page, void *kva) {
	struct uninit_page *uninit = &page->uninit;

	/* 먼저 가져옵니다. page_initialize가 값을 덮어쓸 수 있습니다. */
	vm_initializer *init = uninit->init;
	void *aux = uninit->aux;

	/* TODO: 이 함수를 수정해야 할 수도 있습니다. */
	return uninit->page_initializer (page, uninit->type, kva) &&
		(init ? init (page, aux) : true);
}

여기서

	vm_initializer *init = uninit->init;

이 부분이 lazy_load_segment가 됨

	/* TODO: 이 함수를 수정해야 할 수도 있습니다. */
	return uninit->page_initializer (page, uninit->type, kva) &&
		(init ? init (page, aux) : true);

이 부분에서

uninit->page_initializer (page, uninit->type, kva)

이건 공통 초기화 함수임

즉, anon_initialize(), file_backed_initialize() 같은 타입별 페이지 초기화자 함수라는 거임

struct page 안에 anon, file 중 어떤 타입인지 설정하고,

page->operations도 연결하는 역할

페이지 타입과 kva가 설정되면서, 그에 맞는 동작 함수 포인터들도 초기화됨

결과적으로, 이 페이지가 anon인지 file인지 정식 타입으로 초기화가 됨

그럼 이제 얘는 뭐냐

(init ? init (page, aux) : true)

아까 init = lazy_load_segment였음

그럼 lazy_load_segment겠지?

 

init이 존재한다면

→ init(page, aux)를 호출한 결과값 반환

아니라면 (init이 NULL이라면)

→ 그냥 true 반환

 

  • 추가적인 초기화 작업이 필요할 경우에만 수행
  • load_segment()에서 넘긴 aux 정보를 이용해 디스크로부터 데이터를 읽고, kva에 복사
  • 타입 설정 이후, 실제 데이터를 로딩함

이 두 수행이 다 성공해야 true를 반환함

why?

  • lazy_load_segment()가 false를 반환하면, 이 전체 조건식도 false가 됨
  • 그럼 결국 uninit_initialize()도 false를 반환함
  • 페이지 초기화 실패 처리

이제 lazy_load_segment를 구현하면 됨

 

유저프로그램(fork제외)테스트 성공시, 구현기로 찾아뵙겠습니다