(TIL)다시 돌아 munmap()수정하기...

낚시하는 곰·2025년 6월 6일
1

jungle TIL

목록 보기
10/20

void *mmap(...) 시스템 콜

계획

전반적인 구현 계획

  1. 명세 정리: 요소 이름들 확실히 잡고 가기
  2. 그림으로 추상화하기
    1. 컨트롤 플로우
    2. 아키텍처도 그려보기
  3. 수도코드
  4. 한번 싹 정리
  5. 코딩
  6. 디버깅

컨트롤 플로우 그리기

swap out()

→ mmap()은 Disk에 file을 backup하고, 매핑해주는 함수??

→ addr, length, fd, offset등이 필요하다.

→ file list[fd]로 해당 file을 찾는다

→ offset ↔ length까지 read()해서 Disk에 있는 file에 write()한다??

  • Disk에 있는 file을 어떻게 찾지??
  • Disk_alloc() - 디스크에 메모리 할당 이런 함수가 있는 지 찾아보자.

→ swap in() 실행

→ get_disk_file() - 대충 disk에 있는 file addr를 반환해주는 함수

→ struct thread의 mapping list

→ …

아키텍처

file copy는 큰 주소에서 작은 주소로 복사가 이루어진다. 복사하고 난 후에 file backed()을 하기 위해서 read at()을 진행해야 하는데 위 상태에서 보면 addr은 가장 낮은 주소이다. addr을 위로 올라가면서 read at()을 진행해야 할까?

구상

  • lazy loading 방식으로 메모리 매핑 진행함
  • mmap(addr, length, fd, offset)
  • addr, length = 0 → fail
  • file size = 0 → fail
  • addr의 page에 spt가 존재하면 → fail
  • fd == 0 || fd == 1 → fail
  • addr이 4KB align이 되어 있지 않으면 → fail
  • file reopen()실행 → open()으로 충분하지 않나?
  • do_mmap()함수 호출 → 실제 page 매핑?
    • 매핑할 가상 주소, file length, fd?, file 정보를 가지고 있는 aux등이 필요할 것 같다.
  • mmap() 성공 시 addr을 반환. 만약 실패 시 NULL을 반환.

flow

if → addr == 0, length == 0, spt_find() ≠ NULL, fd == 0, fd == 1, addr % 4096 ≠ 0이라면 NULL을 반환한다.

file_reopen()

page = do_mmap()

return addr

구현

// 1. addr이 0인지 check한다
// 2. fd가 0이나 1이 아닌지 check한다
// 3.file 용량이 0인지 check 한다.
// 4. addr의 page에 spt가 없어야 한다?
// 5. addr aline인있는 지 c
void *mmap (void *addr, size_t length, int writable, int fd, off_t offset) 
{
	struct thread *curr = thread_current();
	if (addr == NULL || length == 0 || fd == 0 || fd == 1 || (uint64_t) addr % 4096 != 0 || spt_find_page(&curr->spt, addr)) { // 이건 맞다고 가정하고
		return NULL;
	}
	
	struct file *file = process_get_file_by_fd(fd);
	
	void *upage = do_mmap(addr, length, writable, file, offset);

	return upage;
}

do_mmap()

flow

목표 : lazy load할 때 필요한 lazy aux를 초기화하고, page fault시점에 file read at()을 진행한다.

  • check addr()하고,
  • lazy aux정보를 초기화
  • vm_alloc()으로 메모리 할당
  • file backed()으로 disk에 공간을 만들어줘야 할 것 같은데?
    • vm_alloc_initializer()에서 vm_file type으로 줘야 함.
  • 만약 vm_alloc_initializer()이 실패했다면
    • 메모리 할당은 해제해줘야 하는데 어떻게해줘야 하지?
  • copy()한 page를 읽어야 하는데 어떻게 읽지?
    • addr이 가장 낮은 주소일테니까 1page만큼 위로 올리자.
    • 그리고 length를 - 1page 하고, length가 음수가 되면 종료
  • file mmapping list멤버를 struct 구조체에 선언한다.
    • 나중에 munmap할 때 spt를 순회하면서 page를 free해주기 위해서 spt에 page정보를 초기화 해줘야 한다.
      • struct thead에 mmap_list 선언
      • list에 mmap_file 삽입해준다.
        • start addr 멤버 초기화
  • 반환값은 addr주소를 반환한다.

문제 정의

  • file reopen()을 while() 안에 넣고, stack마다 open()을 해주고 있는데 여기서 while()로 reopen()을 여러 번 호출하는 것이 문제라고 한다. 왜 문제일까? stack에 쌓은 page를 전부…. page와 file 개념을 내가 혼동하고 있나? 사실 지금 하나의 file을 여러 번 open하고 있었던 게 아닐까??

debug

