Project 3 : VIRTUAL MEMORY - Anonymous Page(기입 완료- 수정중)

Anonymous Page

이 프로젝트의 이 부분에서는 익명 페이지라는 디스크 기반이 아닌 이미지를 구현합니다.

익명 매핑에는 백업 파일이나 장치가 없습니다. 익명 페이지는 파일 기반 페이지와 달리 이름이 지정된 파일 소스가 없기 때문에 익명입니다. 익명 페이지는 스택 및 힙과 같은 실행 파일에 사용됩니다.

익명 페이지를 설명하는 구조체는 include/vm/anon.h에 anon_page가 있습니다. 현재는 비어 있지만 구현할 때 필요한 정보나 익명 페이지의 상태를 저장하기 위해 멤버를 추가할 수 있습니다. 또한 페이지의 일반 정보를 포함하는 include/vm/page.h의 구조체 페이지를 참조하세요. 익명 페이지의 경우 페이지 구조체에 구조체 anon_page anon이 포함되어 있습니다.

 

Page Initialization with Lazy Loading

지연 로딩은 메모리가 필요한 시점까지 메모리 로딩을 지연시키는 설계입니다. 페이지가 할당되어 해당 페이지에 해당하는 페이지 구조가 있지만 전용 물리적 프레임이 없고 페이지의 실제 콘텐츠가 아직 로드되지 않은 상태입니다. 콘텐츠는 실제로 필요한 시점에만 로드되며, 이는 페이지 오류로 표시됩니다.

 

페이지 유형이 세 가지이므로 초기화 루틴은 각 페이지마다 다릅니다. 아래 섹션에서 다시 설명하겠지만 여기서는 페이지 초기화 흐름에 대한 개략적인 보기를 제공합니다. 먼저, 커널이 새 페이지 요청을 수신하면 vm_alloc_page_with_initializer가 호출됩니다. 이 이니셜라이저는 페이지 구조를 할당하고 페이지 유형에 따라 적절한 이니셜라이저를 설정하여 새 페이지를 초기화한 후 사용자 프로그램에 제어권을 다시 반환합니다. 사용자 프로그램이 실행되는 동안 프로그램이 소유하고 있다고 생각하지만 아직 페이지에 콘텐츠가 없는 페이지에 액세스하려고 시도하기 때문에 페이지 오류가 발생합니다. 오류 처리 절차 중에 uninit_initialize가 호출되어 앞서 설정한 이니셜라이저를 호출합니다. 이 이니셜라이저는 익명 페이지의 경우 anon_initializer, 파일 지원 페이지의 경우 file_backed_initializer가 됩니다.

 

페이지는 초기화->(page_fault->lazy-load->swap-in>swap-out->...)->파괴의 라이프사이클을 가질 수 있습니다. 라이프사이클의 각 전환마다 필요한 절차는 페이지 타입(또는 VM_TYPE)에 따라 다르며, 이전 단락에서는 초기화에 대한 예제였습니다. 이 프로젝트에서는 각 페이지 유형별로 이러한 전환 프로세스를 구현합니다.

 

Lazy Loading for Executable

지연 로딩에서는 프로세스가 실행을 시작할 때 즉시 필요한 메모리 부분만 주 메모리에 로드합니다. 이렇게 하면 모든 바이너리 이미지를 한 번에 메모리에 로드하는 eager loading에 비해 오버헤드를 줄일 수 있습니다.

 

지연 로딩을 지원하기 위해 include/vm/vm.h에 VM_UNINIT이라는 페이지 유형을 도입했습니다. 모든 페이지는 처음에 VM_UNINIT 페이지로 생성됩니다. 또한 초기화되지 않은 페이지에 대한 페이지 구조체(include/vm/uninit.h의 구조체 uninit_page)를 제공합니다. 초기화되지 않은 페이지를 생성, 초기화 및 소멸하는 함수는 include/vm/uninit.c에서 찾을 수 있으며, 이 함수는 나중에 완성해야 합니다.

 

