미소를뿌리는감자의 코딩

[Pintos_project3 (9)] vm_claim_page, vm_do_claim_page 본문

정글 일지

[Pintos_project3 (9)] vm_claim_page, vm_do_claim_page

미뿌감 2024. 12. 2. 21:16
728x90

1. 개요

이번에 vm_claim_page와 vm_do_claim_page 구현을 통해서 VM와 PM를 mapping 시켜주는 것을 구현해 주려고 한다.

함수의 흐름을 정리하기 위해 적어보았다.

 

2. 본문

// vm/vm.c

/* Claim the page that allocate on VA. */
/* 페이지를 할당 - 물리적 프레임 할당 
 * vm_get_frame을 통해 frame을 가져오는 작업은 완료되어 있음
 * 이제 MMU (Memory Management Unit) 을 설정해 주어야 함.
 * 가상 주소를 물리 주소에 매핑하는 작업을 page table에 추가하고, 성공 여부를 bool을 통해 알려줘야 함.
 *  */
bool vm_claim_page(void *va UNUSED) {
    /* TODO: Fill this function */
	/* Pseudo cod
	 * vm_get_frame을 통해 프레임을 가져온 후, 
	 * va에서 외부 구조체로 page에 접근한 후; 프레임과 연결..?
	 * 연결을 어떤 변수에다가 해야하지? */

    struct page *page = spt_find_page(&thread_current()->spt, va); // va로 spt에서 va에 해당하는 페이지를 찾고,

    if (page == NULL) // spt에 va에 해당하는 페이지가 할당되어 있지 않다면 false 반환
        return false;

    return vm_do_claim_page(page);
}

 

spt_find_page를 통해서 supplement page table에서 va에 해당하는 페이지를 찾아준다.

만약, 페이지가 없다면 false를 반환하고, 페이지가 있다면 vm_do_claim_page를 통해서 페이지 할당을 시작한다.

 

// vm/vm.c

/* Claim the PAGE and set up the mmu. */
static bool vm_do_claim_page(struct page *page) {
    struct frame *frame = vm_get_frame(); // vm_get_frame으로 새로 값을 할당할 frame을 PM에서 찾기.

    /* Set links */
    frame->page = page; // 해당 프레임의 페이지를 현재 페이지로 mapping하고
    page->frame = frame; // 현재 페이지의 프레임을 새로 할당한 프레임과 mapping 시켜준다.

    /* TODO: Insert page table entry to map page's VA to frame's PA. */
	// VA를 PA와 매핑이 성공되었다면, true가 반환되고, 실패했다면 false가 반환됨.
    if (!pml4_set_page(thread_current()->pml4, page->va, frame->kva, page->writable))
        return false;

    return swap_in(page, frame->kva);  
	/* uninit_initialize - swap_in 핸들러가 실행되며 uninit_initialize가 실행된다.
	 * uninit_initialize에서 vm_alloc_page_with_initializer에서 설정한 초기화 함수가 실행됨.
	 */ 
}

vm_get_frame을 통해서, physical memory에서 frame을 할당 받는다.

이후, page와 frame을 서로 mapping 시켜 준다.

 

struct frame과 struct page를 확인해보면, 서로 포인터로 참조하는 멤버가 있음을 확인할 수 있다. [ page - frame mapping ]

pml4_set_page는 VA와 PA를 PML4 테이블을 이용해서 address끼리 매핑해주는 함수이다. [ VA - PA mapping ]

 

pml4_set_page 함수를 조금 알아보자.

/* 사용자 Virtual Page인 UPAGE를 페이지 맵 레벨4 (PML4)에 추가하여 
 * 커널 Virtual address (KPAGE)로 식별되는 물리적 프레임과 매핑을 설정
 * ---> KPAGE는 PM와 1:1 매핑이 되어 있음. 따라서, 커널은 물리 메모리를 '직접' 참조할 수 있음.
 * -----> 예를 들어 물리 주소 0x1000에 있는 페이지는 커널 가상 주소에서도 0x1000으로 접근할 수 있음.
 * UPAGE는 이미 매핑되어 있으면 안됨.
 * KPAGE는 palloc_get_page()를 사용하여 사용자 pool에서 얻은 페이지 이어야 함.
 * WRITABLE이 true이면 새 페이지는 읽기/쓰기 가능 상태가 되고,
 * 그렇지 않은 경우 읽기 전용 상태가 됨.
 * 메모리 할당에 성공하면 true를 반환. 실패하면 false 반환.*/

