728x90

➡️ 과제 설명(Gitbook)

 

Memory Management · GitBook

Memory Management In order to support your virtual memory system, you need to effectively manage virtual pages and physical frames. This means that you have to keep track of which (virtual or physical) memory regions are being used, for what purpose, by wh

casys-kaist.github.io

  • 당신의 가상 메모리 시스템이 지원되기 위해서는 효율적으로 가상 페이지들과 물리 프레임들을 관리해야한다.
  • 당신은 어느 가상 또는 물리 메모리 공간이 사용되고 있고 어떠한 목적으로, 누가 사용했는지 등을 추적하고 있어야한다.
  • 먼저 보조 페이지 테이블을 다루고 그 이후에 물리 프레임을 다뤄야한다.
  • ⭐ ”페이지”는 가상 페이지를 의미하고 “프레임”은 물리 페이지를 의미한다!

 

 


Page Structure and Operations

struct page

  • include/vm/vm.h 에 정의되어 있는 page 는 가상 메모리에서의 페이지를 의미하는 구조체이다.
  • 이 구조체는 page에 대해 우리가 알아야하는 모든 데이터를 저장하고 있다.
struct page {
  const struct page_operations *operations;
  void *va;              /* Address in terms of user space */
                                                 /* 유저 영역에서의 주소 */
  struct frame *frame;   /* Back reference for frame */
                                                 /* 프레임을 위한 역참조 */

  union { // union 중 하나의 멤버만 가질 수 있다.
    struct uninit_page uninit;
    struct anon_page anon;
    struct file_page file;
#ifdef EFILESYS
    struct page_cache page_cache;
#endif
  };
};

 


Page Operations

  • 한 페이지는 VM_UNINIT , VM_ANON , VM_FILE 이라는 세 타입 중 하나를 갖는다.
  • 하나의 페이지는 swap in, swap out, 페이지 삭제와 같은 여러 동작을 수행하게 된다.
  • 각 페이지 타입별로 요구되는 단계와 행해야하는 태스크가 다르다.
    • eg. VM_ANONVM_FILEdestroy 함수가 하는 일이 다르다.
  • 이를 구현하기 위한 방법으로는 switch-case 구문을 활용한 방법과 객체 지향 프로그래밍의 컨셉인 “클래스 상속”방법이 있다.
  • 실질적으로 “클래스”와 “상속”에 관련한 개념이 C 프로그래밍에 존재하지는 않지만 해당 개념을 이해하기 위해 함수 포인터를 사용할 것이다.
  • 이러한 방식은 리눅스같은 실제 운영체제 코드에서 사용되는 방식과 유사하다.
  • 함수 포인터는 함수를 가리키거나 메모리상의 실행가능한 코드를 가리키는 포인터이다.
  • 함수포인터는 다른 검사 없이 런타임에 결정되는 값을 바탕으로 특정한 함수를 호출한다.
  • 우리의 경우에 있어서는 단순히 destory(page) 를 호출하는 것만으로도 충분하다.
  • 컴파일러는 올바른 함수 포인터를 호출해서, 페이지 타입에 따라 적절한 함수 포인터를 호출할 것 이다.

 

  • 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;
};
  • 3개의 함수 포인터를 포함한 함수 테이블이다.

 

/* vm/file.c */

static const struct page_operations file_ops = {
    .swap_in = file_backed_swap_in,
    .swap_out = file_backed_swap_out,
    .destroy = file_backed_destroy,
    .type = VM_FILE,
};
  • page_operations에 대한 항목이 file_ops로 선언되어 있다.
  • file-backed 페이지에 대한 함수 포인터 테이블이다.

 

  • 함수 포인터 인터페이스로 file_backed_destroy 함수가 호출되는 방식

1. vm_dealloc_page(page) (vm.c)로 VM_FILE 을 전달한다.

2. vm_dealloc_page(page) 의 함수 내부에서 destroy(page) 함수를 호출한다.
3. destroy 함수(vm.h)를 호출하면 아래 매크로가 작동한다.

