본문 바로가기

TIL (Today I Learned)

4/2 (화) TIL - 오늘의 일지 (mmap, swap disk)

728x90

 

 

! 오늘의 결과

히히

 

 

! 오늘의 알게된 점

 

페이지와 물리메모리의 맵핑을 끊어줘야 한다는 것을 나는 page->frame에 대한 가시적인 처리를 하라는 얘기로 생각했다. 그래서 page->frame = NULL로 끊어줘보기도 했는데 계속 page->frame의 접근이 page fault가 발생했었다. 알고보니 직접적인 처리가 아니라 pml4_clear_page()를 통해 해당 페이지의 참조 비트와 물리 주소와의 관계를 clear 시키라는 얘기였다...아직 이해가 덜 되었나보다.

 

 

! 오늘의 실수 (트러블 슈팅)

 

1. reopen한 파일을 써야하는데 reopen 해놓고 기존 파일을 사용하고 있었다. 또 멍청이슈...

file.c의 do_mmap()에서는 process.c의 lode_segment와 거의 같은 흐름을 가져왔다. 

lode_segment()과 다른 점은 anonymous가 아닌 file_backed 페이지로 할당한다는 점.

if (!vm_alloc_page_with_initializer(VM_FILE, addr, writable, lazy_load_segment, nec))
        return NULL;

 

이때 보조 데이터로 사용하는 nec (우리는 necessary_info로 만들었다.)에 정보를 갱신해주는 과정에서 실수가 있었다.

 

파일을 열고 그에 대한 정보를 가져올 때, 기존의 파일을 가져다 작업을 하면 안 된다.

기존 파일은 외부에서 open된 파일이기 때문에 외부에서 닫힐 가능성이 있다. 이를 방지하기 위해 우리는 file_reopen()을 사용했다.

문제는 reopen()을 해놓고 새로 open한 파일이 아니라 기존의 파일을 사용하고 있었다...이것 때문에 mmap 테스트 케이스의 절반 가까이 통과가 되지 않았다. 왜 안 되는지 코드를 살펴보다가 reopen한 파일을 사용하지 않는 것을 발견하고 수정했다!

 

 

2. syscall.c에 있는 write()에 작성했던 버퍼의 쓰기 권한에 대한 exit()문을 삭제했다. 

if (p && !p->writable)
        exit(-1);

 

read할 버퍼의 쓰기 권한을 체크했기 때문에 write도 해줘야한다고 생각했는데 아니었다...

방금 쓰면서 깨달았다. write()에서 인자로 들어오는 buffer는 이 버퍼에 write를 하는게 아니라 버퍼에 쓰여 있는 데이터를 read만 하기 때문에 쓰기 권한을 체크할 필요가 없으며, 오히려 read-only일 때 내쫓아버리는 것이였다.

 

그렇다면 파일에 대한 쓰기 권한은 체크해야 하지 않을까? 하는 의문에 아래 코드를 추가했고, 테스트 케이스는 무사히 통과했다. 

if (write_fd->file && write_fd->file->deny_write)
        exit(-1);

 

 

 

 

3. hash_destroy() 함수를 process_exit()에 추가 & process_cleanup() 위치 변경

 

기존에는 hash table 전체를 삭제하는 코드가 없었고, process_cleanup() 을 exit 가장 마지막 순서로 두었다.

이때 mmap-exit 케이스가 통과하지 못 했다.

// 기존
file_close(t->running);

sema_up(&t->wait_sema);
sema_down(&t->exit_sema);

process_cleanup();

// 변경
file_close(t->running);
process_cleanup();
hash_destroy(&t->spt.hash_table,NULL);

sema_up(&t->wait_sema);
sema_down(&t->exit_sema);

 

hash_destroy()를 해야 전체적인 메모리 누수가 없을 것이라고 판단했고, 완전한 종료가 될 수 있다고 생각했다.

process_cleanup()에서 spt_kill에 대한 함수를 호출하며 hash에 접근하기 때문에 hash_destroy() 호출을 process_cleanup() 이후로 정했다.

 

이후 mmap-exit를 통과했다.

 

 

 

4. hash func 변경

 

우리는 swap table을 hash를 이용해서 구현했다. hash를 구현할 때는 hash_func이 두 개 필요한데, 처음에는 이전에 spt 테이블을 구현할 때 사용했던 함수를 조금만 수정해서 사용했었다.

 

  변경 전 코드, slot에 저장하는 page->va를 사용했다. 기존에도 va를 사용했기 때문에...같은 정보를 활용했다.

unsigned anon_page_hash(const struct hash_elem *p_, void *aux UNUSED)
{
    const struct slot *p = hash_entry(p_, struct slot, swap_elem);
    return hash_bytes(&p->page->va, sizeof p->page->va);
}

 

위 코드를 사용하면 vm_anon_init -> hash_insert에서 TIMEOUT이 발생한다...

 

문제

우리는 init할 때 삽입하는 slot에 page를 NULL로 초기화했다. 그러다보니 hash_func에서 사용할 page가 존재하지 않았던 것.

 

  변경 후 코드

unsigned anon_page_hash(const struct hash_elem *p_, void *aux UNUSED)
{
    const struct slot *p = hash_entry(p_, struct slot, swap_elem);
    return hash_bytes(&p->index, sizeof p->index);
}

 

함수에서 사용할 정보를 slot의 index로 변경해주어 통과했다!

각 slot의 index는 처음 할당된 이후에 변경되지 않으므로 사용하기 적절하다고 판단했다.

 

 

 

 

! 오늘 한 일

 

  • 어제 마무리하지 못한 mmap 케이스를 모두 마무리
  • swap in/out에 대한 함수 작성 - anon에 대한 함수만 작성
  • swap을 위해 FIFO와 Clock 알고리즘 작성 - 두 알고리즘 모두 통과하나 Clock 알고리즘이 눈에 띄게 빠름

많지 않아보이지만 무려 테스트 케이스 14개 -> 2개를 만들었다. mmap은 구현보다는 오류 찾기 및 수정에 가깝지만 꽤 많은 내용을 수정해야 했고, swap 시스템은 처음부터 구현했기 때문에 Disk와 섹션 등에 대한 이해부터 해야했다.

그래도 왜 섹션을 저렇게 (8개로 나누어서 등등) 사용해야 하는지 이해했다. 굿~

 

내일은 남은 file_backed에 대한 swap과 WIL 작성, 시간이 된다면 extra인 cow까지 구현해볼 예정이다. 핀토스도 거의 다 왔다 파이팅