728x90

➡️ 과제 설명(Gitbook)

 

Anonymous Page · GitBook

In this part of this project, you will implement the non-disk based image called anonymous page. An anonymous mapping has no backing file or device. It is anonymous because it does not have any named file source (unlike file-backed pages). Anonymous pages

casys-kaist.github.io

 

  • 디스크 기반이 아닌 이미지를 구현한다.
  • 익명 페이지는 파일 기반 페이지(file-backed pages)와 달리 이름이 있는 파일 소스를 가지고 있지 않음(익명매핑에는 백업 파일 혹은 장치가 없음)
  • 익명페이지는 스택과 힙처럼 실행가능 파일에서 사용됨
  • 익명 페이지 구조체 anon_pageinclude/vm/anon.h 에 선언되어 있음
    • 현재는 구조체가 비어있음(구현을 위한 구조체 멤버 추가 가능)
    • include/vm/vm.h 에서 struct page 를 확인할 것 struct anon_page anon 가 페이지 구조체에 포함되어 있음)

 

📢 페이지는 크게 익명(VM_ANON)페이지와 파일 기반 페이지(VM_FILE)로 나눌 수 있다.

- 익명 페이지 : 파일 매핑 x, 커널 할당 페이지
eg: stack, heap (런타임 시 결정)
- 파일 페이지 : 파일 매핑 o
eg: code, data (컴파일 시 결정 - 실제 파일로 부터 가져옴)

 

 


Page Initialization with Lazy Loading

💡 Demanding Paging 이라고도 한다!

  • 지연 로딩(lazy loading)은 필요시점까지 메모리의 로딩을 지연시킴
  • 페이지가 할당되었다는 것은 대응되는 페이지 구조체는 있지만 연결된 물리 메모리 프레임은 아직 없고, 페이지의 실제 콘텐츠는 아직 로드되지 않음을 의미
  • 페이지 폴트로 인해 실제로 콘텐츠가 필요할 때 로드됨(페이지 폴트로 시그널을 받음)
  • 세 가지 페이지 타입(VM_UNINIT, VM_ANON, VM_FILE)을 가지는데 각 페이지별로 초기화 루틴이 다름
  • 아래에서 자세히 다룰 예정이며, high-level 관점에서의 페이지 초기화 흐름을 설명
    • 커널이 새로운 페이지를 달라는 요청을 받으면 vm_alloc_page_with_initializer 가 호출됨
    • initializer가 페이지 구조를 할당하고 페이지 유형에 따라 적절한 initializer를 설정하여 새로운 페이지를 초기화함
    • 그리고, 컨트롤을 사용자 프로그램으로 반환
    • 사용자 프로그램이 실행될때, 페이지 폴트가 발생
      • 프로그램은 소유하고 있다고 생각되는 페이지에 액세스하려고 하지만 페이지 내용이 아직 없기 때문에 페이지 폴트 발생
    • 페이지 폴트 처리 절차 중에 uninit_initialize 을 호출하고 이전에 설정한 초기화 함수를 호출합니다.
    • 🔥 익명 페이지를 위한 초기화 함수 : 'anon_initializer'
      🔥 파일기반 페이지를 위한 함수 : 'file_backed_initializer'
  • 페이지는 초기화→(페이지 폴트 → 지연 로딩 → 스왑 인 → 스왑 아웃 → …) → 삭제 라는 cycle을 가짐
  • 페이지 타입(혹은 VM_TYPE)별 초기화 방법이 다른 것처럼, cycle의 전환(transition)마다 페이지 타입별로 다른 프로시저가 요구됨

⭐ 각 페이지 타입에 따른 전환(transition processes) 과정을 구현해야한다.

 

 

Lazy Loading for Executable

  • 지연로딩에서 프로세스가 실행을 시작 시, 당장 필요한 메모리 부분만 메인 메모리에 로드됨
  • 지연로딩은 한번에 바이너리 이미지들을 로드하는 즉시로딩(eager loading)보다 상대적으로 오버헤드를 줄일 수 있음
  • VM_UNINIT (페이지타입) : 지연 로딩을 지원하기 위한 페이지 타입
  • include/vm/vm.h 확인
  • 모든 페이지들은 초기에 VM_UNINIT 타입의 페이지로 생성된다.
📢 UNINIT 이라는 거는 따로 페이지 타입이 아니라, 단순히 frame과 매핑되지 않은 “껍데기”뿐인 페이지를 나타낸다는 의미이다.
- 말 그대로 초기화되지않은(= 타입(VM_ANON, VM_FILE)이 결정되지않은) 페이지.

 