#define destroy(page) \
	if ((page)->operations->destroy) (page)->operations->destroy (page)

 

4. 인자로 VM_FILE 을 넘기고 .destroy 필드가 file_backed_destroy 를 가리키게 된다.

static const struct page_operations file_ops = {
	.swap_in = file_backed_swap_in,
	.swap_out = file_backed_swap_out,
	.destroy = file_backed_destroy,
	.type = VM_FILE,
};

 

 

Implement Supplemental Page Table

  • 이미 당신의 핀토스는 가상 메모리와 물리 메모리를 매핑해주는 pml4 를 가지고 있을 것이다. 그러나 이것은 충분치 않다.
  • 페이지 폴트와 자원 관리를 다루기 위해 각 페이지에 대한 추가적인 정보를 가지고 있는 보조 페이지 테이블이 필요하다.
  • ∴ ⭐ 프로젝트 3의 첫 작업으로 보조 페이지 테이블을 위한 기초적인 기능을 구현하는 걸 제안한다.

📢 vm/vm.c 에 보조 페이지 테이블을 관리하는 함수를 구현하시오.

  • 당신은 첫째로 어떻게 핀토스에 보조 페이지 테이블을 설계할 것인지 결정해야한다.
  • 보조 페이지 테이블에 대한 설계를 마친 뒤, 아래의 3가지 함수를 구현하라.

 

void supplemental_page_table_init (struct supplemental_page_table *spt);
  • 보조 페이지 테이블을 초기화한다.
  • 보조 페이지 테이블에서 사용하기 위한 자료구조를 선택할 수 있다.
  • 해당 함수는 새 프로세스를 시작하거나(userprog/process.c → initd) 새 프로세스가 fork될 때(userprog/process.c → __do_fork)호출된다.

 

struct page *spt_find_page (struct supplemental_page_table *spt, void *va);
  • 인자로 주어진 보조 페이지 테이블 spt 에서 가상 주소 va 와 대응되는 페이지 구조체를 찾아서 리턴한다.
  • 실패 시, NULL 리턴

 

bool spt_insert_page (struct supplemental_page_table *spt, struct page *page);
  • 인자로 주어진 보조 페이지 테이블 spt 에 페이지 구조체 page 를 삽입한다.

⚠️ 해당 함수에서 주어진 보조 페이지 테이블에서 가상 주소가 존재하지 않는지 검사해야한다.

 

 

Frame Management

  • struct frame
/* include/vm/vm.h */

/* The representation of "frame" */
struct frame {
  void *kva;
  struct page *page;
};
  • frame 구조체는 오직 2개의 필드를 갖는다.
  • kva : 커널 가상 주소
  • page : 페이지 구조체

⚠️ 프레임 관리 인터페이스를 구현할 때, 다른 멤버들을 추가할 수도 있다!

📢 vm/vm.c 에서 vm_get_frame, vm_claim_page and vm_do_claim_page 함수를 구현하시오.

 

static struct frame *vm_get_frame (void);
  • palloc_get_page 를 호출함으로써 유저 풀로부터 새로운 물리 페이지를 가져온다.
  • 유저 풀로 부터 성공적으로 페이지를 가져오면, 프레임을 할당하고 멤버를 초기화하고, 해당 프레임을 리턴한다.
  • vm_get_frame 함수를 구현한 뒤에는 모든 유저 공간 페이지(PALLOC_USER)들을 해당 함수를 통해 할당해야한다.
  • 일단 지금은 페이지 할당 실패의 경우에 따른 swap out을 다룰 필요가 없다.
  • ⭐ 지금은 PANIC(”todo”)로 해당 케이스들을 표시하라.

 

