1221-TIL(pintos project 3 - Anon 구현)

그로밋·2023년 12월 26일
0

krafton jungle

목록 보기
50/58
  • 1221 목요일
  • memory management 빠진 부분 확인
  • 빠진부분 작성
  • anon page 구현

memory management missing part

<vm.c?>
/* Return true on success */
bool vm_try_handle_fault(struct intr_frame *f UNUSED, void *addr UNUSED,
                         bool user UNUSED, bool write UNUSED,
                         bool not_present UNUSED) {
    struct supplemental_page_table *spt UNUSED = &thread_current()->spt;
    if (spt == NULL) return false;

    return vm_claim_page(addr);
}

이렇게 vm_claim_page(addr); 하면 해당 함수에서 spt에서 va에 해당하는 page 찾아서 return vm_do_claim_page(page); 이렇게 vm_do_claim_page함수를 호출한다.


Anonymous page

Anonymous page 구현 들어가기 전에 page 초기화 흐름을 살펴보자


출처: 링크텍스트

anonymous page 시작해보자.

team todo list on 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 테케 모두 통과 ]

1) vm_alloc_page_with_initializer

bool
vm_alloc_page_with_initializer (enum vm_type type, void *upage, bool writable,
		vm_initializer *init, void *aux) {

	ASSERT (VM_TYPE(type) != VM_UNINIT)

	struct supplemental_page_table *spt = &thread_current ()->spt;

	/* Check wheter the upage is already occupied or not. */
	if (spt_find_page (spt, upage) == NULL) {
		struct page *p = (struct page *)malloc(sizeof(struct page));
		bool (*page_initializer)(struct page *, enum vm_type, void *);
		switch (VM_TYPE(type))
		{
		case VM_ANON:
			page_initializer = anon_initializer;
			break;
		case VM_FILE:
			page_initializer = file_backed_initializer;
			break;
	}
	uninit_new(p, upage, init, type, aux, page_initializer);
	p->writable=writable; // 순서 중요. 필드 수정을 uninit_new 이후에 해야함.
	return spt_insert_page(spt, p);
err:
	return false;
}

2) load_segment

When is this function called?

inside of the load function(fuc loads the executable file to the current thread) when the process is executed.

what does this function do?

  • loads the file's content to the upage.
  • Be awere that this function shouldn't load a file's content directly, but hand over the ingredients for the actual loading such as function and arguments to "lazy_load_segment()"

struct lazy_load_arg

Need a structure that includes the information for the loading to write load_segment.

"include/vm/file.h"
struct lazy_load_arg {
	struct file* file;
	uint32_t read_bytes;
	uint32_t zero_bytes;
	off_t ofs;
	void *open_addr;
	void *close_addr;
};
"userprog/process.c"
static bool load_segment(struct file *file, off_t ofs, uint8_t *upage,
                         uint32_t read_bytes, uint32_t zero_bytes,
                         bool writable) {
    ASSERT((read_bytes + zero_bytes) % PGSIZE == 0);
    ASSERT(pg_ofs(upage) == 0);
    ASSERT(ofs % PGSIZE == 0);

    while (read_bytes > 0 || zero_bytes > 0) {
        /* Do calculate how to fill this page.
         * We will read PAGE_READ_BYTES bytes from FILE
         * and zero the final PAGE_ZERO_BYTES bytes. */
        size_t page_read_bytes = read_bytes < PGSIZE ? read_bytes : PGSIZE;
        size_t page_zero_bytes = PGSIZE - page_read_bytes;

        struct lazy_load_arg *lazy_load_arg =
            (struct lazy_load_arg *)malloc(sizeof(struct lazy_load_arg));
		lazy_load_arg->file = file;
		lazy_load_arg->ofs = ofs; 					 // location will start to read
		lazy_load_arg->read_bytes = page_read_bytes; // bytes the page need to read
		lazy_load_arg->zero_bytes = page_zero_bytes; // bytes need to be filled with 0
        void *aux = NULL;
        if (!vm_alloc_page_with_initializer(VM_ANON, upage, writable,
                                            lazy_load_segment, aux))
            return false;

        /* Advance. update for the repeat */
        read_bytes -= page_read_bytes;
        zero_bytes -= page_zero_bytes;
        upage += PGSIZE;
		ofs += page_read_bytes;
    }
    return true;
}

