운영체제(OS)/with PintOS

(Project3-Virtual Memory) - Memory Management

스탠딩 2023. 12. 26. 21:22

git book 가이드와 코드 구현

보충 페이지 테이블 구현

이 시점에서 Pintos에는 가상 및 물리 메모리 매핑을 관리하기 위한 페이지 테이블 (pml4)이 있습니다. 그러나 이것만으로는 충분하지 않습니다. 이전 섹션에서 논의된 대로 페이지 폴트 및 리소스 관리를 처리하기 위해 각 페이지에 대한 추가 정보를 보유할 보충 페이지 테이블도 필요합니다. 따라서 프로젝트 3의 첫 번째 작업으로 보충 페이지 테이블을 위한 몇 가지 기본 기능을 구현하는 것이 좋습니다.

vm/vm.c에 보충 페이지 테이블 관리 함수를 구현하세요.

먼저 Pintos에서 어떻게 보충 페이지 테이블을 설계할지를 결정해야 합니다. 자체 보충 페이지 테이블을 설계한 후에 아래 세 가지 함수를 해당 설계에 따라 구현하세요.

 

void supplemental_page_table_init (struct supplemental_page_table *spt);

 보충 페이지 테이블을 초기화합니다. 보충 페이지 테이블에 사용할 데이터 구조를 선택할 수 있습니다. 이 함수는 새로운 프로세스가 시작될 때(initd in userprog/process.c)와 프로세스가 복제될 때(__do_fork in userprog/process.c) 호출됩니다.

struct supplemental_page_table
{
    struct hash spt; // pintos에서 제공하는 hash 자료형을 이용하여 spt 생성
};
static uint64_t spt_hash_func(const struct hash_elem *e, void *aux)
{
  const struct page *pg = hash_entry(e, struct page, page_elem); //해시요소가 들어있는 페이지 추출
  return hash_int(pg->va); // 페이지의 가상주소를 해싱하여 반환
}

static bool spt_less_func(const struct hash_elem *a, const struct hash_elem *b)
{
  const struct page *pg_a = hash_entry(a, struct page, page_elem); //해시요소가 들어있는 페이지 추출
  const struct page *pg_b = hash_entry(b, struct page, page_elem);
  return pg_a->va < pg_b->va;
}
void supplemental_page_table_init(struct supplemental_page_table *spt)
{
  hash_init(&(spt->spt), spt_hash_func, spt_less_func, NULL); // hash_init 함수를 이용하여 spt를 초기화
}

 

 

struct page *spt_find_page (struct supplemental_page_table *spt, void *va);

주어진 보충 페이지 테이블에서 va에 해당하는 struct page를 찾습니다. 실패할 경우 NULL을 반환합니다.

struct page *spt_find_page(struct supplemental_page_table *spt, void *va)
{
  struct page *page = NULL;
  /* TODO: Fill this function. */
  void *page_addr = pg_round_down(va); // va로 페이지 시작주소 추출

  struct page pg;
  pg.va = page_addr;
  struct hash_elem *found = hash_find(&(spt->spt), &(pg.page_elem)); // 해당 페이지 번호가 spt에 있다면
  if (found == NULL)
    return NULL;
  page = hash_entry(found, struct page, page_elem); // 실제 페이지 추출

  return page;
}

 

 

bool spt_insert_page (struct supplemental_page_table *spt, struct page *page);

주어진 보충 페이지 테이블에 struct page를 삽입합니다. 이 함수는 가상 주소가 주어진 보충 페이지 테이블에 이미 존재하는지 확인해야 합니다.

bool spt_insert_page(struct supplemental_page_table *spt, struct page *page)
{
  int succ = false;
  /* TODO: Fill this function. */
  if (hash_insert(&(spt->spt), &(page->page_elem)) == NULL) // spt에 page가 있는지 확인 후 없으면 삽입
    succ = true;

  return succ;
}

 

프레임 관리 

 