/* 목적: VA와 PA을 PML4 구조를 통해 매핑 해주는 함수. */
bool pml4_set_page(uint64_t *pml4, void *upage, void *kpage, bool rw) { // page -> va의 *upage니까, 페이지를 의미
    ASSERT(pg_ofs(upage) == 0); // 페이지 시작 주소 offset인지 확인
    ASSERT(pg_ofs(kpage) == 0); // ''
    ASSERT(is_user_vaddr(upage));// upage가 user address인지 확인
    ASSERT(pml4 != base_pml4); // 주어진 pml4가 현재 실행 중인 스레드의 base_pml4와 같지 않은지 확인?

    uint64_t *pte = pml4e_walk(pml4, (uint64_t)upage, 1); // PML4에서 VA에 해당하는 page table entry를 찾음./pte = page table entry

    if (pte)
        *pte = vtop(kpage) | PTE_P | (rw ? PTE_W : 0) | PTE_U; //vtop: 커널 가상 주소를 물리 주소로 변환.
    return pte != NULL; // boolean 반환.
}

page table entry를 할당해주고, 성공하면 true - 실패하면 false를 반환해준다.

 

/* PML4에서 가상 주소(va)에 해당하는 page table entry (VM) 주소를 반환.
 * 만약 PML4 entry에 va에 대한 페이지 테이블이 존재하지 않을 경우, 
 * create에 따라 동작이 달라짐.
 * (create가 true) 새로운 페이지 테이블이 생성되고 해당 테이블의 포인터를 반환.
 * (create가 false) null pointer 반환.*/

/* 함수의 목적: page table entry 주소를 찾고 반환해주는 역할.*/
uint64_t *pml4e_walk(uint64_t *pml4e, const uint64_t va, int create) {
    /* va는 다음과 같은 구조를 지님
     * va = [PML4 Index] [PDPT Index] [PD Index] [Offset] 
     * 여기서 단계 별로 추적하여 특정 entry를 가리킴. 
     * 해당 entry를 이용해서, 
     * 1. 가상 주소를 물리 주소로 매핑
     * 2. 페이지 속성(읽기/쓰기 권한, 유효 여부 등)을 설정/검사할 수 있음.*/
    uint64_t *pte = NULL;
    int idx = PML4(va); // va를 이용해서 PML4에 접근할 인덱스를 계산
    int allocated = 0;
    if (pml4e) {
        uint64_t *pdpe = (uint64_t *)pml4e[idx]; //계산한 va에 해당하는 index를 가지고, PDPT entry를 찾아낸다.
        if (!((uint64_t)pdpe & PTE_P)) {  // PTE_P는 해당 엔트리가 유효한 지 확인할 수 있음. 
            if (create) { 
                uint64_t *new_page = palloc_get_page(PAL_ZERO); // entry가 유효하지 않음으로 새로운 PDPT 를 생성.
                if (new_page) {
                    pml4e[idx] = vtop(new_page) | PTE_U | PTE_W | PTE_P; // 플래그 설정해주고,
                    allocated = 1;
                } else
                    return NULL;
            } else
                return NULL;
        }
        pte = pdpe_walk(ptov(PTE_ADDR(pml4e[idx])), va, create); // 확인된 or. create에 따라 새로 생성된, 
                                                                 //pdpt를 가준으로 pd index를 찾음.
    }
    if (pte == NULL && allocated) { // 새로운 PDPT를 할당한 경우, 
        palloc_free_page((void *)ptov(PTE_ADDR(pml4e[idx]))); // 할당했던 페이지를 반환하여 메모리 정리.
        pml4e[idx] = 0; // PML4 엔트리 초기화
    }
    return pte; //최종적으로 page table entry 반환.
}

pml4_set_page 에서 사용되는 함수인 pml4_walk이다. 이 함수는 pte 주소를 찾고 반환해준다.

만약 create가 true이면, PDPT가 없을 시, 새로 페이지를 생성해준다.

 

3. 결과

결과는 아직 이전과 동일하다.

 

728x90