🤔 왜 구현하는데?
프로세스의 가상 메모리 공간을 관리하기 위해 구현해야 하는 중요한 요소로 디스크에 저장된 파일과 연결되지 않은, 메모리 상에서만 존재하는 페이지이다.
구현하기 위한 절차(순서)
1. lazy_load_segment() 함수 설정 : 첫번째 페이지 폴트가 발생했을 때 실행하며 해당 세그먼트를 로드하고 초기화하는 역할을 함, lazy_load_segment()함수를 생성해서 세그먼트 lazy loading을 처리
2. vm_alloc_page_with_initializer()에서 가상 메모리를 할당 : 각 페이지는 lazy loading을 위해 uninit_new() 함수로 초기화 한후 lazy_load_segment() 함수로 설정
3. 페이지 폴트 처리 : 페이지 폴트가 발생하면 페이지 폴트 핸들러를 호출하는데, 페이지 폴트 핸들러는 해당 페이지가 로드되어 있지 않은 경우 'lazy_load_segment()' 함수를 호출하여 해당 세그먼트를 로드하고 초기화
4. 파일 읽기 : lazy_load_segment() 함수 내에서 파일 내용을 읽어와 메모리에 쓰는 작업을 수행
5. 페이지 초기화 : memset() 함수를 사용하여 초기화되지 않은 나머지 부분을 0으로 설정
6. 페이지 테이블 업데이트 : 로드한 페이지를 페이지 테이블에 매핑(spt_insert_page)
1,3,4,5 lazy_load_segment()
함수 설정
static 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으로 지정한다. 세그먼트의 시작 위치를 ofs으로 설정
file_seek(lazy_load_arg->file,lazy_load_arg->ofs);
//2) 파일을 read_bytes만큼 물리 프레임에 읽어 들인다. 이때 읽은 바이트 수가 read_bytes수와 다르면 할당된 메모리를 해제하고 false 반환
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 함수는 특정 메모리 영역을 지정된 값으로 설정하는 역할을 함
memset(page->frame->kva + lazy_load_arg->read_bytes,0,lazy_load_arg->zero_bytes);
return true;
}
2,6 ```bool vm_alloc_page_with_initializer (enum vm_type type, void va, bool writable, vm_initializer init, void aux);```
주어진 타입의 초기화되지 않은 페이지를 생성
(1) 초기화 되지 않은 페이지를 생성
(2) 생성된 페이지의 swap_in 핸들러가 호출된다. 이 핸들러는 페이지를 초기화하고 주어진 aux와 함께 init을 호출하여 페이지를 초기 상태로 설정
→ swap_in 핸들러는 페이지 폴트 핸들러의 일부로 해당 페이지를 메모리로 로드하는 역할을 한다. 이때 swap_in 함수 내에서 uninit_initialize 함수에 도달하는데 이 부분을 수정해야 할수도 있다.
(3) 페이지 구조체를 프로세스의 보조 페이지 테이블에 삽입해서 페이지의 가상주소와 물리 주소간의 매핑이 이루어짐
struct page *p = (struct page *)malloc(sizeof(struct page));
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;
}
uninit_new(p,upage,init,type,aux,page_initializer);//uninit_new 호출해서 "uninit" 페이지 구조체를 생성
p->writable = writable; //page의 상태를 수정 가능한 상태로 만듦
return spt_insert_page(spt,p); // 페이지를 만들었으므로 spt에 페이지 삽입
supplemental_page_table_copy
와supplemnetal_page_table_kill
함수를 구현해서 spt의 복사와 제거를 지원한다.
supplemental_page_table_copy
: 자식이 부모의 실행 컨텍스트를 상속해야 할때 사용됨bool
supplemental_page_table_copy (struct supplemental_page_table *dst UNUSED,
struct supplemental_page_table *src UNUSED) {
struct hash_iterator i;
hash_first(&i, &src->spt_hash);
while(hash_next(&i)){
//src_page 정보
struct page *src_page = hash_entry(hash_cur(&i), struct page, hash_elem);
enum vm_type type = src_page ->operations->type;
void *upage = src_page -> va;
bool writable = src_page->writable;
/*1) type이 uninit이면 */
if(type == VM_UNINIT){
vm_initializer *init = src_page -> uninit.init;
void *aux = src_page->uninit.aux;
vm_alloc_page_with_initializer(VM_ANON,upage,writable,init,aux);
continue;
}
/*2) type이 uninit이 아니면*/
if(!vm_alloc_page_with_initializer(type,upage,writable,NULL,NULL)) return false;
if(!vm_claim_page(upage)) return false;
struct page *dst_page = spt_find_page(dst,upage);
memcpy(dst_page->frame->kva,src_page->frame->kva,PGSIZE);
}
return true;
}
supplemental_page_table_kill
: 프로세스가 종료될 때 호출(exit())void
supplemental_page_table_kill (struct supplemental_page_table *spt UNUSED) {
/* TODO: Destroy all the supplemental_page_table hold by thread and
* TODO: writeback all the modified contents to the storage. */
/*페이지 테이블과 관련된 자원만 해제, 해시 테이블은 그대로 보존 */
hash_clear(&spt->spt_hash,hash_page_destroy); // 해시 테이블에서 모든 요소를 제거
}
(2) hash_page_destroy 함수를 추가 구현해준다.
/* 페이지 구조체에 메모리 해제 작업을 수행*/
void hash_page_destroy(struct hash_elem *e, void *aux){
struct page *page =hash_entry(e,struct page, hash_elem);
destroy(page);
free(page);
}
uninit_destroy
anon_destroy
🤔 rehash란?
해시 충돌을 해결하기 위해 해시 테이블의 크기를 확장하거나 재구성 하는 작업
1. 더 큰 크기의 새로운 해시 테이블 생성
2. 기존 해시 테이블의 모든 요소를 순회하면서 새로운 해시 테이블에 재배치
3. 재배치된 요소들을 기존 해시 테이블에서 제거하고, 메모리를 해제
4. 새로운 해시 테이블로 대체