5 min read

pintos - project3(Memory Mapped Files) 수도 코드

이번 챕터에서는 특정 파일과 연결된 페이지인 file-backed page를 할당하고 해제하는 mmap과 munmap 시스템 콜을 구현합니다.아마 앞선 챕터를 구현하면 anonymous와 file-backed의 차이점에 대해서는 알게 될것이라고 생각합니다.다만 제가 생각하기에 위 두 가지 종류의 페이지의 가장 큰 차이점 하나를 소개하고 구현으로 넘어가겠습니다.

file-backed의 경우 페이지안의 내용이 이미 디스크에 존재하는 파일의 데이터를 복제합니다.그래서 page-fault가 발생했을 때 디스크(저장 장치)에 실제로 존재하는 파일로 부터 프레임으로의 할당이 이루어집니다.

//usserprog/syscall.c
void *mmap (void *addr, size_t length, int writable, int fd, off_t offset);
  • 우선 시스템콜의 mmap함수는 인자들은 검증해주는 함수로 구현했습니다.이후 검증을 성공적으로 통과하면 do_mmap()을 호출하여 실제로 메모리 매핑을 진행합니다.
  • 첫번째 검증으로 offset의 값이 PGSIZE에 알맞게 align되어 있는지 체크합니다.
  • 이후 addr의 값이 유효한 주소인지 확인합니다.세부적으로는 해당 주소가 NULL인지,해당 주소의 시작점으로 align이 되는지 마지막으로 주소가 커널영역인지 유저영역인지를 확인합니다.
  • 그 다음으로 파일을 읽어야하는 크기인 length가 0이하인지도 체크해줍니다.
  • 또한 현재 주소를 가지고 있는 페이지가 SPT에 존재해야하기에 spt-find를 통해 유효한 페이지인지 확인합니다.
  • 마지막으로 fd값이 표준입력 또는 표준출력인지 확인하고 해당 fd를 통해 가져온 file구조체가 유효한지 검증해줍니다.
  • 이렇게 다양한 검증을 성공적으로 통과하면 드디어 do_mmap()함수를 호출하여 실제 매모리 매핑을 진행합니다.
//vm/file.c
void* do_mmap (void *addr, size_t length, int writable, struct file *file, off_t offset); 
  • 우선적으로 인자로 들어온 file을 reopen()을 통해 동일한 파일에 대해 다른 주소를 가지는 파일 구조체를 생성합니다. reopen()하는 이유는 mmap을 하는 동안 만약 외부에서 해당 파일을 close()하는 불상사를 예외처리하기 위함입니다.
  • 저희 팀의 경우 해당 함수를 구현할때 prcess.c의 load_segment()함수를 떠올렸습니다.생각해보면 load_segment() 또한 load()에서 open()한 파일을 받아서 lazy-loading을 수행하도록 페이지를 생성해줍니다.즉,사실상 거의 동일한 로직을 사용해도 된다는 것입니다.
  • 다만 차이점이 한가지 있다면 load_segment()는 페이지 이니셜라이저를 호출할때 페이지의 타입의 ANON으로 설정했고 do_mmap()은 이와 달리 FILE 타입으로 인자를 넘겨줘야한다는것입니다.
  • 구현적으로 팁을 드리자면 해당 함수는 void*형의 주소를 반환해야합니다.즉,성공적으로 페이지를 생성하면 addr을 반환합니다.하지만 addr 주소의 경우 iter를 돌면서 PGSIZE만큼 변경되기 때문에 초기의 addr을 리턴값으로 저장해두고 iter를 돌아야합니다.
//usserprog/syscall.c
void munmap (void *addr);
  • 해당 함수는 do_munmap()의 래퍼함수입니다.
  • 별다른 로직없이 그저 do_munmap() 함수를 호출해주면 됩니다.
//usserprog/syscall.c
void do_munmap (void *addr)
  • 구현을 하기 앞서 해당 함수가 어떻게 돌아가는지를 먼저 설명하겠습니다.우선 mmap()함수의 역연산을 하는 함수입니다.mmap()을 통해 저희는 파일 타입이 FILE인 UNINIT페이지를 생성했습니다.이후 page-fault가 발생하면 해당 페이지는 FILE타입의 페이지로 초기화되며 물리프레임과 연결됩니다.
  • 그러면 do_munmap()에서는 연결된 물리프레임과의 연결을 끊어주어야합니다.
  • 다만,중요한점이 FILE 타입의 페이지는 file-backed 페이지이기에 디스크에 존재하는 파일과 연결된 페이지입니다.그래서 만약 해당 페이지에 수정사항이 있을 경우 이를 감지하여 변경사항을 디스크의 파일에 써줘야합니다.
  • 코드를 구현하려면 우선 주어진 addr을 통해서 SPT로부터 page를 하나 찾습니다.
  • 이후,해당 페이지가 변경이 되어있을 경우 디스크에 존재하는 file에 write해주고 dirty-beat를 다시 0으로 변경시켜줍니다.
  • 만약 변경이 되어있지 않을 경우 해당 페이지를 pml4에서 삭제 해주고 addr을 다음 페이지 주소로 변경해줍니다.
  • 페이지를 변경하는 iter를 저희조는 가장 간단하게 while(true)를 통해 모든 페이지를 찾아주었는데,이는 논리적인 허점이 있으니 mmap()에서 페이지의 넘버링을 통해 페이지의 시퀀스를 저장한다음 해당 시퀀스를 iter의 종료조건으로 넣어주시는것도 좋은 방법입니다.(그 외 다양한 방법이 존재하는것 같습니다)
  • 아마 위의 방법은 cow를 구현할때 문제가 될수도 있습니다.

이까지 구현하시면 아마 mmap 시리즈의 테스트 케이스가 통과해야합니다.