가상 메모리 시스템을 지원하려면
virtual pages
와physical frames
을 효과적으로 관리한다. 즉, 사용 중인 (virtual or physical) 메모리 영역, 용도, 사용자 등을 추적해야 한다. 먼저supplemental page table
을 처리하고 그 다음 실제 프레임을 처리한다. 참고로virtual page
에는"page"
라는 용어를 사용하고 physical page에는"frame"
이라는 용어를 사용한다.
include/vm/vm.h
에 정의된 page
는 가상 메모리에 있는 페이지를 나타내는 구조이다. 그것은 우리가 페이지에 대해 알아야 할 모든 필요한 데이터를 저장한다. 현재 구조는 템플릿에서 다음과 같다.
struct page {
const struct page_operations *operations;
void *va; /* Address in terms of user space */
struct frame *frame; /* Back reference for frame */
union {
struct uninit_page uninit;
struct anon_page anon;
struct file_page file;
#ifdef EFILESYS
struct page_cache page_cache;
#endif
};
};
Union에는 여러 명의 구성원이 있지만 한 번에 한 명의 구성원만 값을 포함할 수 있다.
즉, 시스템의 페이지는 uninit_page
, anon_page
, file_page
또는 page_cache
일 수 있다.
위에서 설명하고 include/vm/vm.h
에서 정의한 대로 페이지는 VM_UNINIT
, VM_ANON
또는 VM_FILE
일 수 있습니다. 페이지에는 swapping in, swapping out, destroying 등의 여러 가지 작업이 있다. 페이지 유형마다 이러한 작업에 필요한 단계와 태스크가 다르다. 즉, VM_ANON
페이지와 VM_FILE
페이지에 대해 다른 destroy
함수를 호출해야 한다. 한 가지 방법은 각 함수에서 스위치 케이스 구문을 사용하여 각 케이스를 처리하는 것. 그것을 다루기 위해 객체 지향 프로그래밍의 "클래스 상속" 개념을 도입한다.
실제로, C 프로그래밍 언어에는 클래스나 상속이 없으며, 리눅스와 같은 실제 운영 체제 코드에서도 비슷한 방식으로 개념을 실현하기 위해 함수 포인터를 사용한다.
함수 포인터는 지금까지 배운 다른 포인터들처럼 메모리 내의 함수나 실행 가능한 코드를 가리키는 포인터이다. 함수 포인터는 검사 없이 런타임 값을 기반으로 특정 함수를 호출하여 실행할 수 있는 간단한 방법을 제공하기 때문에 유용하다. 우리의 경우 코드 레벨에서 단순히 destroy(page)
를 호출하는 것으로 충분하며 컴파일러는 올바른 함수 포인터를 호출함으로써 페이지 유형에 따라 적절한 destroy
루틴을 선택한다. (뭔말이야)
페이지 작업 구조 page_operations
의 구조는 include/vm/vm.h
에 정의되어 있다. 이 구조를 3개의 함수 포인터를 포함하는 함수 테이블로 간주한다.
struct page_operations {
bool (*swap_in) (struct page *, void *);
bool (*swap_out) (struct page *);
void (*destroy) (struct page *);
enum vm_type type;
};
이제 page_operation
구조를 어디서 찾을 수 있는지 알아보자. include/vm/vm.h
의 struct page
를 들여다보면 operations
라는 필드가 있다. 이제 vm/file.c
로 이동하면 page_operations structure file_ops
가 함수 프로토타입보다 먼저 선언된 것을 볼 수 있다. .destroy
필드는 페이지를 destroy하고 같은 파일에 정의되는 함수인 file_backed_destroy
값을 가지고 있다.
file_backed_destroy
가 함수 포인터 인터페이스로 어떻게 호출되는지 알아보자. vm_deloc_page(page)
(in vm/vm.c
)가 호출되었는데 이 페이지가 file-bakced page (VM_FILE
)라고 가정한다. 함수 내에서 destroy(page)
를 호출합니다. destroy(page)
는 다음과 같이 include/vm/vm.h
의 매크로로 정의된다.
#define destroy(page) if ((page)->operations->destroy) (page)->operations->destroy (page)
이것은 destroy
함수를 호출하는 것이 실제로 page structure에서 검색되는 destroy
함수인 (page)->operations->destroy (page)
를 호출함을 알려준다. 페이지는 VM_FILE
페이지이므로 .destroy
필드는 file_backed_destory
를 가리킨다. 따라서 파일 백업 페이지에 대한 삭제 루틴이 수행됨.
이때 Pintos는 메모리의 가상 및 물리적 매핑을 관리하기 위한 페이지 테이블(pml4
)을 갖게 된다. 하지만 이것만으로는 충분하지 않다. 이전 섹션에서 논의한 것처럼 페이지 오류 및 리소스 관리를 처리하기 위해 각 페이지에 대한 추가 정보를 저장할 수 있는 추가 페이지 표도 필요하다. 따라서 project3의 첫 번째 작업으로 supplemental page table
에 대한 몇 가지 기본 기능을 구현할 것.
void supplemental_page_table_init (struct supplemental_page_table *spt);
supplemental page table
함수는 프로세스가 시작될 때 호출된다.struct page *spt_find_page (struct supplemental_page_table *spt, void *va);
bool spt_insert_page (struct supplemental_page_table *spt, struct page *page);
supplemental_page_table
에 insert. supplemental_page_table
에 va
가 없는지 확인해야한다.이제, 모든 페이지는 단지 메타데이터가 구성되었을 때 메모리에 대한 데이터만 보유하고 있는 것이 아니다. 따라서 물리적 메모리를 관리하기 위해서는 다른 방식이 필요하다. include/vm/vm.h
에는 물리적 메모리를 나타내는 struct frame
이 존재한다. 현재 구조는 아래와 같다.
/* The representation of "frame" */
struct frame {
void *kva;
struct page *page;
};
현재는 kernel virtual address인 kva
와 page structure인 page
의 두 필드만 있지만, 프레임 관리 인터페이스를 구현할 때 추가할 수 있다.
Implement
vm_get_frame
,vm_claim_page
andvm_do_claim_page
invm/vm.c
.
static struct frame *vm_get_frame (void);
paloc_get_page
를 호출하여 user pool에서 새로운 physical page를 가져온다.vm_get_frame
을 구현한 후에는 이 함수를 통해 모든 사용자 공간 페이지(PALOC_USER)를 할당해야 한다.PANIC("todo")
으로 표시.bool vm_do_claim_page (struct page *page);
vm_get_frame
을 호출하여 프레임을 얻는다. (이미 템플릿에서 완료)bool vm_claim_page (void *va);
va
를 할당하기 위해 페이지를 요청.vm_do_claim_page
를 호출