페이지 오류 발생 시 페이지 오류 처리기(userprog/exception.c의 page_fault)는 제어권을 vm/vm.c의 vm_try_handle_fault로 전송하고, vm_try_handle_fault는 먼저 유효한(valid) 페이지 오류인지 확인합니다. By valid, we mean the fault that accesses invalid. 가짜 오류인 경우 페이지에 일부 콘텐츠를 로드하고 사용자 프로그램에 제어권을 반환합니다.

 

가짜(bogus) 페이지 오류에는 지연 로드된 페이지, 스왑아웃된 페이지, 쓰기 보호된 페이지(write-protected page)의 세 가지 경우가 있습니다(See Copy-on-Write (Extra)). 지금은 첫 번째 경우인 지연 로드된 페이지에 대해서만 살펴보겠습니다. 지연 로딩에 대한 페이지 오류인 경우 커널은 이전에 vm_alloc_page_with_initializer에서 설정한 초기화기 중 하나를 호출하여 세그먼트를 지연 로드합니다. userprog/process.c에서 lazy_load_segment를 구현해야 합니다.

 

vm_alloc_page_with_initializer()를 구현합니다. 전달된 vm_type에 따라 적절한 이니셜라이저를 가져와서(fetch) 이 이니셜라이저로 uninit_new를 호출해야 합니다.

 

bool vm_alloc_page_with_initializer (enum vm_type type, void *va,
        bool writable, vm_initializer *init, void *aux);

-주어진 타입의 초기화되지 않은 페이지를 생성합니다. uninit 페이지의 swap_in 핸들러는 유형에 따라 페이지를 자동으로 초기화하고, 주어진 AUX로 INIT를 호출합니다. 페이지 구조가 완성되면 프로세스의 supplementary page table에 페이지를 삽입합니다. vm.h에 정의된 VM_TYPE 매크로를 사용하면 편리합니다.

 

The page fault handlerswap_in을 호출할 때 호출 체인을 따라가다가 최종적으로  uninit_intialize에 도달합니다. 우리는 이에 대한 완전한 구현을 제공합니다. 하지만 당신의 설계에 따라 uninit_initialize를 수정해야 할 수도 있습니다.

static bool uninit_initialize (struct page *page, void *kva);

-첫 번째 오류 발생 시 페이지를 초기화합니다. 템플릿 코드는 먼저 vm_initializer와 aux를 가져와 함수 포인터를 통해 해당 page_initializer를 호출합니다. your design에 따라 함수를 수정해야 할 수도 있습니다.

 

필요에 따라 vm/anon.c에서 vm_anon_init 및 anon_initializer를 수정할 수 있습니다.

 

void vm_anon_init (void);

-Initialize for anonymous page subsystem. 이 함수에서는 anonymous page와 관련된 모든 것을 설정할 수 있습니다.

 

bool anon_initializer (struct page *page,enum vm_type type, void *kva);

-이 함수는 먼저 anonymous page에 대한 핸들러를 설정합니다 in page->operations. 현재 빈 구조체인 anon_page의 일부 정보를 업데이트해야 할 수도 있습니다. 이 함수는 anonymous pages(예: VM_ANON)의 이니셜라이저로 사용됩니다.

 

userprog/process.c에서 load_segment 및 lazy_load_segment를 구현합니다. 세그먼트 로딩을 구현합니다 from executables. 이러한 모든 페이지는 커널이 페이지 오류를 인터셉트할 때만 loaded lazily 되어야 합니다.

 

You'll need to modify the core of the program loader, which is the loop in load_segment of userprog/process.c. 이 루프는 매번 vm_alloc_page_with_initializer를 호출하여 보류 중인 page object를 생성합니다. 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에 제공할 auxiliary valuesaux로 설정해야 합니다. 바이너리 로딩에 필요한 정보를 포함하는 구조체를 생성할 수 있습니다.

 

static bool lazy_load_segment (struct page *page, void *aux);

-load_segment에서 vm_alloc_page_with_initializer의 네 번째 인수로 lazy_load_segment가 제공되는 것을 보셨을 것입니다. 이 함수는 executable's pages에 대한 이니셜라이저이며 page faults가 발생할 때 호출됩니다. 이 함수는 페이지 구조체와 aux를 인자로 받습니다. aux는 load_segment에서 당신이 설정한 정보입니다. 이 정보를 사용하여 세그먼트를 읽을 파일을 찾아서 결국 세그먼트를 메모리로 읽어야 합니다.

 

