sylee6529 / PintOS-VM

PintOS Virtual memory 구현 (교육용 OS 직접 구현)-C language
Other
2 stars 2 forks source link

이서연 #1

Open sylee6529 opened 10 months ago

sylee6529 commented 10 months ago

할 일

구현 목표

mmap 구현 (완료)

진행상황

5-6 fail (확률적으로 pass하는 테스트 때문에)

기타

sylee6529 commented 10 months ago

추가사항

sylee6529 commented 10 months ago

🚀 Trouble Shooting - About Formatting


✔️ 1차) header 파일을 살펴보자


✔️ 2차) 현재 코드 내용에서 에러가 났을 수 있다


✔️ 3차) 해결 - include header 파일의 순서가 어긋남

image

지금까지의 Project 1, 2에서는 해당 포맷팅을 동일하게 사용했지만 문제가 없었다. 하지만 이번 프로젝트에서는 순서가 민감한 부분이 있었던 것 같다.

설정 변경을 통해 header 파일 순서가 바뀌지 않게 하였고, 해결되었다 image clang을 통해 포맷을 설정했는데, 여기에 sortIncludes라는 옵션을 꺼주면 해결된다.

sylee6529 commented 10 months ago

Anonymous Page 구현 중 문제

sylee6529 commented 10 months ago

Anonymous Page 구현 중 문제

  • initdprocess_exec->vm_alloc_page_with_initializer 안에서 error 발생 -> 바로 종료 image

해결 - (나만의) 상식과 그렇지 않은 코드

원래 나의 코드(아래)는 다음과 같았다. hash_insert의 return 값이 hash_elem이니, 성공하면 값을 주고 실패하면 null을 줄 것이라 기대하고, NULL이 아닌 경우 true를 반환하고, NULL이면 false를 반환하는 코드를 짰었다. image

하지만, hash_insert 주석 중 한 문장... Inserts NEW into hash table H and returns a null pointer, if no equal element is already in the table.

성공하면 null을 반환한다. 그래서 NULL일 때 true를 반환하도록 고쳤더니 해결. 간단하게 삼항연산자를 사용할 수 있다. image

sylee6529 commented 10 months ago

vm_alloc_page_with_initializer에서 첫번째 줄 thread_current에서 바로 터지는 현상

image

sylee6529 commented 10 months ago

vm_alloc_page_with_initializer에서 첫번째 줄 thread_current에서 바로 터지는 현상

image

해결) Detail 누락 & Human error

1. load_segment 마지막 줄에 offset 업데이트 누락

ofs += page_read_bytes;

load_lazy_segment()를 보면 file_seek(file, ofs); 부분이 있는데, 여기서 ofs는 load_segment에서 설정한 aux->ofs을 가져와 그 위치부터 파일을 읽는다. 이 말은, 파일을 읽은 다음 다음 파일을 읽기 전에, 다음 세그먼트 시작 위치를 가르키는 ofs 위치를 갱신해주지 않으면, 계속 같은 위치에서 파일을 읽게 될 것이라는 이야기다.

이를 신경쓰지 않으면, 실행은 되나, 어떤 출력 결과물도 나오지 않는다. image

2. setup_stack 수정

이 경우, 생각하지 못한 시나리오 때문이라기 보다는, vm_alloc_page_with_initializer 함수의 코드를 vm_claim_page까지 하는 코드에서 spt_insert_page까지만 하는 코드로 수정했는데, 이를 호출하는 함수인 setup_stack 에는 이를 반영하지 못하여 일어난 일이었다. 물리 메모리 할당하는 코드까지 추가하면 완료. (kernel panic의 주 원인이었다) before

// SPT에 페이지 삽입 & 바로 할당
    success = vm_alloc_page(VM_MARKER_0, stack_bottom, true);
    if (!success) {
        return false;
    }

    // stack page 넣은 다음 주소에 rsp를 설정
    if_->rsp = stack_bottom;

    return success;