bool vm_do_claim_page (struct page *page);
  • 인자로 주어진 page에 물리 메모리 프레임을 할당한다.
  • vm_get_frame 를 호출하여 프레임 하나를 얻는다.
    • 이미 템플릿에 구현되어있다.
  • 이후, MMU를 세팅한다.
    • 가상 주소로부터 물리 주소로의 매핑을 페이지 테이블에 추가한다.
  • return
    • 성공 시, true
    • 실패 시, false

 

bool vm_claim_page (void *va);
  • 인자로 주어진 va에 페이지를 할당하고 해당 페이지에 프레임을 할당한다.
  • 우선 한 페이지를 얻고 그 이후에 해당 페이지를 인자로 갖는 vm_do_claim_page라는 함수를 호출한다.

 

 


➡️ 구현 [Section 2] Memory Management

 

🎯 1st Goal. ~ Anonymous Page

💡 Project 1, 2 테스트 결과 확인을 위한 최소 절차

1. Supplemental Page Table 과제 개요

  • 페이지 폴트 발생 시, 어떤 데이터가 있어야하는지 확인한다.
  • 프로세스 종료시, 어떤 페이지를 free 시킬 지 결정한다.
    • 말 그대로 PTE를 보조하는 역할을 수행한다.
    • 페이지 테이블 엔트리가 물리 주소를 가리키고 있는 상황에서 다른 페이지 테이블 엔트리도 같은 물리 메모리를 참조하는 경우가 있다.
    • 기존 페이지 테이블은 단순히 가상 메모리 ↔ 물리 메모리에 대한 매핑만 수행한다.
      페이지 폴트가 발생하면 원래 어떤 페이지가 있었는지 알 수 있는 방법이 없다. → 이를 추적하는 작업을 수행한다.
  • 구현해야하는 함수
    1. supplemental_page_table_init
    2. spt_find_page
    3. spt_insert_page

2. Frame Management 과제 개요

 

 

 

 


1. Supplemental_page_table 구현하기

/* include/vm/vm.h */

/*------------------------- [P3] Memory Management --------------------------*/
struct supplemental_page_table {
    struct hash spt_hash; // 해쉬 테이블로 구현
};
  • ⭐ hash table bucket은 동적할당을 하는데 해당 bucket에 연결된 연결 리스트는 동적할당을 하지 않는다!

 

struct page {
    const struct page_operations *operations;
    void *va;              /* Address in terms of user space */
    struct frame *frame;   /* Back reference for frame */

    /* Your implementation */
    /*------------------------- [P3] Memory Management --------------------------*/
    struct hash_elem hash_elem;

    /* Per-type data are binded into the union.
     * Each function automatically detects the current union */
    union {
        struct uninit_page uninit;
        struct anon_page anon;
        struct file_page file;
#ifdef EFILESYS
        struct page_cache page_cache;
#endif
    };
};
    • spt의 구현은 1) array, 2) list, 3) hash table, 4) bitmap … 에서 고를 수 있는 데, Hash table을 사용하는 게 시간, 공간 복잡도 측면에서 좋다고 하므로 해쉬 테이블을 사용한다.

Most implementations of the virtual memory project use a hash table to translate pages to frames. You may find other uses for hash tables as well. (_Appendix: Hash Table)

  • 그리고 넌지시 계속 해시 테이블을 사용하라고 강조하는 것 같음
  • [Appendix : Hash Table] 핵심만 정리하기
    • struct hash 로 표현되어 있다.
    • struct hash 는 해시 테이블 전체를 의미하며 실질적인 원소는 숨겨져 있다.
      • 직접적으로 접근할 수도, 필요도 없다.
      • 함수와 매크로를 통해 접근하라.
    • struct hash_elem 타입의 원소로 접근이 가능하다.
    • hash_entrylist_entry 와 사용법이 동일하다.
      • elem 값으로 해당 구조체의 시작주소를 리턴받는다.
    • 해시 테이블의 요소인 key는 고유성, 불변성을 갖춰야한다.

 

0. 도움 함수들

