6/22 PintOS Project3(6)

JK·2023년 6월 23일

Memory Mapped Files

오늘부터 Memory Mapped Files 구현을 시작하겠습니다

mmap

mmapfd로 열린 파일의 길이가 0바이트인 경우 에 대한 호출이 실패할 수 있습니다. addr이 페이지 정렬되지 않았거나 매핑된 페이지 범위가 실행 가능한 로드 시간에 매핑된 스택 또는 페이지를 포함하여 기존 매핑된 페이지 집합과 겹치는 경우 실패해야 합니다. Linux에서 addrNULL인 경우 커널은 매핑을 생성할 적절한 주소를 찾습니다. 단순화를 위해 주어진 에서 mmap을 시도할 수 있습니다 addr. 따라서 addr0인 경우 일부 Pintos 코드는 가상 페이지 0이 매핑되지 않는다고 가정하기 때문에 실패해야 합니다. mmap이 length0일 때도 실패해야 합니다. 마지막으로 콘솔 입력 및 출력을 나타내는 파일 설명자는 매핑할 수 없습니다.

메모리 매핑된 페이지도 익명 페이지처럼 게으른 방식으로 할당되어야 합니다. vm_alloc_page_with_initializer또는 를 사용하여 vm_alloc_page 페이지 개체를 만들 수 있습니다.

void *mmap(void *addr, size_t length, int writable, int fd, off_t offset)
{
	if (!addr || addr != pg_round_down(addr))
		return NULL;

	if (offset != pg_round_down(offset))
		return NULL;

	if (!is_user_vaddr(addr) || !is_user_vaddr(addr + length))
		return NULL;

	if (spt_find_page(&thread_current()->spt, addr))
		return NULL;

	struct file *f = process_get_file(fd);
	if (f == NULL)
		return NULL;

	if (file_length(f) == 0 || (int)length <= 0)
		return NULL;

	return do_mmap(addr, length, writable, f, offset); // 파일이 매핑된 가상 주소 반환
}

위의 코드는 mmap 함수입니다.

1. 함수 시그니처:

mmap 함수는 파일을 가상 메모리에 매핑하는 역할을 합니다. 함수는 매핑된 가상 주소를 반환하거나 매핑에 실패한 경우 NULL을 반환합니다.

2. 함수 동작:

  1. 주어진 addr이 NULL이거나 페이지 경계에 정확히 맞지 않는 경우 NULL을 반환합니다.

  2. 주어진 offset이 페이지 경계에 정확히 맞지 않는 경우 NULL을 반환합니다.

  3. addr과 addr + length가 유저 가상 주소 범위에 속하는지 확인합니다.

  4. thread_current()의 보조 페이지 테이블에서 주어진 addr에 해당하는 페이지를 찾습니다. 이미 해당 주소에 매핑된 페이지가 있다면 NULL을 반환합니다.

  5. 주어진 fd에 해당하는 파일을 가져옵니다. 파일이 없는 경우 NULL을 반환합니다.

  6. 파일의 길이가 0이거나 length가 0보다 작거나 같은 경우 NULL을 반환합니다.

  7. do_mmap 함수를 호출하여 실제 매핑 작업을 수행하고, 매핑된 가상 주소를 반환합니다.

do_mmap 함수는 addr을 기준으로 length만큼의 파일을 메모리에 매핑하는 작업을 수행합니다. 이 함수는 mmap 함수 내부에서 호출되며, 매핑된 가상 주소를 반환합니다.

따라서 mmap 함수는 주어진 조건에 따라 파일을 가상 메모리에 매핑하는 역할을 하며, 매핑에 성공하면 매핑된 가상 주소를 반환합니다.

do_mmap

void *
do_mmap(void *addr, size_t length, int writable,
		struct file *file, off_t offset)
{
	struct file *f = file_reopen(file);
	void *start_addr = addr; // 매핑 성공 시 파일이 매핑된 가상 주소 반환하는 데 사용
	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);	  // upage가 페이지 정렬되어 있는지 확인
	ASSERT(offset % PGSIZE == 0); // 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;
}

위의 코드는 do_mmap 함수입니다.

1. 함수 시그니처:

do_mmap 함수는 매핑된 가상 주소를 반환하거나 매핑에 실패한 경우 NULL을 반환합니다.

2. 함수 동작:

  1. 주어진 file을 재오픈하여 파일 객체 f를 가져옵니다.

  2. start_addr 변수를 addr로 초기화합니다. 이 변수는 매핑에 성공한 경우 파일이 매핑된 가상 주소를 반환하는 데 사용됩니다.

  3. length를 페이지 크기(PGSIZE)와 비교하여 총 페이지 수
    total_page_count를 계산합니다.

  4. read_bytes 변수에는 파일의 길이와 length 중 작은 값을 저장합니다.

  5. zero_bytes 변수에는 페이지 크기에서 read_bytes를 페이지 크기로 나눈 나머지를 구하여 0으로 채워야 할 바이트 수를 저장합니다.

  6. read_bytes와 zero_bytes가 페이지 크기의 배수가 되도록 보장합니다.

  7. read_bytes가 0보다 크거나 zero_bytes가 0보다 큰 동안 다음 작업을 반복합니다:

    • 현재 페이지에 대한 정보를 담는 struct lazy_load_arg 객체 lazy_load_arg를 동적으로 할당합니다.

    • lazy_load_arg에 파일 객체 f, 오프셋 offset, 읽을 바이트 수 page_read_bytes, 0으로 채울 바이트 수 page_zero_bytes를 저장합니다.

    • vm_alloc_page_with_initializer 함수를 호출하여 대기 중인 페이지 객체를 생성하고, 이를 VM_FILE 타입으로 초기화합니다. 이때 초기화 함수로 lazy_load_segment 함수와 lazy_load_arg 객체를 전달합니다.

    • 생성된 페이지 객체를 찾아 매핑된 페이지 수 mapped_page_count를 total_page_count로 설정합니다.

    • 읽은 바이트와 0으로 채운 바이트를 추적하고 가상 주소를 증가시킵니다.

  8. 매핑에 성공한 경우 start_addr를 반환합니다.

