가상 메모리 시스템을 위해 가상 페이지와 물리 프레임을 효과적으로 관리해야한다
어떤 메모리 영역이 사용되고, 왜 사용되며, 누가 사용하는지를 추적해야 한다는 뜻이다
보조 페이지 테이블 먼저 다루고 물리 프레임 다룬다고 한다
헷갈리니 단어 의미 통일 한다고 한다
페이지 : 가상 페이지
프레임 : 물리 페이지
include/vm/vm.h
에 정의된 struct page
는 가상 메모리에서 하나의 페이지를 나타내는 구조체
여기에 페이지에 대해 우리가 알아야 할 모든 필요 데이터 저장한다
템플릿의 현재 구조체란다
struct page {
const struct page_operations *operations;
void *va; /* 사용자 공간 관점의 주소 */
struct frame *frame; /* 프레임에 대한 역참조 */
union {
struct uninit_page uninit;
struct anon_page anon;
struct file_page file;
#ifdef EFILESYS
struct page_cache page_cache;
#endif
};
};
이는 페이지 연산, 가상 주소, 물리 프레임을 가진다
추가적으로 union 필드가 존재하는데 union은 특별한 데이터 타입으로, 하나의 메모리 영역에 서로 다른 타입의 데이터를 저장할 수 있게 한다
union에는 여러 멤버가 존재하지만 동시에 하나의 멤버만 값을 가질 수 있다
코드 보면 나오듯 시스템에서 하나의 페이지가 uninit_page
, anon_page
, file_page
, 또는 page_cache
중 하나일 수 있음을 의미한다
예시로, 만약 페이지가 익명 페이지라면, struct page
는 멤버 중 하나로 struct anon_page anon
을 가진다
anon_page
는 익명 페이지에 대해 우리가 유지해야 하는 모든 필요한 정보를 포함한다
익명 페이지에 대해서는 여기서 참고
페이지는 VM_UNINIT
, VM_ANON
, VM_FILE
로 정의되고
각 페이지에 대해서는 swap-in, swap-out, destroy 같은 다양한 동작이 수행된다
그치만 페이지의 종류에 따라 동작 절차와 작업이 다르다
페이지 종류에 따라 서로 다른 destroy 함수가 호출되거나 해야 한다
한 가지 방법은 switch-class문을 활용해 각 경우를 처리하는 경우
이는 객체지향 프로그래밍(OOP)의 “클래스 상속(class inheritance)” 개념을 차용한 것이고 C언어에는 클래스도 상속도 없다
그치만 C에는 포인터가 있어 함수 포인터를 사용해 이 개념 구현한다
함수 포인터는 특정 메모리 내의 함수를 가리키는 포인터다
런타임값에 따라 특정 함수 간단히 호출할 수 있도록 해줘 유용하다
struct page_operations
페이지 연산을 위한 구조체는 include/vm/vm.h
에 정의되어 있다
이 구조체는 세 개의 함수 포인터를 포함하는 함수 테이블이라 보면 된다
struct page_operations {
bool (*swap_in) (struct page *, void *);
bool (*swap_out) (struct page *);
void (*destroy) (struct page *);
enum vm_type type;
};
이 구조체는 include/vm/vm.h
에 있다
vm/file.c
보면 page_operations
구조체인 file_ops
가 선언되어 있다
이것이 파일 기반 페이지를 위한 함수 포인터 테이블이다
file_backed_destroy
를 통해 호출 과정 알아보자
만약 vm_deallock_page(page)
가 호출되었고 페이지가 파일 기반 페이지 (VM_FILE
)라고 가정하면
해당 함수 안에서는 destroy(page)
가 호출된다
destoy(page)
는 include/vm/vm.h
에 매크로로 정의되어 있다
#define destroy(page) if ((page)->operations->destroy) (page)->operations->destroy (page)
destroy(page)
호출은 사실상 (page)->operations->destroy(page)
를 의미한다는 걸 알 수 있다
즉, paage->operation->destroy
를 가져와 그 함수를 실행한다
이 페이지가 VM_FILE
이면 .destroy
필드는 file_backed_destroy
를 가리킨다
그 결과, 파일 기반 페이지에 대한 destroy 루틴이 실행된다
가상-물리 메모리 매핑을 관리하기 위한 페이지 테이블(pml4)이 있으나 이로는 부족하다
페이지 폴트, 리소스 관리를 위해서는 추가 정보가 필요하고
이를 위해 보조 페이지 테이블(supplemental page table, SPT) 을 구현해야 한다
프로젝트 3의 시작은 이걸로 여는게 좋다
vm/vm.c
에 보조 페이지 테이블 관리 함수 구현이 목표다
보조 페이지 테이블을 어떤 자료구조로 설계할지 결정하고 다음 세 가지 함수를 구현하면된다
/* 보조 페이지 테이블을 초기화 한다 initd와 __do_fork 시점에 호출된다
* 새 프로세스 시작과 fork 시점 */
void supplemental_page_table_init (struct supplemental_page_table *spt);
/* 주어진 보조 페이지 테이블에서 가상 주소 va에 대응되는 struct page를 찾는다
* 실패 시 NULL 반환 */
struct page *spt_find_page (struct supplemental_page_table *spt, void *va);
/* struct page를 주어진 보조 페이지 테이블에 삽입한다
* 해당 가상 주소 보조 페이지 테이블에 존재 여부 확인해야 한다*/
bool spt_insert_page (struct supplemental_page_table *spt, struct page *page);
딱 보니 해시테이블로 만들면 되겠다
이제 페이지에는 메타 데이터뿐 아니라 실제 메모리 포함하게 된다
물리 메모리 관리를 위해 별도의 체계가 필요하다
include/vm/vm.h
에는 물리 메모리 표현하는 struct frame
이 존재한다
/* "프레임"의 표현 */
struct frame {
void *kva;
struct page *page;
};
보다시피 필드 두 개가 끝이다
kva
: 커널 가상 주소 (kernel virtual address)
page
: 대응되는 페이지 구조체
당연하게도 필요에 따라 멤버 더 추가해야 한다
vm/vm.c
에 다음 함수들을 구현한다
static struct frame *vm_get_frame (void);
사용자 풀에서 palloc_get_page
호출해 새 물리 페이지 얻는다
성공 시 프레임 할당 및 멤버들 초기화하여 반환
이 함수 구현 이후에는 모든 사용자 공간 페이지 할당 얘가 해야 한다
페이지 할당 실패 시 지금은 PANIC("todo")
처리하면 된다
bool vm_do_claim_page (struct page *page);
페이지를 claim 한다
먼저 vm_get_frame
호출해 프레임 얻는다
이후 MMU 설정
반환값은 성공 여부 나타낸다 (bool)
bool vm_claim_page (void *va);
가상 주소 va
에 해당하는 페이지를 claim 한다
우선 spt
에서 페이지 가져오고 vm_do_claim_page
호출
GitBOOK 보니 뭐부터 해야할지 알겠다