/*------------------------- [P3] Memory Management 추가 함수 --------------------------*/
/* [KAIST 35p.] vm_hash_func 
 * spt에 넣을 인덱스를 해쉬 함수를 돌려서 도출한다.
 * hash.c - 'hash_hash_func' 의 구현 형태
 * hash_bytes 설명 : Returns a hash of the SIZE bytes in BUF(hash_elem).
*/
static unsigned 
hash_func (const struct hash_elem *e, void *aux UNUSED) {
    const struct page *p = hash_entry(e, struct page, hash_elem); // hash 테이블이 hash_elem을 원소로 가지고 있으므로 페이지 자체에 대한 정보를 가져온다.
    return hash_bytes(&p->va, sizeof(p->va)); // 인덱스를 리턴해야하므로 hash_bytes를 리턴한다. 
}

/* [KAIST 35p.] vm_less_func 
 * 체이닝 방식의 spt를 구현하기 위한 함수
 * 해시 테이블 버킷 내의 두 페이지의 주소값 비교
 * hash.c - 'hash_less_func' 의 구현 형태
*/
static unsigned 
less_func(const struct hash_elem *a, const struct hash_elem *b, void *aux) {
    const struct page *a_p = hash_entry(a, struct page, hash_elem);
    const struct page *b_p = hash_entry(b, struct page, hash_elem);
    return a_p->va < b_p->va;
}

/* [KAIST 34p.] insert_vme
 * spt 해시 테이블에 페이지를 삽입한다.
*/
static bool 
insert_page(struct hash *h, struct page *p) {
    if(!hash_insert(h, &p->hash_elem))
        return true;
    else
        return false;
}

/* [KAIST 34p.] delete_vme
 * spt 해시 테이블에서 페이지를 삭제한다.
*/
static bool 
delete_page(struct hash *h, struct page *p) {
    if(!hash_delete(h, &p->hash_elem))
        return true;
    else
        return false;
} 

 

 

1. supplemental_page_table_init

  • spt를 초기화하는 작업을 수행하는데, 해시 테이블을 사용해서 이를 구현하기로 했으므로 hash_init을 사용하여 해시 테이블을 초기화해준다.
/* vm/vm.c */

void
supplemental_page_table_init (struct supplemental_page_table *spt UNUSED) {
    hash_init(&spt->spt_hash, hash_func, less_func, NULL);
}

 

2. spt_find_page

  • spt에서 va와 대응되는 page를 찾는다.
/* vm/vm.c */

struct page *
spt_find_page (struct supplemental_page_table *spt UNUSED, void *va UNUSED) {
    /*
    * va를 통해 page를 찾아야하는데, hash_find의 인자는 hash_elem이므로 이에 해당하는 hash_elem을 만들어준다.
    * 
    * 1. dummy page 생성(hash_elem 포함)
    * 2. va 매핑
    * 3. 해당 페이지와 같은 해시 값을 갖는 hash_elem을 찾는다.
    */

    struct page *page = (struct page *)malloc(sizeof(struct page)); 
    // ↳ page를 새로 만들어주는 이유? : 해당 가상 주소에 대응하는 해시 값 도출을 위함
    //   page 생성 시, page_elem도 생성된다.
    struct hash_elem *e;

    page->va = pg_round_down(va); // 페이지를 만들고 가상 주소를 매핑해주는 작업 수행

    /* e와 같은 해시값을 갖는 page를 spt에서 찾은 다음 해당 hash_elem을 리턴 */
    e = hash_find(&spt->spt_hash, &page->hash_elem);
    free(page);

    if (e == NULL)
        return NULL;
    else
        return hash_entry(e, struct page, hash_elem); // e에 해당하는 page 리턴
}
  • va → page 과정을 위해
    page 생성 → hash_elem 생성 → 해당 hash_elem을 통해 find !
    의 작업을 수행한다.

 

3. spt_insert_page

  • 앞서 구현한 insert_page 함수를 응용한다.
/* vm/vm.c */