스택 할당을 새 메모리 관리 시스템에 맞도록 userprog/process.c의 setup_stack을 조정해야 합니다. 첫 번째 스택 페이지는 느리게( lazily ) 할당할 필요가 없습니다. 로드 시점에 command line arguments를 사용하여 할당하고 초기화할 수 있으므로 it to be faulted in 될 때까지 기다릴 필요가 없습니다. 스택을 식별하는 방법을 당신이 제공해야 할 수도 있습니다. vm/vm.h의 vm_type에 있는 보조 마커(예: VM_MARKER_0)를 사용하여 페이지를 표시할 수 있습니다.

 

마지막으로, 오류 주소에 해당하는 페이지 구조가 spt_find_page를 통해 보충 페이지 테이블을 참조하여 해결되도록 vm_try_handle_fault 함수를 수정합니다.

모든 요구 사항을 구현한 후 포크를 제외한 프로젝트 2의 모든 테스트를 통과해야 합니다.

 

Supplemental Page Table - Revisit

이제 복사 및 정리 작업을 지원하기 위해 supplemental page table 인터페이스를 다시 살펴봅니다. 이러한 작업은 프로세스를 생성(보다 구체적으로 하위 프로세스 생성)하거나 삭제할 때 필요합니다. 자세한 내용은 아래에 자세히 설명되어 있습니다. 이 시점에서 보충 페이지 테이블을 다시 살펴보는 이유는 위에서 당신이 구현한 초기화 함수 중 일부를 사용하고 싶을 수 있기 때문입니다.

 

supplemental_page_table_copysupplemental_page_table_kill을 vm/vm.c에서 구현합니다.

 

bool supplemental_page_table_copy (struct supplemental_page_table *dst,
    struct supplemental_page_table *src);

-supplemental page table을 src에서 dst로 복사합니다. 자식이 부모의 실행 컨텍스트(execution context)를 상속해야 할 때 사용됩니다(예: fork() ). src의 supplemental page table에 있는 각 페이지별로 반복하고 dst의 supplemental page table에 있는 entryexact copy을 만듭니다. 당신은 uninit page를 할당하고 즉시 클레임해야 할 것 입니다.

 

void supplemental_page_table_kill (struct supplemental_page_table *spt);

-supplemental page table이 보유하고 있던 모든 리소스를 해제합니다. 이 함수는 프로세스가 종료될 때 호출됩니다(userprog/process.c의 process_exit()). page entries 별로 반복하고 tablepages에 대해 destroy(page)를 호출해야 합니다. 이 함수에서 실제 페이지 테이블(pml4)과 물리적 메모리(palloc-ed memory)는 걱정할 필요가 없습니다. 호출자는 supplemental page table이 정리된 후 them을 정리하기 때문입니다.

 

Page Cleanup

uninitialized page에 대한 destroy 절차를 위한 핸들러로 vm/uninit.c에 uninit_destroy를, vm/anon.c에 anon_destroy를 구현합니다. uninit page가 다른 page objects로 전환되더라도 프로세스가 종료될 때 여전히 uninit page가 남아있을 수 있습니다.

 

static void uninit_destroy (struct page *page);

-page 구조체가 보유하던 리소스를 해제합니다. 페이지의 vm type을 확인하고 그에 따라 처리하는 것이 좋습니다.

 

 

지금은 anonymous pages만 처리할 수 있습니다. 나중에 이 function을 다시 방문하여 file-backed pages(파일로 백업된 페이지) 를 정리할 수 있습니다.

static void anon_destroy (struct page *page);

-anonymous page가 보유하고 있던 리소스를 해제합니다. page struct를 명시적으로 해제할 필요는 없으며, 호출자가 해제해야 합니다.

 

이제 프로젝트 2의 모든 테스트가 통과되어야 합니다.

 

++++

1차 추가 수정( 23.6.18)

  Comments,     Trackbacks