after

    // SPT에 페이지 삽입 & 바로 할당
    if (vm_alloc_page(VM_ANON | VM_MARKER_0, stack_bottom, true)) {
        if (vm_claim_page(stack_bottom)) {
            if_->rsp = USER_STACK;
            success = true;
        }
    }

    return success;

image

3. vm_get_frame 에 메모리 할당 필요 before

struct frame *frame = NULL;

after

struct frame *frame = (struct frame *)malloc(sizeof(struct frame));

malloc을 통해 할당받은 frame 주소 값에 있는 kva 멤버 값에 palloc_get_page로 user pool에서 할당을 받는데, 일단 frame 값을 할당받지 않으면 주소 값을 가지고 있지 않아 (frame이 가르키는 메모리 공간이 없음) 문제가 생길 수 있다.

이것도 이건데, 기존의 템플릿 코드를 유심히 보지 않고 나둬서 생긴 일이다. 이를 간과하지 않아야 한다. (..)

(코드는 아래)

struct frame *frame = (struct frame *)malloc(sizeof(struct frame));
frame->kva = palloc_get_page(PAL_USER);
//...
sylee6529 commented 10 months ago

틈새정리 - load() 함수에 대해

코드 상 하는 일은 대략적으로 다음과 같다.

sylee6529 commented 10 months ago

Test case 현황

sylee6529 commented 10 months ago

Todo

sylee6529 commented 10 months ago

Project 2 테스트케이스 해결 못하는 문제 (52 fail 정체)

sylee6529 commented 10 months ago

Project 2 테스트케이스 해결 못하는 문제 (52 fail 정체)

  • read-boundary, fork, exec, wait, sync 등 조금조금씩 안 됨 read-boundary 같은 경우에는 아래의 코드에서 find_page를 못해 false 로 반환되며 -1 exit하는 모습이 있었음...