이제 모든 페이지는 단순히 생성 당시의 메모리에 대한 메타데이터를 보유하는 것이 아닙니다. 따라서 물리 메모리를 관리하기 위한 다른 방식이 필요합니다. include/vm/vm.h에는 물리 메모리를 나타내는 struct frame이 존재합니다. 현재 이 구조체는 다음과 같이 생겼습니다:

/* The representation of "frame" */
struct frame {
  void *kva;
  struct page *page;
};

이것은 오직 두 개의 필드만을 가지고 있습니다. kva는 커널 가상 주소이고, page는 페이지 구조체입니다. 프레임 관리 인터페이스를 구현할 때 더 많은 멤버를 추가하는 것이 허용됩니다.

vm/vm.c에 vm_get_frame, vm_claim_page 및 vm_do_claim_page를 구현하세요.

 

static struct frame *vm_get_frame (void);

palloc_get_page를 호출하여 사용자 풀에서 새로운 물리 페이지를 가져옵니다. 사용자 풀에서 페이지를 성공적으로 가져오면 프레임을 할당하고 그 멤버를 초기화한 후에 해당 프레임을 반환합니다. vm_get_frame을 구현한 후에는 모든 사용자 영역 페이지 (PALLOC_USER)를 이 함수를 통해 할당해야 합니다. 현재는 페이지 할당 실패 시 스왑 아웃을 처리할 필요가 없습니다. 단순히 해당 경우에 PANIC("todo")로 표시하세요.

static struct frame *vm_get_frame(void)
{
  struct frame *frame = NULL;
  /* TODO: Fill this function. */
  void *pg_ptr = palloc_get_page(PAL_USER); // 유저 풀에서 페이지를 할당받음
  if (pg_ptr == NULL)
    return PANIC // 페이지 할당 실패 시(이후에 수정해야 됨)

  frame = (struct frame *)malloc(sizeof(struct frame)); // 프레임 구조체 생성
  frame->kva = pg_ptr; // 멤버 초기화
  frame->page = NULL;

  ASSERT(frame != NULL);
  ASSERT(frame->page == NULL);
  return frame;
}

 

 

bool vm_do_claim_page (struct page *page);

클레임, 즉 물리 프레임, 페이지를 할당합니다. 먼저 vm_get_frame을 호출하여 프레임을 가져와야 합니다(이미 템플릿에서 완료되었습니다). 그런 다음 MMU를 설정해야 합니다. 다시 말해, 가상 주소에서 물리 주소로의 매핑을 페이지 테이블에 추가해야 합니다. 반환 값은 작업이 성공했는지 여부를 나타내어야 합니다.

static bool vm_do_claim_page(struct page *page)
{
  struct frame *frame = vm_get_frame();

  if (frame == NULL)
    return false;

  /* Set links */
  frame->page = page;
  page->frame = frame;

  /* TODO: Insert page table entry to map page's VA to frame's PA. */
  struct thread *cur_thread = thread_current();
  lock_acquire(&lru_lock); // 동기화를 위해 lock 설정
  list_push_back(&lru, &(frame->lru_elem)); // 프레임을 관리하는 리스트에 push
  lock_release(&lru_lock);

  if (pml4_set_page(cur_thread->pml4, page->va, frame->kva, page->writable) == false)
    return false; // 페이지 주소와 프레임 주소를 페이지 테이블에 매핑

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

 

 

bool vm_claim_page (void *va);

va에 할당할 페이지를 클레임합니다. 먼저 페이지를 가져와야 하고, 그런 다음 해당 페이지와 함께 vm_do_claim_page를 호출해야 합니다.

bool vm_claim_page(void *va)
{
  struct page *page = NULL;
  /* TODO: Fill this function */
  page = spt_find_page(&thread_current()->spt, va); // spt에서 va에 해당하는 페이지를 찾아보고
  if (page == NULL)
    return false;

  return vm_do_claim_page(page); // 페이지 요구 함수 호출
}