(mmap-over-stk) begin
(mmap-over-stk) open "sample.txt"
(mmap-over-stk) open "sample.txt": FAILED
mmap-over-stk: exit(1)
void
test_main (void) 
{
  int handle;
  uintptr_t handle_page = ROUND_DOWN ((uintptr_t) &handle, 4096);
  
  CHECK ((handle = open ("sample.txt")) > 1, "open \"sample.txt\"");
  CHECK (mmap ((void *) handle_page, 4096, 0, handle, 0) == MAP_FAILED,
         "try to mmap over stack segment");
}

file open()에서 fail이 난다. 왜 발생하는 것일까?

syscall.c의 open()은 내가 건든적이 없으니 아닌 것 같고.

struct lazy_aux_file_backed *aux = malloc(sizeof(struct lazy_aux_file_backed));
		aux->file = file_reopen(file);
		aux->writable = writable;
		aux->length = length > PGSIZE ? PGSIZE : length;
		aux->offset = offset;

여기서 문제가 발생한 건가?

int open(const char *filename) {
	check_address(filename); // 이상한 포인터면 즉시 종료
	dprintfe("[open] check_address 바로 다음 라인\n");
	struct file *file_obj = filesys_open(filename);
	dprintfe("[open] filesys_open 바로 다음 라인\n");
	
	if (file_obj == NULL) {
		return -1;
	}

	int fd = process_add_file(file_obj);

	if (fd == -1) { // fd table 꽉찬 경우 그냥 닫아버림
		file_close(file_obj);
    	file_obj = NULL;
	}
	
	dprintfe("[open] open() 마지막 라인\n");
	return fd;
}

여기서 open()의 마지막 라인이 실행이 안된다…

잘 되던게 안될 수는 없다!! 분명 뭔가가 있는 것이다. 솔직히 약간 모르겠지만 아까 팀원이 file name을 잘못 줘서 터졌다는 걸 들었다. 바로 file name 수정한다!!!

void *mmap (void *addr, size_t length, int writable, int fd, off_t offset) 
{
	struct thread *curr = thread_current();
	if (addr == NULL || length == 0 || fd == 0 || fd == 1 || (uint64_t) addr % 4096 != 0 || spt_find_page(&curr->spt, addr)) { // 이건 맞다고 가정하고
		return NULL;
	}
	
	struct file *file = process_get_file_by_fd(fd);
	
	void *upage = do_mmap(addr, length, writable, file, offset);

	return upage;
}

file 찾아서 반환했지만 실패…

struct lazy_aux_file_backed *aux = malloc(sizeof(struct lazy_aux_file_backed));
		aux->file = file_reopen(file);
		aux->writable = writable;
		aux->length = length > PGSIZE ? PGSIZE : length;
		aux->offset = offset;

여기서 reopen()이 반복되면서 하나의 File 중복 참조해서 문제가 생기는 걸까?

pass tests/vm/mmap-over-stk

이거 open()에서 문제가 생긴 것이 아니다. printf()를 끄지 않고 돌렸는데 이때 출력용으로 사용한 포인터에서 문제가 생긴 것 같다.

구현

/* Do the mmap */
void *
do_mmap(void *addr, size_t length, int writable, struct file *file, off_t offset)
{
	// 1. addr로부터 페이지 생성
	// 1-1. lazy_load, aux 초기화해서 넘겨주기.
	// 1-2. 복사(length, offset, 등등) 이거 바로 해줘요? 그럼 또 lazy 아니잖아. -> 이 내용이 lazy_load에서 타입 체크후에 복사 바로 하면 되지 않겠나.
	// 1-3. 나머자 내용은 0으로 채워야 함.
	void *start_addr = addr;

	while (length > 0)
	{
		struct lazy_aux_file_backed *aux = malloc(sizeof(struct lazy_aux_file_backed));
		aux->file = file_reopen(file);
		aux->writable = writable;
		aux->length = length > PGSIZE ? PGSIZE : length;
		aux->offset = offset;

		if (!vm_alloc_page_with_initializer(VM_FILE, addr, writable, lazy_load_file_backed, aux))
		{
			// page clean??
			free(aux);
			while(start_addr < addr){
				vm_dealloc_page(spt_find_page(&thread_current()->spt, addr));
				addr -= PGSIZE;				
			}
			file_close(file);
			return NULL;
		}

		// 쓴 만큼 offset, length 업데이트.
		length -= PGSIZE;
		offset += PGSIZE;
		addr += PGSIZE;
	}

	struct mmap_file *mmap_file = malloc(sizeof(struct mmap_file));
	mmap_file->start_addr = start_addr;
	list_push_back(&thread_current()->mmap_list, &mmap_file->elem);

	return start_addr;
}
profile
취업 준비생 낚곰입니다!! 반갑습니다!!

0개의 댓글