bool
spt_insert_page (struct supplemental_page_table *spt UNUSED,
        struct page *page UNUSED) {
    return insert_page(&spt -> spt_hash, page);
}

 

 


2. Frame_table 구현하기

  • ❓ Frame table
    • Page를 가상 주소 공간에 할당하기 위해 OS는 Frame table을 가지고 있다.
    • Frame table에는 비어있는 프레임, 사용중인 프레임, 총 프레임과 같은 정보를 가지고 있다.
  • frame_table은 리스트로 구현한다.
  • 👋 frame_table은 리스트로 구현하는 이유
    • frame은 전역적으로 관리하면서 frame들의 개수는 어마어마하게 많을 거 이기 때문에, linked list로 구현하는듯?
  • ⭐ frame_table은 전역적으로 구현해야한다.

 

  • frame 구조체는 프로세스의 커널 가상 주소에 위치해있다.
  • frame_table : 사용 가능한 빈 프레임들의 집합
/* vm/vm.c */

static struct list frame_table;
static struct list_elem *start; // frame_table을 순회하기 위한 첫 번째 원소

 

/* include/vm/vm.h */

struct frame {
    void *kva;
    struct page *page; // 해당 프레임과 연결된 va 페이지

    /*------------------------- [P3] Memory Management --------------------------*/
    struct list_elem frame_elem; // frame을 리스트 형태로 구현했기 때문에 list_elem을 추가한다.
};
  • frame table을 연결 리스트로 구현하기 때문에 list_elem을 필드로 추가해준다.
    • frame table을 순회하기 위한 용도!

 

1-1. vm_get_frame

  • palloc()을 하고 새 프레임을 할당받아온다.
  • 만약 가용한 페이지가 없다면 페이지를 내쫓고 해당 페이지를 리턴한다.
    • 즉, 항상 유효한 주소를 반환한다.

 

/* vm/vm.c */

static struct frame *
vm_get_frame (void) {
    /* TODO: Fill this function. */
    struct frame *frame = (struct frame*)malloc(sizeof(struct frame)); 
    // frame 구조체를 위한 공간 할당한다.(작으므로 malloc으로 _Gitbook Memory Allocation 참조)

    frame->kva = palloc_get_page(PAL_USER); 
    // 유저 풀(실제 메모리)로부터 페이지 하나를 가져온다. 
    // ↳ 사용 가능한 페이지가 없을 경우, NULL 리턴
    if(frame->kva == NULL) { // 사용 가능한 페이지가 없는 경우
        frame = vm_evict_frame(); // swap out 수행 (frame을 내쫓고 해당 공간을 가져온다.)
        frame->page = NULL;

        return frame; // 무조건 유효한 주소만 리턴한다는 말이 통하는 이유 : swap out을 통해 공간 확보후, 리턴하기 떄문
    }
    list_push_back (&frame_table, &frame->frame_elem); // 새로 frame을 생성한 경우
    frame->page = NULL;

    ASSERT (frame != NULL);
    ASSERT (frame->page == NULL);
    return frame;
}
  • palloc_get_page 주석 설명(threads/palloc.c) : 사용 가능한 단일 페이지를 가져오고 커널 가상 주소를 반환한다. PAL_USER가 설정되어 있으면 사용자 풀에서 페이지를 가져오고, 그렇지 않으면 커널 풀에서 페이지를 가져온다. FLAGS에 PAL_ZERO가 설정되어 있으면, 그러면 그 페이지는 0으로 채워진다. 사용 가능한 페이지가 없으면 FLAGS에서 PAL_ASSERT가 설정되지 않은 경우 null 포인터를 반환한다. 이 경우 커널이 패닉 상태에 빠진다.

 

  • frame 구조체에 대한 정보는 malloc으로 할당하고 실제 frame에 해당하는 물리 메모리 공간은 palloc을 이용해서 할당한다.

 

  • 🙋‍♂️ palloc이면 ‘page’할당이니까 가상 메모리에 대한 공간할당이 아닌가?
    • Nope. USER_POOL로 부터 메모리 공간을 가져오므로 실제 메모리를 가져오는 과정이다. 다만, page크기만큼의 공간(4kb)을 frame과 매핑하여 가져온다.
    • 근데 왜 이걸 kva(커널 가상 주소)에 넣지? Gitbook - Introduction 을 보면 직접적으로 물리 메모리에 접근하는 방법은 없고, 커널 가상 주소로 부터 접근해야한다고 나와있다.
      • physical memory의 구성 : kernel pool, user pool
      • kernel 가상 주소는 physical memory와 일대일 매핑된다.
      • 따라서 유저 풀로 부터 얻은 메모리도 kva 중 하나이다.

 

  • palloc_get_page(PAL_USER)
    • 유저 풀로 부터 페이지를 할당 하는 이유?
    • 커널 풀의 페이지가 부족해지면 커널 함수들의 작동이 어려워질 수 있으므로 유저 풀로 부터 먼저 페이지를 할당한다. (threads/palloc.c 주석 참조)

 

  • User Pool 🆚 Kernel Pool 설명 (from threads/palloc.c)
