6/20 PintOS Project3(4)

JK·2023년 6월 20일

PintOS Project3(4)

어제에 이어서 Anonymous page 부분 구현을 해보겠습니다

load_segment

static bool
load_segment(struct file *file, off_t ofs, uint8_t *upage,
			 uint32_t read_bytes, uint32_t zero_bytes, bool writable)
{
	ASSERT((read_bytes + zero_bytes) % PGSIZE == 0);
	ASSERT(pg_ofs(upage) == 0);
	ASSERT(ofs % PGSIZE == 0);

	while (read_bytes > 0 || zero_bytes > 0)
	{
		/* Do calculate how to fill this page.
		 * We will read PAGE_READ_BYTES bytes from FILE
		 * and zero the final PAGE_ZERO_BYTES bytes. */
		size_t page_read_bytes = read_bytes < PGSIZE ? read_bytes : PGSIZE; // 최대로 읽을 수 있는 크기는 PGSIZE
		size_t page_zero_bytes = PGSIZE - page_read_bytes;

		/* TODO: Set up aux to pass information to the lazy_load_segment. */
		struct lazy_load_arg *lazy_load_arg = (struct lazy_load_arg *)malloc(sizeof(struct lazy_load_arg));
		lazy_load_arg->file = file;
		lazy_load_arg->ofs = ofs;
		lazy_load_arg->read_bytes = page_read_bytes;
		lazy_load_arg->zero_bytes = page_zero_bytes;

		if (!vm_alloc_page_with_initializer(VM_ANON, upage,
											writable, lazy_load_segment, lazy_load_arg))
			return false;

		/* Advance. */
		read_bytes -= page_read_bytes;
		zero_bytes -= page_zero_bytes;
		upage += PGSIZE;
		ofs += page_read_bytes;
	}
	return true;
}

위 코드는 파일을 메모리에 로드하는 역할을 합니다.

함수 시그니처:

함수는 파일 포인터 file, 파일의 오프셋 ofs, 로드할 메모리의 시작 주소 upage, 읽을 바이트 수 read_bytes, 0으로 채울 바이트 수 zero_bytes, 그리고 메모리의 쓰기 가능 여부 writable을 매개변수로 받습니다. 함수는 로드 작업의 성공 여부를 나타내는 불리언 값을 반환합니다.

함수 동작:

  1. 읽을 바이트 수와 0으로 채울 바이트 수의 합이 페이지 크기인 PGSIZE의 배수임을 확인합니다.

  2. upage의 오프셋이 0인지 확인합니다.

  3. ofs가 페이지 크기인 PGSIZE의 배수임을 확인합니다.

  4. read_bytes와 zero_bytes가 모두 0이 될 때까지 반복합니다.

  5. 각 페이지에 대해 다음 작업을 수행합니다:

  • 현재 페이지에 읽을 바이트 수 page_read_bytes를 계산합니다. 읽을 바이트 수가 페이지 크기인 PGSIZE보다 작을 수 있으므로 작은 값을 선택합니다.

  • 남은 바이트 수 page_zero_bytes는 0으로 채울 바이트 수입니다. 페이지 크기인 PGSIZE에서 page_read_bytes를 뺀 값을 사용합니다.

  • lazy_load_segment 함수에 전달할 보조 데이터를 나타내는 lazy_load_arg를 동적으로 할당합니다.

  • lazy_load_arg에 파일 포인터 file, 오프셋 ofs, 읽을 바이트 수 page_read_bytes, 0으로 채울 바이트 수 page_zero_bytes를 저장합니다.

  • vm_alloc_page_with_initializer 함수를 호출하여 페이지를 할당하고 lazy_load_segment 함수를 초기화 함수로 사용하여 페이지를 초기화합니다.

  • 페이지 할당이 실패하면 false를 반환합니다.

  • 읽은 바이트 수와 0으로 채울 바이트 수를 갱신합니다.

  • upage를 다음 페이지로 이동시킵니다.

  • 다음 페이지의 오프셋으로 이동시킵니다.

  1. 모든 페이지가 성공적으로 로드되면 true를 반환합니다.

이 함수를 사용하여 파일의 세그먼트를 메모리에 로드할 수 있습니다. 파일을 페이지 단위로 읽어와 메모리에 할당하고, 남은 공간은 0으로 채워서 메모리에 로드합니다. 이를 위해 페이지 별로 lazy_load_segment 함수를 초기화 함수로 사용하여 vm_alloc_page_with_initializer 함수를 호출합니다.