따라서 do_mmap 함수는 주어진 파일을 가상 메모리에 매핑하는 작업을 수행하고, 매핑에 성공한 경우 매핑된 가상 주소를 반환합니다. 매핑에 실패한 경우 NULL을 반환합니다.

munmap

지정된 주소 범위에 대한 매핑을 매핑 해제합니다 addr. 이 주소 범위는 아직 매핑 해제되지 않은 동일한 프로세스에서 mmap에 대한 이전 호출에서 반환된 가상 주소여야 합니다.

exit모든 매핑은 어떤 다른 방법을 통해서든 프로세스가 종료될 때 암시적으로 매핑 해제됩니다 . 암시적이든 명시적이든 매핑이 매핑 해제되면 프로세스에서 쓴 모든 페이지는 파일에 다시 기록되며 기록되지 않은 페이지는 기록되지 않아야 합니다. 그런 다음 해당 페이지는 프로세스의 가상 페이지 목록에서 제거됩니다.

파일을 닫거나 제거해도 해당 매핑이 매핑 해제되지 않습니다. 생성된 매핑은 munmapUnix 규칙에 따라 가 호출되거나 프로세스가 종료될 때까지 유효합니다. 자세한 내용은 열린 파일 제거를 참조하십시오 . file_reopen각 매핑에 대해 파일에 대한 별도의 독립적인 참조를 얻으려면 이 함수를 사용해야 합니다 .

둘 이상의 프로세스가 동일한 파일을 매핑하는 경우 일관된 데이터를 볼 필요가 없습니다. Unix는 두 개의 매핑이 동일한 물리적 페이지를 공유하도록 하여 이를 처리하고 mmap 시스템 호출에는 클라이언트가 페이지를 공유할지 또는 비공개(즉, copy-on-write)를 지정할 수 있도록 하는 인수도 있습니다.

필요에 따라 수정 vm_file_init하고 싶을 수 있습니다 .vm_file_initializervm/vm.c

void munmap(void *addr)
{
	do_munmap(addr);
}

위의 코드는 munmap 함수입니다.

1. 함수 시그니처:

munmap 함수는 do_munmap 함수를 호출하여 특정 가상 주소로부터 매핑을 해제하는 역할을 합니다

2. 함수 동작:

munmap 함수의 동작은 매우 간단합니다. 주어진 가상 주소 addr을 인자로 하여 do_munmap 함수를 호출합니다. 이 함수를 통해 해당 가상 주소로부터 매핑이 해제됩니다.

munmap 함수는 일반적으로 do_munmap 함수의 복잡한 동작을 추상화하여 사용자가 간단하게 매핑 해제를 호출할 수 있도록 하는 래퍼 함수입니다. 따라서 munmap 함수는 실제 매핑 해제 작업을 담당하는 do_munmap 함수에게 매핑 해제할 가상 주소를 전달하는 역할을 합니다.

do_munmap

void do_munmap(void *addr)
{
	struct supplemental_page_table *spt = &thread_current()->spt;
	struct page *p = spt_find_page(spt, addr);
	int count = p->mapped_page_count;
	for (int i = 0; i < count; i++)
	{
		if (p)
			destroy(p);
		{
			if (pml4_get_page(thread_current()->pml4, p->va))
				// 매핑된 프레임이 있다면 = swap out 되지 않았다면 -> 페이지를 제거하고 연결된 프레임도 제거
				spt_remove_page(spt, p);
			else
			{ // swap out된 경우에는 매핑된 프레임이 없으므로 페이지만 제거
				hash_delete(&spt->spt_hash, &p->hash_elem);
				free(p);
			}
		}
		pml4_clear_page(thread_current()->pml4, p->va);
		addr += PGSIZE;
		p = spt_find_page(spt, addr);
	}
}

위의 코드는 do_munmap 함수입니다.

1. 함수 시그니처:

do_munmap 함수는 주어진 가상 주소로부터 매핑을 해제하는 역할을 합니다. 함수 시그니처는 다음과 같습니다

2. 함수 동작:

  1. 현재 스레드의 보조 페이지 테이블(spt)을 얻습니다.

  2. 주어진 가상 주소 addr에 해당하는 페이지를 spt에서 찾습니다.

  3. 페이지의 mapped_page_count 값을 얻어 매핑된 총 페이지 수를 파악합니다.

  4. 반복문을 통해 매핑된 모든 페이지를 처리합니다.

    • 페이지를 파괴(destroy)합니다.

    • 만약 페이지에 매핑된 프레임이 있다면(즉, swap out되지 않았다면), 페이지와 연결된 프레임을 제거합니다.

    • 그렇지 않은 경우(즉, swap out된 경우), 페이지를 해시 테이블에서 제거하고 메모리를 해제합니다.

    • 현재 스레드의 페이지 테이블에서 해당 가상 주소의 페이지 엔트리를 제거합니다.

  5. 매핑 해제가 완료된 후, 주어진 가상 주소를 PGSIZE(페이지 크기)만큼 증가시킵니다.

  6. 다음 페이지에 대해 동일한 과정을 반복합니다.

do_munmap 함수는 매핑을 해제하고 관련된 자원들을 정리하는 역할을 합니다. 이를 통해 사용 중인 가상 주소 영역을 해제하고 프레임이나 페이지 테이블 등을 적절히 관리할 수 있게 됩니다.

profile
^^

0개의 댓글