이 섹션에서, 우리는 메모리에 매핑된 페이지를 구현할 것이다. anonymous page와 달리, memory-mapped page, 즉 file-backed mapping을 구현한다.(memory-mapped = file-backed) 페이지 내 내용은 일부 기존 파일 데이터를 미러링한다. page fault가 발생하면 물리 프레임이 즉시 할당되고 내용은 파일에서 메모리로 복사된다. 메모리 매핑된 페이지가 매핑 해제되거나 교체되면 내용 변경사항이 파일에 반영된다.
mmap
및 munmap
시스템 호출mmap
, munmap
메모리 매핑 파일에 대한 두 가지 시스템 콜을 구현한다. 우리의 가상 메모리 시스템은 반드시 페이지를 lazy하게 mmap 영역에 load해야만 하고 mmaped file 자체를 매핑에 대한 백업 저장소로 사용해야 한다. 우리는 이 두 시스템 콜을 구현하기 위해 반드시 do_mmap
과 do_munmap
을 사용해야 한다.
void *mmap (void *addr, size_t length, int writable, int fd, off_t offset);
파일 디스크립터 fd
로 오픈한 파일을 offset
byte 위치에서부터 시작해 length
바이트 크기만큼 addr
에 위치한 프로세스 가상 주소 공간에 매핑한다. 전체 파일은 addr
에서 시작해 연속적으로 이어지는 가상 페이지에 매핑된다. 만약 파일의 길이가 PGSIZE
의 배수가 아니면, 매핑된 최종 페이지의 일부 바이트가 파일 끝을 넘어 삐져나온다. page fault가 일어났을 때 이 바이트를 0으로 세팅해라. 그리고 이 페이지를 디스크에 다시 쓸 때(swap out) 버린다. 만약 매핑에 성공하면, 이 함수는 파일이 매핑된 곳의 가상 주소를 리턴한다. 실패하면, 파일을 매핑하는데 유효한 주소가 아닌 반드시 NULL을 리턴해야 한다.
mmap
을 호출하는 것은 실패할 수도 있는데, 만약 fd로 open한 파일이 0바이트 길이를 가지고 있을 경우이다. 만약 addr이 page-align되어있지 않거나 매핑된 페이지 범위가 기존에 존재하는 매핑된 페이지 집합을 어떤 경우에도 덮어씌우는 경우에는 반드시 fail로 처리해야 한다. 리눅스에서, 만약 addr
이 NULL이라면, 커널은 적절한 주소를 찾는데 이 주소는 매핑을 생성하기에 적절한 곳이다. 간단히 우리는 주어진 addr에 대해 그냥 mmap을 시도할 수 있다. 그에 따라, 만약 addr이 0이면, 이는 반드시 fail이다. 왜냐면 몇몇 pintos 코드는 가상 페이지 0을 매핑되지 않은 곳으로 간주하기 때문이다. 우리의 mmap 함수는 length가 0인 케이스에도 fail이어야 한다. 마지막으로, 콘솔 입력 및 출력을 나타내는 file descriptor는 매핑할 수 없다.
메모리 매핑 페이지도 anon page와 마찬가지로 lazy하게 할당되어야 한다. vm_alloc_page_with_initializer
또는 vm_alloc_page
를 사용해 페이지 객체를 만들 수 있다.
void munmap (void *addr);
addr
범위의 정해진 주소에 대한 메모리 매핑을 해제한다. 이addr
은 반드시 아직 매핑되지 않은 동일한 프로세스에 의한 mmap 호출로부터 반환된 가상주소여야만 한다.
모든 매핑은 exit
을 통해서나 혹은 다른 방법을 통해 내재적으로 프로세스가 exit할 때 unmapped된다. 명시적으로든 내재적으로든 매핑이 unmapped될 때, 해당 프로세스에 의해 기록된 모든 페이지는 파일에 다시 기록된다. 기록되지 않은 페이지는 기록되지 않아야 한다. 그 다음, 페이지는 프로세스의 가상 페이지 목록에서 제거된다.