if (not_present) {
        page = spt_find_page(spt, addr);
        if (page == NULL) {
            return false;
        }

해결

1. page fault handler에서 주소 유효성 체크 수정 [read-boundary 해결]

before

if (!user || is_kernel_vaddr(addr)) {
        return false;
    }

after

if (addr == NULL) return false;
    if (is_kernel_vaddr(addr)) return false;

2. 물리 페이지 로딩에 대한 불필요한 체크 [fork 관련 해결]

물리 페이지 로드에 필요한 vm_do_claim_page 함수에서, 조건을 많이 체크하여 로드 여부를 결정했었는데, 그렇다보니 되어야 할 케이스까지 false가 반환이 되었던 것 같다. 받으면 무조건 물리 메모리에 로드하도록 느슨하게 바꾸니 해결되었다.

before

/* TODO: Insert page table entry to map page's VA to frame's PA. */
    bool set_page = false;
    if (!pml4_get_page(thread_current()->pml4,
                       page->va)) {  // NULL이어야 기존것이 아님.
        set_page = pml4_set_page(thread_current()->pml4, page->va, frame->kva,
                                 page->writable);
        if (set_page) {
            return swap_in(page, frame->kva);
        }
    }
    return false;

after

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

    /* TODO: Insert page table entry to map page's VA to frame's PA. */
    // 가상 주소와 물리 주소를 매핑
    struct thread *current = thread_current();
    pml4_set_page(current->pml4, page->va, frame->kva, page->writable);

    return swap_in(page, frame->kva);

3. lazy_load_segment에서 free 제거

project2 테스트케이스에서 fork-read만 안 되는 현상이 있었다. 열심히 보니 vm.c에서는 문제가 없어보였다. 근데 이전에 문제를 해결할 때도 그랬지만, 코드 수행을 위한 체크를 너무 빡빡하게 수정했던 것이 되려 걸림돌이 되었던 경우가 많아서 다시 보는데, lazy_load_segment에서 메모리 할당 해제를 위해 일부러 코드를 추가했는데, 팀원분이 이 부분 free를 해주면 오히려 테스트 통과가 되지 않더라 라는 이야기를 해주셔서 반영해보니 해결되었다.

aux 부분이 fork할 때 자식이 참조하는 정보가 날라가서 안 될 수도 있다(?) 라는 가설이 가장 설득력있어 보인다. image

sylee6529 commented 10 months ago

테스트케이스 현황

sylee6529 commented 10 months ago

stack growth 구현 중 궁금점 - rsp에는 어떤 값을 넣을까?

A. 주소를 빼기 전 값을 넣어야 한다. 왜냐하면 rsp 값으로부터 PGSIZE 만큼 낮은 주소로 확장해 나가는데, 주소를 뺀 후의 값을 rsp를 넣으면 낮은 주소로 확장해 나갈 때, 접근할 수 없는 영역이게 되기 때문이다.

높은 주소가 위, 낮은 주소가 아래일 때, 스택은 아래로 성장하는 상황이라 할 때, rsp는 '천장'에 있게 되는 셈이다.

sylee6529 commented 10 months ago

Todo: 아래의 특수 테스트 케이스를 해결하기

FAIL tests/vm/pt-write-code2 FAIL tests/vm/pt-grow-stk-sc

sylee6529 commented 10 months ago

Todo: 아래의 특수 테스트 케이스를 해결하기

FAIL tests/vm/pt-write-code2 FAIL tests/vm/pt-grow-stk-sc

해결 - kernel 모드에서 writable 여부를 검사해야 한다

slack 질답 채널에 올라온 질문으로 알게 된 사실인데, pintos-kaist 설계 상, CR0 레지스터의 WP bit가 세팅되어 있지 않기 때문에 writable이 false인 file도 read할 때 page fault가 발생하지 않고 그냥 쓰게 되어 있다

여기서 중요한 것은, "read를 하는데 왜 writable을 체크하는가"에 대한 것인데, read가 fd를 가져와 buffer에 '쓰는' 것이기 때문이다. 이 함수 안에서 file_read 함수가 쓰이는데, 그것의 주석은 다음과 같다.

SIZE 바이트만큼 FILE에서부터 BUFFER로 읽어들인다. ...

read는 FILE->BUFFER로 바이트를 읽고, wirte는 BUFFER->FILE로 바이트를 쓴다. 전자는 읽는다고 표현되어 있지만, 'buffer에 쓰는 작업'이라고 생각하는게 맞는 것 같다. buffer에 쓴다는건 메모리에 쓴다는 것이니, 현재 buffer에 쓰지 못하게 되어 있다면 못하게 하는 것 = read-only 페이지를 read하지 못하게 하는 것과 동일하다라고 받아들였다.

image 이렇게 추가하면 write-2 테스트케이스는 통과. (물론 pt-grow-stk-sc 테스트 케이스를 위해 함수를 바꿔야 해서 저렇게 추가한 코드는 수정이 필요할 듯)

해결 - size가 엄청 큰 buffer의 가능성을 고려하자 [pt-grow-stk-sc 해결]

주소의 유효성을 검증하면 보통, 시작 주소에 해당하는 page가 있는지 찾는데, 생각해보면 size가 엄청 큰 buffer인 경우 여러 페이지를 갖고 있을 수 있다. 그래서 PGSIZE만큼 addr을 빼면서 순회하고, page를 찾는 로직을 추가한다.

for (void *addr = end_addr; addr >= start_addr; addr -= PGSIZE) {
        struct page *pg = check_address(addr);
        if (pg == NULL) {
            exit(-1);
        }

        if (to_write == true && pg->writable == false) {
            exit(-1);
        }
    }
sylee6529 commented 10 months ago

테스트케이스 현황

image

팀원분과 2개정도 차이가 나지만, 나중에 구현할 것에 관한 테스트여서 신경쓰지 않고 진행하기로 했다. 다음은 mmap 구현 가보자고

sylee6529 commented 10 months ago

7 fail의 문턱 - mmap-exit이 Fail 되는 현상

해결 - destroy 함수 코드가 이상하다

image (참고로 syn-read 문제가 해결되지 않아 6 fail도 가능하다)