메모리 로딩을 필요한 시점까지 지연시키는 디자인
페이지가 할당되었다는 것은 대응되는 페이지 구조체는 있지만, 별도의 물리 메모리 프레임은 아직 없고 페이지의 실제 콘텐츠는 아직 로드되지 않은 상태이다. 콘텐츠는 page fault에 의해 실제로 콘텐츠가 필요하다는 signal을 받을 때 로드된다.
핀토스는 세 가지 타입의 페이지가 있다.
1. uninit_page
2. anonymous_page
3. file_backed_page
각 페이지 타입별로 초기화 루틴이 다르다.
우선 커널이 새로운 페이지를 달라는 요청을 받으면, vm_alloc_page_with_initializer
가 호출된다. 이 함수는 페이지 구조체를 할당하고, 페이지 타입에 맞는 적절한 초기화 함수를 세팅함으로써 새로운 페이지를 초기화한다. 그리고 유저 프로그램으로 제어권을 넘긴다.
유저 프로그램이 실행될 때, lazy loading으로 인해 콘텐츠가 아직 로드되지 않은 페이지에 접근하게 되면 page fault가 일어나게 된다. 이 page fault를 처리하는 과정에서 1. uninit_initialize
을 호출하고 이전에 당신이 세팅해놓은 초기화 함수를 호출한다. (첫 번째 폴트에서 페이지 초기화)
2. anon_initializer
: 익명 페이지를 위한 초기화 함수
3. file_backed_initializer
: 파일 기반 페이지를 위한 초기화 함수
페이지는 초기화 -> page_fault -> lazy_loading -> swap_in -> swap_out ... -> 파괴 라는 생명 주기를 가진다.
but 페이지 타입별로 다른 프로시저가 요구된다. 각 페이지 타입별로 다른 상태 변이의 과정들을 구현해야 한다.
지연 로딩에서는 프로세스가 실행을 시작할 때 당장 필요한 메모리 파트만 메인 메모리에 로드된다. -> 즉시 로딩보다 상대적으로 오버헤드를 줄일 수 있음
초기화 후 page fault가 일어나면, 페이지 폴트 핸들러는 vm_try_handle_fault
함수에게 제어권을 넘긴다. 이 함수는 유효한 페이지 폴트인지 먼저 체크한다.
이 페이지 폴트가 유효하지 않은 페이지에 접근한 폴트라면, 찐 페이지 폴트일 것이다.
그렇지 않고 bogus fault라면 이는 페이지에서 콘텐츠를 로드하고 유저 프로그램에게 제어권을 반환해야 한다.
(지연 로딩으로 인해 물리 메모리와 매핑이 되어 있지만 콘텐츠가 로드되어 있지 않은 경우가 있을 수 있다. 따라서 물리 메모리와 매핑은 되어 있지만 콘텐츠가 로드되어 있지 않은 경우: bogus fault
라면 콘텐츠를 로드하면 되고, 매핑되지 않은 페이지라면 그대로 유효한 page fault 라는 의미)
page_fault()
함수: 페이지 폴트가 발생했을 때 호출되며, 페이지 부재의 원인을 확인하고 처리하는 역할
필요한 페이지를 디스크에서 읽어와 물리 메모리에 로드한다. 필요한 페이지를 선택하고, 그 페이지를 디스크로 스왑아웃하고 새 페이지를 스왑인 한다. PTE를 갱신하여 페이지가 물리 메모리의 새 위치를 가리키도록 한다.
파일 시스템에서 실제 파일의 데이터를 가상 메모리로 불러와 메모리에 로드하는 과정
일반적으로 실행 파일의 코드 및 데이터 섹션을 가상 메모리로 로드하는 데 사용된다. 예를 들어 프로그램이 실행될 때, 해당 프로그램의 실행 파일에서 코드와 데이터를 읽어와 메모리에 올린다.
load_segment()
: 주어진 file로부터 ofs에서 시작하여 upage로 read_bytes+zero_bytes의 가상 메모리 공간을 초기화한다.
파일의 데이터를 실제로 메모리로 로드하지 않고, 필요한 경우에만 로드하는 방식
메모리를 불필요하게 차지하지 않고, 프로그램이 실제로 데이터에 액세스할 때만 메모리로 로드하여 효율성을 높인다.
특히 OS에서 메모리를 아껴야 하는 경우에 유용하다.
간단히 말하면 Load Segment는 파일 데이터를 처음부터 메모리에 로드하고 Lazy Load Segment는 필요한 데이터만 메모리로 로드하는 방식이다.
anonymous page는 파일에서 가져온 데이터가 아니라 특정 파일과 연결되지 않은 메모리 페이지이다. 이러한 페이지는 주로 프로세스의 heap 또는 stack과 관련이 있다.
anonymous page는 일반적으로 0 또는 기본값으로 초기화된다. (처음 생성될 때 실제 데이터가 바로 저장되지 않고, 필요한 경우에만 데이터가 채워지기 때문)
lazy_load_segment()
: 실행 가능한 파일의 페이지들을 초기화하는 함수이고, page fault가 발생할 때 호출된다. 이 함수는 page 구조체와 aux를 인자로 받는다. aux는 load_segment에서 내가 설정하는 정보다. 이 정보를 사용하여 세그먼트를 읽을 파일을 찾고 최종적으로 세그먼트를 메모리에서 읽어야 한다.
load -> spt setting
page fault -> 물리 메모리에서 가져오는건 페뽈이 한다.
VM_UNINIT
초기화되지 않은 메모리로, 프로그램 실행 도중 초기화되지 않은 데이터를 가리킬 때 사용된다. 초기화되지 않은 페이지는 필요할 때 페이지 폴트가 발생하면 물리 메모리에 할당된다.
VM_ANON
익명 메모리를 나타낸다. 익명 메모리는 파일로 백업되지 않는 메모리로, 주로 힙 메모리나 스택과 관련이 있다. VM_ANON 페이지는 물리 메모리에 할당되며 프로세스의 스택 또는 힙을 나타내기 위해 사용된다.
VM_FILE
파일로 지원되는 가상 메모리를 나타낸다. 이 유형의 페이지는 실제 파일에서 데이터를 읽어오는 데 사용된다. 파일에서 읽은 데이터가 이러한 페이지로 로드된다.
스택 할당 부분이 새로운 메모리 관리 시스템에 적합할 수 있도록 setup_stack()을 수정해야 한다.
첫 스택 페이지는 지연적으로 할당될 필요가 없다. 페이지 폴트가 발생하는 것을 기다릴 필요 없이 스택 페이지를 load time 때 커맨드 라인의 인자들과 함께 할당하고 초기화 할 수 있다.
이 함수에서 스택을 확인하는 방법을 제공해야 한다.
setup_stack()
: 바로 써야할 페이지에 대해서 미리 물리 프레임을 연결해주는 함수
한개의 페이지 크기만큼을 USER_STACK 주소에서 내려 새로운 스택 바텀으로 설정해준다. vm_alloc_page()를 호출하여 바로 하나의 UNINIT 페이지를 생성하고 vm_claim_page() 값이 NULL이 아니면 if의 rsp값을 USER_STACK으로 변경한다.
프로젝트 2의 argument passing에서 스택에 인자를 쌓았을 때 그 인자들이 쌓인 페이지는 lazy_loading 되지 않고 곧바로 사용되어야 하기에 setup_stack() 함수에서 이를 설정해주는 것이다. 그러면 설정된 rsp인 USER_STACK부터 argument passing의 인자가 쌓인다.
59 of 141 tests failed.