스택그로스는 이 레퍼런스를 참고해서 구현했더니 바로 관련 테스트케이스가 통과했다.
다만 조건문중에 아주 복잡한 부분이 있어서 짚고 넘어갔다.
스택 확장으로 처리할 수 있는 폴트인 경우를 표현하기 위해 (USER_STACK - (1 << 20) <= rsp - 8 && rsp - 8 == addr && addr <= USER_STACK)
이렇게 된 모습이다.
연산자 우선순위에 의해서
1 << 20
: 비트 왼쪽 시프트 연산은 가장 높은 우선순위를 갖는데, 이는 1을 20만큼 왼쪽으로 비트 이동시키는 연산입니다.USER_STACK - (1 << 20)
: 뺄셈 연산은 비트 시프트 연산보다 우선순위가 낮습니다. 따라서 USER_STACK에서 (1 << 20)을 뺀 값이 계산됩니다.rsp - 8
: 뺄셈 연산은 비트 시프트 연산보다 우선순위가 낮기 때문에, rsp에서 8을 뺀 값이 계산됩니다.addr == rsp - 8
: 비교 연산은 뺄셈보다 우선순위가 높으므로, rsp - 8과 addr이 같은지 비교합니다.USER_STACK - (1 << 20) <= rsp - 8
: 이전 단계에서 계산된 값과 USER_STACK - (1 << 20)을 비교합니다.addr <= USER_STACK
: 비교 연산은 뺄셈보다 우선순위가 높으므로, addr과 USER_STACK을 비교합니다.AND (&&)
연산자를 사용하여 앞선 모든 비교를 연결합니다.현재 전체 테스트케이스 결과 모습
아래의 특수 테스트 케이스를 해결해야 한다.
FAIL tests/vm/pt-write-code2
FAIL tests/vm/pt-grow-stk-sc
이건 동료 document 의 도움을 받아 해결했다.
현재까지 상황 27 of 141 tests failed.
page-merge-seq
page-merge-par
page-merge-stk
이 세개 fail 떴었다가 다시 돌리니까 두개 fail 뜬다...
왜이럴까? 일단 제쳐두고 mmap하자.
void *mmap(void *addr, size_t length, int writable, int fd, off_t offset) {
if (!addr || addr != pg_round_down(addr)) return NULL; // addr 없는 경우
if (offset != pg_round_down(offset))
return NULL; // offset이 page aligned 되지 않은 경우
if (!is_user_vaddr(addr) || !is_user_vaddr(addr + length))
return NULL; // addr이 user 영역이 아닌 경우
if (spt_find_page(&thread_current()->spt, addr))
return NULL; // spt에 있는 경우
struct file *f = process_get_file(fd);
if (f == NULL) return NULL; // file이 없는 경우
if (file_length(f) == 0 || (int)length <= 0)
return NULL; // file의 길이가 0 혹은 음수인 경우
return do_mmap(addr, length, writable, f,
offset); // 파일이 매핑된 가상 주소 반환
}
매핑을 진행할 때는 간단하게 (페이지 주소 addr + PGSIZE에 위치하는) 다음 페이지에 이어서 매핑하면 되지만, 매핑을 해제할 때는 몇 번째 페이지까지 해제해야 하는지를 알아야 한다. 따라서 총 몇 페이지를 사용해서 매핑이 이루어졌는지에 대한 정보도 page 구조체에 함께 저장해 둔다. 출처
void *do_mmap(void *addr, size_t length, int writable, struct file *file,
off_t offset) {
struct file *f = file_reopen(file);
// this is for returning mapped virtual address when the mapping is suceeded
void *start_addr = addr;
// Total number of page for mapping
int total_page_count = length <= PGSIZE ? 1
: length % PGSIZE ? length / PGSIZE + 1
: length / PGSIZE;
size_t read_bytes = file_length(f) < length ? file_length(f) : length;
size_t zero_bytes = PGSIZE - read_bytes % PGSIZE;
ASSERT((read_bytes + zero_bytes) % PGSIZE == 0);
ASSERT(pg_ofs(addr) == 0); // page alignment for the upage
ASSERT(offset % PGSIZE == 0); // page alignment for the ofs
while (read_bytes > 0 || zero_bytes > 0) {
/* 이 페이지를 채우는 방법을 계산합니다.
파일에서 PAGE_READ_BYTES 바이트를 읽고
최종 PAGE_ZERO_BYTES 바이트를 0으로 채웁니다. */
size_t page_read_bytes = read_bytes < PGSIZE ? read_bytes : PGSIZE;
size_t page_zero_bytes = PGSIZE - page_read_bytes;
struct lazy_load_arg *lazy_load_arg =
(struct lazy_load_arg *)malloc(sizeof(struct lazy_load_arg));
lazy_load_arg->file = f;
lazy_load_arg->ofs = offset;
lazy_load_arg->read_bytes = page_read_bytes;
lazy_load_arg->zero_bytes = page_zero_bytes;
// vm_alloc_page_with_initializer를 호출하여 대기 중인 객체를
// 생성합니다.
if (!vm_alloc_page_with_initializer(VM_FILE, addr, writable,
lazy_load_segment, lazy_load_arg))
return NULL;
struct page *p = spt_find_page(&thread_current()->spt, start_addr);
p->mapped_page_count = total_page_count;
/* Advance. */
// 읽은 바이트와 0으로 채운 바이트를 추적하고 가상 주소를 증가시킵니다.
read_bytes -= page_read_bytes;
zero_bytes -= page_zero_bytes;
addr += PGSIZE;
offset += page_read_bytes;
}
return start_addr;
}
위 테케 포함 중간중간 mmap 관련 테스트 실패
syscall.c 의 syscall_handler에서
SYS_MMAP에서 mmap함수 결과를 rax 레지스터에 담았다
before
after
현재상황
mmap-inherit 빼고 mmap 관련 테케 성공