1220 - TIL(Memory Management 구현)

그로밋·2023년 12월 20일
0

krafton jungle

목록 보기
49/58

Task list

Memory Management

  • 목표: 물리 메모리 로드 대신, supplement page table으로 메모리를 관리한다
  • 구현
    • Page Fault Handler 수정: kill 하지말고 spt에서 page를 찾고, 있으면 pte를 추가
    • struct thread, page, supplement_page_table, frame에 필요한 멤버 추가 가능
    • spt 관련 기본 함수 구현 (init, copy, kill 3가지)
    • 종료 시 SPT를 참조하여 어떤 리소스를 해제할 지 결정
    • spt 관련 page 연산 함수 구현 (find, insert, remove)
    • frame 관련 함수 구현 (get, claim, do_claim)
    • Free Frame이 없으면(swap slot이 가득차면), victim page 선택(eviction policy 구현)

Anonymous Page

  • 목표: Page Type에 따라 알맞게 처리하고, lazy loading을 구현한다.
  • 구현
    • page type에 따른 initializer 호출
    • struct anon_page에 필요한 멤버 추가 가능
    • 처음은 무조건 uninit 페이지로 생성, 페이지 구조만 할당
    • lazy loading을 위해 load_segment while문 로직, setup_stack 로직 수정
    • Page Fault Handler에서 Valid한 Fault인지 체크
      [여기까지 하면 fork 제외 Project2 테케 모두 통과]
    • fork를 고려한 SPT copy, kill 함수 구현
    • uninit_destroy, anon_destroy 구현
      [ 여기까지 하면 Project2 테케 모두 통과 ]

Stack Growth

  • 목표: 스택이 현재 크기보다 커지면(Stack Access) 추가 페이지를 할당한다.
  • 구현
    • 스택 포인터의 잘못된 access를 어디에서 체크할건지 고민
    • page fault handler에서 vm_stack_groth 호출, 구현 (최대 1MB 제한)

Memory Mapped Files

  • 목표: File-backed Page를 구현한다.
  • 구현
    • lazy하게 do_mmap, do_munmap 구현
    • exit될 때 매핑 해제
    • file_init, initializer, destroy 구현

Swap In/Out

  • 목표: 메모리 할당량이 가득 차면, 프레임을 디스크로 교체한다.
  • 구현
    • Anon일 경우 anon_init, initializer 수정
      swap_in, swap_out 구현(out을 먼저)
    • file-backed인 경우, 매핑된 파일에 다시 기록됨 init, initializer 수정
      backed_swap_in, backed_swap_out 구현

Memory Management

현재 상태는 페이지 테이블만 있음(pml4).

1. Implementing Supplemental Page Table

supplemental page table 구조체가 비어있다. 어떠한 정보가 필요한가?

페이지 폴트와 자원 관리를 처리해야함. 그럼 hash table이 일단 필요하겠다. hash 구조체 구경하기

  • Hash 구조체를 supplemental_page_table 구조체 안에 추가
<vm.h>
struct supplemental_page_table { 
	struct hash spt_hash;
};
  • hash.h 에 주석을 보면 Instead, each structure that can potentially be in a hash must embed a struct hash_elem member. 이라고 되어있어서 page에 hash_elem 구조체 추가함
<vm.h>
struct page { 
    '''
    struct hash_elem hash_elem;
    '''
};

그리고 세가지 function을 손봐야한다.

1.1 supplemental_page_table_init()

  • void supplemental_page_table_init (struct supplemental_page_table *spt);
    • spt를 initialize 하기 위해 hash_init이라는 함수를 사용하고 싶은데 인자로 hash_hash_func라는 hash 값을 구해주는 함수포인터와 hash_less_func라는 해쉬 엘리먼트들의 크기를 비교해주는 것들을 넘겨야한다.
      bool hash_init (struct hash *h, hash_hash_func *hash, hash_less_func *less, void *aux)
      그렇다면 hash_hash_func와 hash_less_func를 참고해서 page_hash, page_less함수를 만들자.
      만들었으니 hash_init함수를 사용해서 한줄로 초기화를 해버리자.
    •   <vm.c>
      /* Computes and returns the hash value for hash element E, */
      unsigned
      page_hash (const struct hash_elem *he, void *aux UNUSED) {
      	const struct page *p = hash_entry(he, struct page, hash_elem);
      	// hash_bytes: returns a hash of the size(sec arg) bytes in BUF(first arg) 
      	return hash_bytes (&p->va, sizeof(p->va));
      }
      /* Returns true if A is less than B, or false if A is greater than or equal to B */
      bool
      page_less (const struct hash_elem *a, const struct hash_elem *b, void *aux UNUSED) {
      	const struct page *pa = hash_entry(a, struct page, hash_elem);
      	const struct page *pb = hash_entry(b, struct page, hash_elem);
      	return pa->va < pb->va;
      }
      /* Initialize new supplemental page table */
      void
      supplemental_page_table_init (struct supplemental_page_table *spt UNUSED) {
      	hash_init(spt, page_hash, page_less, NULL);
      }

