supplementary page table(spt)
: 보조 데이터들을 담고 있는 프로세스마다 존재하는 자료구조
보조 데이터는 각각의 페이지에 대해 데이터가 존재하는 곳(frame, disk, swap), 이에 상응하는 커널 가상주소를 가리키는 포인터 정보(active, inactive) 등이 있다.
spt의 용도
1. page fault 발생 시 그곳에 어떤 데이터가 있었어야 했는지 알아내기 위해 spt에서 fault가 발생한 가상 페이지를 탐색
2. 커널이 프로세스가 종료될 때 어떤 자원을 해제(free)할 지 고르기 위해 spt를 조사
구현해야 할 것
void supplemental_page_table_init (struct supplemental_page_table *spt);
spt를 초기화하는 함수. 어떤 자료구조로 구현할 지는 선택(배열, 리스트, 비트맵, 해시)
userprog/process.c
의 initd
, __do_fork
함수에서 새로운 프로세스가 생성될 때 이 함수가 호출된다.
struct page *spt_find_page (struct supplemental_page_table *spt, void *va);
spt로부터 가상주소 va와 대응되는 페이지 구조체를 찾아서 반환, 실패 시 NULL을 반환한다.
bool spt_insert_page (struct supplemental_page_table *spt, struct page *page);
spt에 page를 삽입, spt에 가상주소가 존재하지 않는 것을 확인해야한다.
include/vm/vm.h
에 물리 메모리를 뜻하는 struct frame
이 선언되어 있다. 이 구조체는 다음과 같은 모습이다.
struct frame {
void *kva; /* 커널 가상 주소 */
struct page *page; /* 페이지 구조체를 담기 위한 멤버 */
};
frame 관리 인터페이스를 구현하는 과정에서 멤버 추가를 할 수도 있다.
static struct frame *vm_get_frame (void);
palloc_get_page()
해서 새로운 물리메모리 페이지를 가져온 후 프레임을 할당하고, 프레임 구조체의 멤버를 초기화하고 해당 프레임을 반환한다.
이 함수를 구현한 이후에는 모든 유저공간 페이지 할당 시 이 함수를 이용해야 한다.
일단은 페이지 할당 실패 시 swap out 구현하지 말고 PANIC("TODO")
로 표시만 해둔다.
bool vm_do_claim_page (struct page *page);
page에 물리 프레임을 할당해준다.
bool vm_claim_page (void *va);
va에 페이지를 할당하고, 해당 페이지에 프레임을 할당해준다.
include/vm/anon.h
에 struct anon_page
가 선언되어 있다. 비어 있으므로 구현 도중 필요한 정보나 상태 멤버를 추가.vm_alloc_page_with_initializer
호출uninit_initialize
를 호출하고 세팅된 초기화 함수를 호출한다.anon_initializer
이고, file_backed page를 위한 초기화 함수는 file_backed_initializer
이다.페이지는 initialize -> (page fault -> lazy loading -> swap in -> swap out -> ...) -> destroy의 생명 주기를 갖는다. 이 주기의 각 상태 변화마다 페이지 타입별로 다른 프로시저가 요구된다. 이번 프로젝트에서 각 페이지 타입별로 다른 상태 변화의 과정을 구현해야 한다.
: 모든 페이지들은 초기에 VM_UNINIT
타입의 페이지로 생성된다. include/vm/uninit.h
에 struct uninit_page
구조체가 선언되어 있다. uninit_page를 생성, 초기화, 제거하는 함수들은 vm/vm.c
에서 찾을 수 있다. 추후 이 함수들을 완성해야 한다.
vm_try_handle_fault
함수에게 제어권을 넘기고 이 함수는 우선 유효한 page fault인지 검사한다. lazy-loading 때문에 page fault가 일어나면 커널은 segment를 지연 로딩하기 위해 vm_alloc_page_with_initializer
함수에서 세팅해 놓은 초기화 함수를 호출한다.
userprog/process.c
에 있는 lazy_load_segment
함수를 구현해야 한다.
bool vm_alloc_page_with_initializer (enum vm_type type, void *va,
bool writable, vm_initializer *init, void *aux);
인자로 주어진 type
으로 uninitialized page를 생성한다. uninit 페이지의 swap_in 핸들러는 페이지 타입에 맞게 페이지를 초기화하고 주어진 AUX를 인자로 삼는 INIT 함수를 호출한다.
페이지 구조체를 가지게 되면 프로세스의 spt에 그 페이지를 삽입해야 한다. vm.h
에 정의되어 있는 VM_TYPE
매크로를 사용하면 편리할 것이다.
page fault 핸들러는 연쇄적으로 함수를 호출하고 swap_in 함수를 호출하면 마침내 uninit_initialize
함수에 다다르게 된다. 이 함수는 이미 작성되어 있지만 필요하면 수정해도 된다.
static bool uninit_initialize (struct page *page, void *kva);
이 함수는 처음으로 fault가 발생한 페이지를 초기화 시킨다. 먼저 uninit_page의 멤버 변수인 vm_initializer
, aux
를 가져온 후 page_initializer
를 함수 포인터로 호출한다. 이 함수를 수정해야한다.
void vm_anon_init(void);
anonymous page의 하위 시스템을 초기화한다. 필요한 것들 셋업
bool anon_initializer (struct page *page,enum vm_type type, void *kva);
처음으로 page->operation에 있는 anonymous page에 대한 핸들러를 설정하여준다. 비어있는 구조체인 anon_page의 정보들을 업데이트 할 필요가 있음.
userprog/process.c
에 있는 load_segment
와 lazy_load_segment
를 구현해야 한다. 실행파일로부터 세그먼트가 로드되는 것을 구현하고 이러한 페이지들은 lazy load가 되어야한다.
프로그램 로더의 핵심인 userprog/process.c
의 load_segment
loop 내부를 수정해야 한다. 루프를 돌 떄마다 load_segment
는 대기 중인 페이지 오브젝트를 생성하는 vm_alloc_page_with_initializer
를 호출한다. Page fault가 발생하는 순간은 세그먼트가 실제로 파일에서 로드될 때이다.
static bool load_segment (struct file *file, off_t ofs, uint8_t *upage,
uint32_t read_bytes, uint32_t zero_bytes, bool writable);
현재 코드는 메인 루프 안에서 파일로부터 읽을 바이트의 수와 0으로 채워야 할 바이트의 수를 측정한다. 그리고 그것은 대기 중인 오브젝트를 생성하는 vm_alloc_page_with_initializer
함수를 호출한다. vm_alloc_page_with_initializer
에 재공할 aux 인자로써 보조 값들을 설정할 필요가 있다. 바이너리 파일을 로드할 때 필수적인 정보를 포함하는 구조체를 생성하는 것이 좋다.