lazy_load_segment

bool lazy_load_segment(struct page *page, void *aux)
{
	/* TODO: Load the segment from the file */
	/* TODO: This called when the first page fault occurs on address VA. */
	/* TODO: VA is available when calling this function. */

	struct lazy_load_arg *lazy_load_arg = (struct lazy_load_arg *)aux;

	file_seek(lazy_load_arg->file, lazy_load_arg->ofs);
    
	if (file_read(lazy_load_arg->file, page->frame->kva, lazy_load_arg->read_bytes) != (int)(lazy_load_arg->read_bytes))
	{
		palloc_free_page(page->frame->kva);
		return false;
	}

	memset(page->frame->kva + lazy_load_arg->read_bytes, 0, lazy_load_arg->zero_bytes);
	// free(lazy_load_arg); // 🚨 Todo : 어디서 반환하지?

	return true;
}

위 코드는 lazy_load_segment입니다.

1. 함수 시그니처:

이 함수는 페이지 폴트가 발생할 때 처음 호출되는 함수로, 파일에서 세그먼트를 로드하는 역할을 합니다. 함수는 페이지를 나타내는 page 포인터와 로드에 필요한 보조 데이터를 나타내는 aux 포인터를 매개변수로 받으며, 로드 작업의 성공 여부를 나타내는 불리언 값을 반환합니다.

함수 동작:

  1. aux 포인터를 struct lazy_load_arg 타입으로 형변환하여 lazy_load_arg 변수에 저장합니다.

  2. 파일의 현재 위치를 lazy_load_arg->ofs로 이동시킵니다.

  3. 파일에서 lazy_load_arg->read_bytes만큼 데이터를 읽어 page->frame->kva에 저장합니다.

  4. 읽은 위치 이후부터 lazy_load_arg->zero_bytes만큼의 공간을 0으로 채웁니다.

  5. 로드 작업이 성공적으로 완료되면 true를 반환합니다. 만약 파일 읽기 도중 오류가 발생하면 할당된 프레임 메모리를 해제하고 false를 반환합니다.

이 함수를 호출하여 세그먼트를 게으르게 로드할 수 있습니다. 파일에서 데이터를 읽어 페이지의 프레임 메모리에 저장하고, 나머지 영역을 0으로 초기화하여 세그먼트를 로드합니다.

supplemental_page_table_copy

bool supplemental_page_table_copy(struct supplemental_page_table *dst UNUSED, struct supplemental_page_table *src UNUSED)
{
	// TODO: 보조 페이지 테이블을 src에서 dst로 복사합니다.
	// TODO: src의 각 페이지를 순회하고 dst에 해당 entry의 사본을 만듭니다.
	// TODO: uninit page를 할당하고 그것을 즉시 claim해야 합니다.
	struct hash_iterator i;
	hash_first(&i, &src->spt_hash);
	while (hash_next(&i))
	{
		// src_page 정보
		struct page *src_page = hash_entry(hash_cur(&i), struct page, hash_elem);
		enum vm_type type = src_page->operations->type;
		void *upage = src_page->va;
		bool writable = src_page->writable;

		/* 1) type이 uninit이면 */
		if (type == VM_UNINIT)
		{ 
			vm_initializer *init = src_page->uninit.init;
			void *aux = src_page->uninit.aux;
			vm_alloc_page_with_initializer(VM_ANON, upage, writable, init, aux);
			continue;
		}

		/* 2) type이 file이면 */
		if (type == VM_FILE)
		{
			struct lazy_load_arg *file_aux = malloc(sizeof(struct lazy_load_arg));
			file_aux->file = src_page->file.file;
			file_aux->ofs = src_page->file.ofs;
			file_aux->read_bytes = src_page->file.read_bytes;
			file_aux->zero_bytes = src_page->file.zero_bytes;
			if (!vm_alloc_page_with_initializer(type, upage, writable, NULL, file_aux))
				return false;
			struct page *file_page = spt_find_page(dst, upage);
			file_backed_initializer(file_page, type, NULL);
			file_page->frame = src_page->frame;
			pml4_set_page(thread_current()->pml4, file_page->va, src_page->frame->kva, src_page->writable);
			continue;
		}

		/* 3) type이 anon이면 */
		if (!vm_alloc_page(type, upage, writable))
			return false;

		if (!vm_claim_page(upage))
			return false;

		struct page *dst_page = spt_find_page(dst, upage);
		memcpy(dst_page->frame->kva, src_page->frame->kva, PGSIZE);
	}
	return true;
}