3) lazy_load_segment

When is this function called?

When the first page fault is called

what does this function do?

  • This function is responsible for loading, practically.
  • As the physical frame mapping is done before this function is called, just need to do loading the content to the physical frame.
  • The Argument aux is the info that is set on the lazy_load_arg, which is the information that needs to find the file
static bool lazy_load_segment(struct page *page, void *aux) {
    void *kva_ = page->frame->kva;

    struct lazy_load_arg *lla = (struct lazy_load_arg *)aux;
    // set ofs as file's position
    file_seek(lla->file, lla->ofs);
    // reading(loading) the content to the physical frame
    if (file_read(lla->file, kva_, lla->read_bytes) != (int)(lla->read_bytes)) {
        palloc_free_page(kva_);
        return false;
    }
    memset(kva_ + lla->read_bytes, 0, lla->zero_bytes);
    return true;
}

4) setup_stack

When is this function called?

Inside of load function when the process is running

what does this function do?

Creates a page of stack at the point downwards as PGSIZE from USER_STACK where the stack's start point.

Note that the lazy loading is not necessary for the first stack page. Becuase when the process is executed, this address is accessed right away after this function is called in order to add commmand line args to the stack.

Therefore, we don't have to wait for the fault, but get a physical frame right away.

When a page is allocated, suppliment marker can be used to indicate the page is stack page by using VM_MARKER_0 with the type.

Lastly, change the rsp to USER_STACK so that the argument_stack function can push the args from the rsp.

"userprog/process.c"
static bool setup_stack(struct intr_frame *if_) {
    bool success = false;
    void *stack_bottom = (void *)(((uint8_t *)USER_STACK) - PGSIZE);

    /* TODO: Map the stack on stack_bottom and claim the page immediately.
     * TODO: If success, set the rsp accordingly.
     * TODO: You should mark the page is stack. */
    /* TODO: Your code goes here */
    if(vm_alloc_page(VM_ANON|VM_MARKER_0, stack_bottom, 1)) {
        // map the physical frame to the allocated page.
        success = vm_claim_page(stack_bottom);
        if (success) {
            if_->rsp = USER_STACK;
        }
    }
    return success;
}

5) vm_try_handle_fault

When is this function called?

what does this function do?

when the page fault is occured, this function gets the control.

이 함수에서 할당된 물리 프레임이 존재하지 않아서 발생한 예외일 경우에는 매개변수인 not_present에 true를 전달받는다.
그럴 경우, SPT에서 해당 주소에 해당하는 페이지가 있는지 확인해서 존재한다면 해당 페이지에 물리 프레임 할당을 요청하는 vm_do_claim_page 함수를 호출한다.
이 함수에서는 위에서 설정한대로 각 페이지에 맞는 초기화 함수가 호출된다.
not_present 값이 false인 상황을 살펴보면,
물리 프레임이 할당되어 있지만 page fault가 일어난 것이므로 그 경우는 read-only page에 write를 한 경우가 된다. 따라서 not_present가 false인 경우는 예외로 처리하면 된다. (return false)
not_present가 true인 경우에도, read-only page에 write 요청을 할 경우가 생길 수 있으므로 이에 대한 예외 처리를 추가한다.

    /* Return true on success */
    bool vm_try_handle_fault(struct intr_frame * f UNUSED, void *addr UNUSED,
                             bool user UNUSED, bool write UNUSED,
                             bool not_present UNUSED) {
        struct supplemental_page_table *spt UNUSED = &thread_current()->spt;

        if (addr == NULL | is_kernel_vaddr(addr)) return false;
        // if the physical page doesn't exit
        if (not_present) {
            page = spt_find_page(spt, addr);
            if (page == NULL) return false;
            if (write == 1 &&
                page->writable ==
                    0) {  // if it's asking to write in unwritable page
                return false;
            }
            return vm_do_claim_page(page);
        }
        return false;
    }
profile
Work as though your strength were limitless. <S. Bernhardt>

0개의 댓글