Memory mapped page를 구현할 것입니다.
anonymous page와 다르게 Memory mapped page는 file page 기반입니다.
page fault가 발생하면 물리 프레임이 즉시 할당되고 content가 file에서 memory로 복사됨
memory에 mapping 된 페이지가 unmapped 또는 swap out되면 content의 모든 변경 사항은 file에 저장됨
memory에 mapping된 page도 anonymous page 처럼 lazy_load로 할당되어야함
mmap 의 목적은 file의 data를 가상메모리 주소공간에 복사를 하는 것
memory에 mapping된 file을 위한 시스템콜 MMAP, MUNMAP을 먼저 구현한다
기존 syscall handler 부분에 system call을 받는 부분을 추가해주고
case SYS_MMAP:
f->R.rax = mmap(a1, a2, a3, a4, a5);
break;
case SYS_MUNMAP:
munmap(a1);
break;
//syscall.c
void
*mmap (void *addr, size_t length, int writable, int fd, off_t offset){
if(offset % PGSIZE != 0 ) // 우리는 모든걸 PGSIZE에 맞춰서 사용하기 때문에 PGSIZE가 아닌 경우 return NULL
return NULL;
if (pg_round_down(addr) != addr || is_kernel_vaddr(addr) || addr == NULL || (long long)length <=0){
// pg_round_down(addr) != addr --> input된 addr이 page 주소가 맞는지 확인
// is_kernel_vaddr (addr) --> addr 가 kernel에 위치하고 있는지
// addr == NULL --> addr이 NULL 인지
// (long long) length <= 0 --> input 된 크기가 0 이상인지
return NULL;
}
if (fd == 0 || fd == 1){
// fd 가 0이나 1이라는 의미는 STDIN, STDOUT 이라는 의미이니깐 들어오면 안되는 애가 들어온거 --> exit로 보내버림
exit_handler(-1);
}
if(spt_find_page(&thread_current()->spt, addr)){
// addr 가 spt table에 존재하고 있는지 확인
return NULL;
}
struct file * target = find_file_using_fd(fd); // fd가 존재하는거니깐 fd에 맞는 file을 찾고
if(target == NULL)
return NULL;
void *ret = do_mmap(addr, length, writable, target, offset);
//fd로 열린 파일의 오프셋 바이트부터 length 바이트 만큼을 프로세스의 가상주소공간의 주소 addr 에 매핑 합니다
return ret;
}
file_reopen 우리는 이미 열린 파일을 사용하게 되는데 reopen 함수를 통해서 해당 파일을 독립적으로 사용할 수 있게 함
/* Do the mmap */
// fd로 열린 파일의 오프셋 바이트부터 length 바이트 만큼을 프로세스의 가상주소공간의 주소 addr 에 매핑 합니다
void *
do_mmap (void *addr, size_t length, int writable, struct file *file, off_t offset) {
struct file *mfile = file_reopen(file); // 해당 파일의 소유권을 가져와서 새 파일을 반환함 - 매핑에 대해 개별적이고 독립적인 참조를 얻음
// load_segment와 동일함 -- 하지만 load segment는 read_byte 가 input으로 들어오지만 여기선 들어오지 않음
// 그에 따라서 실제 사용할 data를 구성해줘야함
void *ori_addr = addr;
size_t read_byte = length > file_length(file) ? file_length(file) : length;
size_t zero_byte = PGSIZE - read_byte % PGSIZE;
while(read_byte > 0 || zero_byte > 0){
size_t page_read_byte = read_byte < PGSIZE ? read_byte : PGSIZE;
size_t page_zero_byte = PGSIZE - page_read_byte;
struct container *container_p = (struct container *)malloc(sizeof(struct container));
container_p->file = mfile;
container_p->offset = offset;
container_p->read_byte = page_read_byte;
if(!vm_alloc_page_with_initializer(VM_FILE, addr, writable, lazy_load_segment, container_p)){
return NULL;
}
read_byte -=page_read_byte;
zero_byte -=page_zero_byte;
addr +=PGSIZE;
offset +=page_read_byte;
}
return ori_addr;
}
file_reopen 함수를 통해서 open했기 때문에 독립적으로 사용이 가능void
munmap (void *addr){
do_munmap(addr);
}
void
do_munmap (void *addr) {
while (true){
struct page *page_ = spt_find_page(&thread_current()->spt, addr); // 해당 address에 맞는 page를 찾음
if(page_ == NULL) // page가 NULL이면 할 필요가 없으니깐
break;
struct container * aux = (struct container *) page_->uninit.aux; // page_의 aux를 형변환 한다 -- 내부 데이터를 다 지울거야
if(pml4_is_dirty(thread_current()->pml4, page_->va)){ // pml4 의 가상페이지에 page_->va 가 dirty 인 경우 (즉, page_->va 가 설치된 후 페이지가 수정된 경우 true를 반환)
file_write_at(aux->file, addr, aux->read_byte, aux->offset); // addr에 있는 정보를 aux->offset부터 read_byte만큼 aux->file에 씁니다
pml4_set_dirty(thread_current()->pml4, page_->va, 0); // pml4 의 가상페이지에 있는 page_->va의 dirty 비트를 dirty 로 설정
}
pml4_clear_page(thread_current()->pml4, page_->va); // pml4 에 존재하는 page_->va를 존재하지 않음으로 표기함 --> 추후에 접근하려고 하면 error 가 발생함
addr +=PGSIZE; // page를 지운 후 addr를 다음 page의 시작지점으로 옮김
}
}