위의 코드는 supplemental_page_table_copy입니다.

1. 함수 시그니처:

이 함수는 두 개의 보충 페이지 테이블인 src와 dst를 받으며, 반환값은 복사 작업의 성공 여부를 나타내는 불리언 값입니다.

이 함수의 역할은 src 보충 페이지 테이블의 각 페이지를 순회하고, 각 페이지에 대한 복사본을 dst 보충 페이지 테이블에 만드는 것입니다. 복사되는 페이지는 uninit 페이지, file 페이지, anon 페이지로 구분됩니다.

2. 함수 동작:

  1. src 보충 페이지 테이블을 순회하기 위해 해시 반복자 i를 선언하고 초기화합니다.

  2. hash_first 함수를 사용하여 src 보충 페이지 테이블의 첫 번째 페이지 엔트리로 이동합니다.

  3. hash_next 함수를 사용하여 다음 페이지 엔트리로 이동하면서 반복문을 실행합니다.

  4. 각 페이지 엔트리에 대해 다음 작업을 수행합니다:

  • 페이지의 유형(type), 가상 주소(upage), 쓰기 가능 여부(writable) 등의 정보를 가져옵니다.

  • 페이지의 유형에 따라 다음 동작 중 하나를 수행합니다:

    • 유형이 VM_UNINIT인 경우:
      src_page의 초기화자(init)와 보조 데이터(aux)를 가져옵니다.

    • vm_alloc_page_with_initializer 함수를 사용하여 VM_ANON 유형의 페이지를 할당하고 초기화합니다.

  • 유형이 VM_FILE인 경우:

    • src_page의 파일 관련 정보를 가져와서 struct lazy_load_arg 구조체에 저장합니다.

    • vm_alloc_page_with_initializer 함수를 사용하여 파일 유형의 페이지를 할당하고 초기화합니다.

    • src_page의 프레임과 페이지 테이블 엔트리를 복사하여 dst_page에 설정합니다.

  • 유형이 VM_ANON인 경우:

    • vm_alloc_page 함수를 사용하여 VM_ANON 유형의 페이지를 할당합니다.

    • vm_claim_page 함수를 사용하여 페이지를 claim합니다.

    • src_page의 프레임 메모리를 dst_page로 복사합니다.

  1. 모든 페이지 엔트리에 대한 복사 작업이 성공하면 true를 반환합니다. 복사 작업 중에 오류가 발생하면 false를 반환합니다.

이 함수를 호출함으로써 src 보충 페이지 테이블의 모든 페이지 엔트리를 dst 보충 페이지 테이블로 복사할 수 있습니다. 각 페이지 엔트리의 유형에 따라 초기화 및 관련 데이터를 처리하고, 프레임 메모리를 복사하여 새로운 페이지를 생성합니다.

supplemental_page_table_kill

void
supplemental_page_table_kill (struct supplemental_page_table *spt UNUSED) {
	/* TODO: Destroy all the supplemental_page_table hold by thread and
	 * TODO: writeback all the modified contents to the storage. */
	hash_clear(&spt->spt_hash, hash_page_destroy);
}

위의 코드는 supplemental_page_table_kill입니다.

1. 함수 시그니처:

이 함수는 매개변수로 struct supplemental_page_table 포인터인 spt를 받으며, 반환값은 없습니다.

이 함수의 역할은 스레드에 의해 보유되고 있는 보충 페이지 테이블을 제거하고, 수정된 내용을 저장소에 기록하는 것입니다.

함수 동작:

  1. hash_clear 함수를 사용하여 spt의 해시 테이블을 제거합니다. 이를 통해 보충 페이지 테이블이 보유하고 있던 모든 페이지 엔트리가 삭제됩니다.

  2. hash_clear 함수의 두 번째 매개변수로 hash_page_destroy 함수를 전달합니다. 이 함수는 페이지 엔트리를 삭제하기 전에 페이지에 대한 정리 작업을 수행합니다.

이 함수를 호출함으로써 보충 페이지 테이블과 관련된 자원을 해제하고 수정된 페이지 내용을 저장소에 기록할 수 있습니다.


Git book 상으로는 Anonymous page 부분의 구현은 끝났지만... 어디가 잘못된 건지 Project2의 부분이 제대로 Pass가 되지 않아서 좀 더 고쳐 봐야 될 거 같습니다
수정이 끝나면 올리겠습니다 :)

profile
^^

0개의 댓글