구현을 완성해야 하는 함수

  • 초기화되지 않은 페이지들을 위한 페이지 구조체를 제공한다.
  • (include/vm/uninit.h 에서 구조체 uninit_page )
  • 초기화되지 않은 페이지를 생성, 초기화, 삭제하는 함수(include/vm/uninit.c 확인)
  • 페이지 폴트가 일어나면, 페이지 폴트 핸들러는(page_fault in userprog/exception.c) vm_try_handle_fault ( vm/vm.c ) 함수에게 제어권을 넘긴다.
    • 함수 역할 : 페이지 폴트가 유효한지 먼저 확인
  • 유효하다는 것은 액세스하는 오류가 유효하지 않다는 것을 의미
  • 가짜(bogus) 오류인 경우 일부 내용을 페이지에 로드하고 사용자 프로그램에 제어 권한을 반환함
📢 “페이지 폴트”가 되게 복잡하게 나와있는데, 핀토스에서는 페이지 폴트의 종류를 두 가지로 나눠놓은 듯하다.
- 페이지 폴트의 보편적인 의미 : 페이지 테이블 내에 va와 매핑되는 물리 주소가 존재하지 않는다.

- Gitbook에서 얘기하는 페이지 폴트의 두 가지 경우
👉 Case 1. 유효하지 않은 메모리 공간에 대한 접근
   (= 이 경우를 real page fault 라고 보는 듯)
  eg. 메모리 범위를 넘어간다거나 커널 영역에 대한 침범 etc..
👉 Case 2. “Lazy loading”으로 인해 페이지 껍데기(VM_UNINIT)만 존재하는 경우(= bogus fault) 
  • bogus 페이지 폴트가 발생하는 3가지 케이스
    : 지연로드 페이지, 스왑 아웃 페이지, 쓰기 보호 페이지(Copy-on-Write (Extra) 참조)

 

  • 지연 로딩 페이지 폴트 발생시 커널은 이전에 vm_alloc_page_with_initializer 에서 설정한 initializer를 호출하여 세그먼트를 lazy load 함

lazy_load_segment ( userprog/process.c ) 함수를 구현한다.

vm_alloc_page_with_initializer() 함수를 구현한다.

  • 전달된 vm_type에 맞는 적절한 initializer를 가져온 후, 이 함수를 인자로 갖는 uninit_new 함수를 호출해야 한다.

 


bool vm_alloc_page_with_initializer (enum vm_type type, void *va,
        bool writable, vm_initializer *init, void *aux);
  • 초기화되지 않은 주어진 타입의 페이지를 생성한다.
  • 초기화 되지 않은 페이지의 swap_in 핸들러는 자동으로 페이지 타입에 맞게 페이지를 초기화하고 주어진 AUX를 인자로 삼는 INIT함수를 호출한다.
  • 페이지 구조체를 가지게 되면, 프로세스의 보조페이지 테이블에 그 페이지를 삽입해야 한다.
  • vm.h에 선언된 VM_TYPE 매크로를 사용하면 된다.

 

  • 페이지폴트 핸들러는 연쇄적으로 함수를 호출하고 swap_in 함수를 호출하면 마침내 uninit_initialize 함수에 다다름
  • uninit_initialize : 완성된 함수이다.
    • 내 구현에 따라 수정도 가능

 


static bool uninit_initialize (struct page *page, void *kva);
  • 처음으로 페이지 폴트가 발생한 페이지를 초기화
  • 템플릿 코드는 먼저 vm_initializer와 aux를 가져오고 함수 포인터를 통해 해당 page_initializer를 호출한다.

 

⭐ 필요에 따라 수정

  • 필요에 따라 vm/anon.c에 있는 vm_anon_init이나 anon_initializer를 수정할 수 있다.

void vm_anon_init (void);
  • 익명 페이지의 하위시스템을 초기화

💡 이 함수에서 익명 페이지와 연관된 어떤 것도 설정 가능


bool anon_initializer (struct page *page,enum vm_type type, void *kva);
  • 처음으로 page→operation에 있는 익명페이지를 위한 핸들러를 설정하는 함수
  • 현재는 비어있는 구조체인 anon_page에 있는 정보를 업데이트 할 필요가 있을 것

💡 이 함수는 익명 페이지를 초기화하는 함수로 사용(ex. VM_ANON)

 

load_segment 와 lazy_load_segment (userprog/process.c)를 구현 (실행파일로부터 세그먼트가 로드되는 것을 구현한다.)

  • 이런 모든 페이지들은 지연적으로 로드되어야 함
  • 오직 커널만이 이들에 대한 페이지 폴트를 유발할 수 있음

⭐ 프로그램 로더의 핵심인 load_segment (userprog/process.c)의 loop를 수정한다.

  • 루프를 돌때마다 load_segment는 대기 중인 페이지 오브젝트를 생성하는vm_alloc_page_with_initializer 를 호출
  • 페이지 폴트가 발생하는 순간은 세그먼트가 실제 파일에서 로드될 때이다.

 


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 인수로 설정해야 한다.

⭐ 바이너리 파일을 로드할 때, 필수정보를 포함하는 구조체를 생성하는 것이 좋다.

 