1.2 spt_find_page()

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

    • hash_find() 함수 사용하자. 주석 설명
      Finds and returns an element equal to E in hash table H, or a
      null pointer if no equal element exists in the table.
      그렇다면 spt_find_page에서 인자로 받아오는 spt가 hash이니까 첫번째 매개변수로 넘기고, va를 아니까 page 할당받아서 va넣고 그 페이지의 hash_elem 을 넘기자.

      /* Find VA from spt and return page. On error, return NULL. */
      struct page *
      spt_find_page (struct supplemental_page_table *spt UNUSED, void *va UNUSED) {
      	struct page *page = NULL;
      	
      	page = palloc_get_page(PAL_USER);
      	page->va = va;
      	
      	struct hash_elem *e;
      	e = hash_find(&spt, &page->hash_elem);
      
      	return e!=NULL ? hash_entry(e, struct page, hash_elem):NULL;
      }

1.3 spt_insert_page()

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

    • hash_insert(&spt, &page->hash_elem) 이렇게 쓰면 될것같은데 존재하지 않을 경우에만이라는 조건을 어떻게 넣을 수 있을까.
      hash_insert 함수의 주석을 보고 아이디어를 얻었다.
      Inserts NEW into hash table H and returns a null pointer, if no equal element is already in the table. If an equal element is already in the table, returns it without inserting NEW.
      그럼 없던 데이터를 넣을 땐 null pointer를 반환하니까 함수 값이 널일 때만 실행 되게 하면 되겠다.

      /* Insert PAGE into spt with validation. */
      bool spt_insert_page(struct supplemental_page_table *spt UNUSED,
                         struct page *page UNUSED) {
        int succ = false;
      
        return hash_insert(&spt, &page->hash_elem) == NULL ? true : false;
      }

2. Frame Management

Implement vm_get_frame, vm_claim_page and vm_do_claim_page in vm/vm.c.

2.1 vm_get_frame

/* Gets a new physical page from the user pool by calling palloc_get_page.
 * When successfully got a page from the user pool, also allocates a frame,
 * initialize its members, and returns it.
 * 사용자 풀에서 페이지를 성공적으로 가져오면, 프레임을 할당하고 해당 프레임의
 * 멤버를 초기화한 후 반환한다. 페이지 할당을 실패할 경우, PANIC ("todo")로
 * 표시한다. (swap out을 구현한 이후 변경한다.)
 *
 * palloc() and get frame. If there is no available page, evict the page
 * and return it. This always return valid address. That is, if the user pool
 * memory is full, this function evicts the frame to get the available memory
 * space.*/
static struct frame *vm_get_frame(void) {
    struct frame *frame = NULL;
    /* TODO: Fill this function. */
    void *kva = palloc_get_page(PAL_USER);

    if (kva == NULL) {
        PANIC("todo");
    }
    // 프레임을 할당하고 해당 프레임의 멤버를 초기화한 후 반환한다
    frame = malloc(size_of(struct frame));
    frame->kva = kva;

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

2.2 vm_do_claim_page

git book 설명
Claims, meaning allocate a physical frame, a page. You first get a frame by calling vm_get_frame (which is already done for you in the template). Then, you need to set up the MMU. In other words, add the mapping from the virtual address to the physical address in the page table. The return value should indicate whether the operation was successful or not.

프레임을 vm_get_frame으로 얻으라고 해서 얻고 frame이랑 page랑 밑에 함수에서 엮어주길래 나도 엮어주고 까지는 했는데 그 다음에서 add the mapping from the virtual address to the physical address in the page table
그니까 page table에서 가상주소에서 물리주소로 맵핑해줘라인데 page table에 어떻게 해줘야할지 난감했다.

page table 관련 함수가 threads/mmu.c에 있어서 함수를 뒤지다가 pml4_set_page() 함수의 주석을 보고 이놈으로 결정했다. 주석은 이렇다.

Adds a mapping in page map level 4 PML4 from user virtual page UPAGE to the physical frame identified by kernel virtual address KPAGE.

근데 pml4가 어디에 있느냐 말이다. 아 thread 구조체 안에 있다.

/* Claim the PAGE and set up the mmu. */
static bool vm_do_claim_page(struct page *page) {
    struct frame *frame = vm_get_frame();

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

    // add the mapping from the virtual address to the physical address in the
    // page table (pml4_set_page())
    struct thread *curr = current_thread();
    pml4_set_page(curr->pml4, page->va, frame->kva, page->writable);

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

2.3 vm_claim_page

Claims the page to allocate va. You will first need to get a page and then calls vm_do_claim_page with the page.

/* Claim the page that allocate on VA. */
bool vm_claim_page(void *va UNUSED) {
    struct page *page = NULL;
    // spt에서 va에 해당하는 page 찾기
    page = spt_find_page(&thread_current()->spt, va);
    return vm_do_claim_page(page);
}
profile
Work as though your strength were limitless. <S. Bernhardt>

0개의 댓글