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제외)테스트 성공시, 구현기로 찾아뵙겠습니다
'크래프톤정글 > Pintos' 카테고리의 다른 글
| PintOS Project 3 _ Virtual Memory: Stack Growth 구현 (0) | 2025.06.07 |
|---|---|
| PintOS Project 3 _ Virtual Memory: Lazy Loading + Anonymous Page + Supplemental Page Table 구현(유저프로그램 통과 ver.) (2) | 2025.06.03 |
| 누나랑 페이지폴트 흐름 따라갈래? (1) | 2025.05.30 |
| VM 시작 (0) | 2025.05.30 |
| User Program _System Call(테스트 별 정리_read-nomal, bad, syn, rox) (0) | 2025.05.25 |