/* Page allocator.  Hands out memory in page-size (or
   page-multiple) chunks.  See malloc.h for an allocator that
   hands out smaller chunks.

   System memory is divided into two "pools" called the kernel
   and user pools.  The user pool is for user (virtual) memory
   pages, the kernel pool for everything else.  The idea here is
   that the kernel needs to have memory for its own operations
   even if user processes are swapping like mad.

   By default, half of system RAM is given to the kernel pool and
   half to the user pool.  That should be huge overkill for the
   kernel pool, but that's just fine for demonstration purposes. */
  /* 페이지 할당자. 메모리를 페이지 크기(또는 페이지 수)로 나눈다.
     더 작은 청크에 대한 할당자는 malloc.h를 참조하라.
     시스템 메모리는 커널과 사용자 풀이라고 불리는 두 개의 "풀"로 나뉜다.
       사용자 풀은 사용자(가상) 메모리 페이지를 위한 것이고 커널 풀은 다른 모든 것을 위한 것이다.
     여기서의 아이디어는 사용자 프로세스가 미친 듯이 스와핑되더라도 커널이 자체 작업을 위한 
     메모리를 가져야 한다는 것이다.(커널 풀에의 사용에 제한을 둬야한다.)

     기본적으로 시스템 RAM의 절반은 커널 풀에, 절반은 사용자 풀에 제공된다.
     커널 풀의 경우 엄청난 오버킬이 될 것이지만, 시연 목적으로는 괜찮다. */

 

  • ✨ Summary : 메모리는 두 개의 풀로 나뉘는 데, 사용자 가상 메모리의 경우 USER POOL에 할당되어야한다.

 

1-2. vm_evict_frame

  • 위의 vm_get_frame에서 사용한 vm_evict_frame 를 남은 부분을 채운다.
  • 메모리에 존재하는 page와 연결된 frame을 메인 메모리에서 디스크로 끌어 내린다.

 

/* vm/vm.c */

static struct frame *
vm_evict_frame (void) {
    struct frame *victim UNUSED = vm_get_victim ();
    /* TODO: swap out the victim and return the evicted frame. */
    swap_out(victim->page);
    return NULL;
}
  • 매크로로 존재하는 swap_out을 적용시켜준다.
  • swap_out의 딥한 과정은 [Swap In/Out] 과정에서 다시 구현한다.

 

1-3. vm_get_victim

  • 페이지 교체 전략을 설정해야한다.
    • ☑️ LRU
    • ❓ clock
    • ❓ second-chance
  • for문을 순회하며 pml4에서 제거할 페이지를 찾는다.
  • LRU 알고리즘을 사용한다. : 가장 오랫동안 참조되지 않은 페이지를 교체하는 기법