static bool lazy_load_segment (struct page *page, void *aux);
  • load_segmen함수 내부의 vm_alloc_page_with_initialize의 4번째 인자가 lazy_load_segment!!!
  • 이 함수는 실행가능 파일의 페이지들을 초기화하는 함수이고, 페이지 폴트가 발생할때 호출됨
  • 페이지 구조체와 aux를 인자로 받음

aux는 load_segment에서 설정하는 정보(구현해야한다.)

  • 이 정보를 사용하여 세그먼트를 읽을 파일을 찾고 최종적으로 세그먼트를 메모리에서 읽어야 함

 

⭐ 스택 할당부분이 새 메모리 관리 시스템에 적합하게 setup_stack(userprog/process.c)을 수정해야 함

  • 첫번째 스택 페이지는 지연적으로 할당될 필요 없음
  • 페이지 폴트가 발생하는 것을 기다릴 필요 없이, 로드 시간에 커멘드 라인의 인자들과 함께 할당하고 초기화 할 수 있음

⭐ 스택을 확인하는 방법을 제공해야함

  • vm_type (예 - VM_MARKER_0)에 있는 보조 메이커를 페이지들을 마킹하는데 사용할 수 있다.

 

⭐ 마지막으로 vm_try_handle_fault 함수를 수정하고 보조 페이지 테이블(spt_find_page 를 통해) 을 참조하여, 페이지 폴트된 주소에 해당하는 페이지 구조체를 해결한다.

  • 모든 요구사항을 구현하고 난 뒤에 fork를 제외한 프로젝트 2의 테스트들을 통과할 것임!!

 

Supplemental Page Table - Revisit

  • 이제 복사 및 정리 작업을 지원하는 spt 인터페이스를 다시 수정한다.
  • 이런 작업들은 프로세스를 생성(정확히는 자식 프로세스를 생성)하거나 파괴할 때 필요하다.
  • 세부적으로, 이 지점에서 우리가 spt를 다시 수정하는 이유는 위에서 구현한 몇 가지 초기화 함수들을 사용해야할 수도 있기 때문이다.

supplemental_page_table_copy , supplemental_page_table_kill 함수를 구현한다.

 


bool supplemental_page_table_copy (struct supplemental_page_table *dst,
    struct supplemental_page_table *src);
  • src에서 dst까지 spt를 복사할 것
  • 자식이 부모의 실행 컨텍스트를 상속할 필요가 있을 때 사용됨(ex.fork())

src의 spt에 있는 각 페이지를 반복하고, dst의 spt 엔트리에 정확한 복사본을 만들 것

  • 초기화되지 않은 페이지를 할당하고, 즉시 요청할 필요가 있음

 

📢 fork에서 부모와 자식이 페이지 테이블을 공유하므로써 속도 향상을 도모할 수 있다.
Ref. https://taeguk2.blogspot.com/2015/10/fork-vfork.html

 


void supplemental_page_table_kill (struct supplemental_page_table *spt);
  • spt에 의해 유지되던 모든 자원들을 해제하는 함수
  • 이 함수는 process가 exits(유저 프로그램-process_exit())할 때 호출
  • 페이지 엔트리를 반복하고 페이지 테이블에 대해 삭제를 호출
  • 이 함수에서 실제 페이지테이블(pml4)와 물리 메모리(palloc된 메모리)에 대해 걱정할 필요 없음
  • spt가 정리되고 나면 호출자가 정리

 

 

Page Cleanup

uninit_destroy (vm/uninit.c)와 anon_destroy(vm/anon.c)를 구현한다.

  • 초기화되지 않은 페이지의 destory 작업을 위한 핸들러이다.
  • 비록 초기화되지않은 페이지들이 다른 페이지 오브젝트로 변화된다고 하더라도, 프로세스가 종료될때 여전히 초기화되지 않은 페이지들이 있을 수 있다.

static void uninit_destroy (struct page *page);
  • 페이지 구조체에 의해 유지되던 자원들을 해제시킨다.
  • 페이지의 vm type을 확인하고 그에 맞춰 핸들링 해야한다.

🔥 익명 페이지를 다루는 것만 가능하고, file-backed 페이지 들을 위해 이 함수를 다시 사용하게 될 것이다.

 


static void anon_destroy (struct page *page);
  • 익명 페이지에 의해 유지되던 자원들을 해제시킨다.
  • 명시적으로 페이지 구조체를 free 시킬 필요는 없음, 호출자가 수행할 예정이다.
  • 🎉 이제 프로젝트2의 모든 테스트들이 통과되어야 한다.

 

 

 

 
728x90

'🌱 Dev Diary > 📄 TIL' 카테고리의 다른 글

Project 3. Stack Growth(1)  (0) 2023.01.19
Project 3. Anonymous Page(2)  (0) 2023.01.19
Project 3. Memory Management  (0) 2023.01.19
Project 3. Introduction  (0) 2023.01.19
Project 2. System Calls 구현 (3) - Process related  (0) 2023.01.05