static struct frame *
vm_get_victim (void) {
    struct frame *victim = NULL;
     /* TODO: The policy for eviction is up to you. */
    struct thread *curr = thread_current();
    struct list_elem *frame_e;
    struct list_elem *e = start;

    // LRU 방식
    // frame table의 처음과 끝을 순회하면서 access bit가 0인 프레임을 찾는다.
    for (frame_e = list_begin(&frame_table); frame_e != list_end(&frame_table); frame_e = list_next(frame_e)) {
        victim = list_entry(frame_e, struct frame, frame_elem);
        if (pml4_is_accessed(curr->pml4, victim->page->va)) // access bit가 1이라면 true
            pml4_set_accessed (curr->pml4, victim->page->va, 0); // access bit를 초기화 해준다.
        else
            return victim;
    }
    return victim;
}

 

2. vm_claim_page

bool
vm_claim_page (void *va UNUSED) {
    struct thread *curr = thread_current();
        /* TODO: Fill this function */
        struct page *page = spt_find_page(&curr -> spt, va); // va에 페이지를 할당한다.
        if (page == NULL)
            return false;

        return vm_do_claim_page (page); // 해당 페이지에 프레임을 할당한다.
}
  • [Gitbook - Memory Management]
    • 인자로 주어진 va 에 페이지 하나를 할당한다.
    • 후속작업인 페이지에 프레임 할당하는 작업은 vm_do_claim_page 에서 이루어진다.

 

3. vm_do_claim_page

  • [Gitbook - Memory Management]
  • vm_get_frame 를 호출하여 프레임 하나를 얻는다.
    • 이미 템플릿에 구현되어있다.
    • 이후, MMU를 세팅한다.
    • 가상 주소로부터 물리 주소로의 매핑을 페이지 테이블에 추가한다.
  • pml4_set_page 주석 설명
/* threads/mmu.c */

/* Adds a mapping in page map level 4 PML4 from user virtual page
 * UPAGE to the physical frame identified by kernel virtual address KPAGE.
 * UPAGE must not already be mapped. KPAGE should probably be a page obtained
 * from the user pool with palloc_get_page().
 * If WRITABLE is true, the new page is read/write;
 * otherwise it is read-only.
 * Returns true if successful, false if memory allocation
 * failed. */

/* 사용자 가상 페이지 UPAGE에서 커널 가상 주소 KPAGE로 식별된 물리적 프레임으로 
 * 페이지 맵 레벨 4 PML4의 매핑을 추가한다.
 * UPAGE가 아직 매핑되지 않아야 합니다. KPAGE는 아마도 palloc_get_page()를 가진 
 * 사용자 풀에서 얻은 페이지일 것이다.
 * 쓰기 가능이 참이면 새 페이지는 읽기/쓰기가 된다
 * 그렇지 않으면 읽기 전용이다.
 * 성공하면 true를 반환하고, 메모리 할당이 실패하면 false를 반환한다. */
  • ✨ Summary : “va”와 “palloc_get_page()를 통해 연결한 얻은 물리 페이지”를 연결한다. WRITABLE 추가 필요!

 

pml4_set_page (uint64_t *pml4, void *upage, void *kpage, bool rw)

  • pml4 : 페이지 테이블
  • upage : va
  • kpage : frame
  • rw : WRITABLE
static bool
vm_do_claim_page (struct page *page) {
  struct frame *frame = vm_get_frame (); // 프레임 하나를 얻는다.

  /* Set links */
  frame->page = page; // 프레임의 페이지(가상)로 얻은 페이지를 연결해준다.
  page->frame = frame; // 페이지의 물리적 주소로 얻은 프레임을 연결해준다.

  /* TODO: Insert page table entry to map page's VA to frame's PA. */
  struct thread *curr = thread_current();
  bool writable = page -> writable; // 해당 페이지의 R/W 여부
  pml4_set_page(curr->pml4, page->va, frame->kva, writable); // 현재 페이지 테이블에 가상 주소에 따른 frame 매핑

  return swap_in (page, frame->kva);
}

 

 

 

 

728x90