[Pintos] - Project3 <Anonymous Page>

da__ell·2022년 12월 19일
0

Pintos-Kaist

목록 보기
9/9
post-thumbnail

Intro

프로세스가 커널에게 익명 페이지를 할당 요청하면 커널은 프로세스에게 가상 메모리 주소 공간을 부여한다. 이 부여된 가상 메모리 공간은 아직 실제 물리 메모리 페이지로는 할당되지 않은 공간이다.
실제 loading은 사용자 프로세스에서 해당 메모리에 접근을 시도할 때가 되어서야 이뤄지는데, 이를 lazy loading이라고 한다.

pintos의 bogus page fault 중 하나가 lazy loading으로 인한 page fault이다.
lazy loading으로 인해 가상 메모리 주소가 존재하는 유효한 페이지이지만 물리 메모리와 맵핑이 되어있지 않은 상태이다. 따라서 이 페이지 폴트가 유효하지 않은 페이지에 접근한 폴트인지 확인하고 아닐 경우 lazy-loading으로 인한 fault로 판단하여 해당 메모리에 대한 loading을 실행한다.

What is Anonymous Page

Anonymous page란 어떤 파일과도 연결되지 않은 페이지를 의미하다
커널로 부터 직접 메모리 영역을 할당받은 메모리 페이지로, heap을 거치지 않고 할당받은 메모리 공간이다. heap영역 외의 공간에서 메모리를 할당받기 때문에 heap에 메모리를 할당 할 때 발생할 수 있는 메모리 단편화(Memory Fragmentation)을 방지할 수 있다.
이런 관점에서 볼 때 힙 또한 anonymous page에 속한다고 볼 수 있다. stack을 사용할 때도 역시 anonymous page를 할당받아서 쓴다.

Anonymous page로 할당받을 경우 0으로 초기화된 메모리 공간으로 할당받는다. 익명페이지는 파일에 매핑되어 있지 않기 때문이다.

malloc() 역시 anonymous page ->heap ->malloc 식으로 anon page의 일부를 할당받는 것이다.

Page Initialization with Lazy Loading

Pintos에서는 3가지 타입의 페이지가 있고, 각 페이지 타입별로 초기화 루틴이 다르다.
우선 커널이 새로운 페이지 요청을 받으면, vm_alloc_page_with_initializer 가 호출됩니다. 이 함수는 페이지 구조체를 할당하고 페이지 타입에 맞는 적절한 초기화 함수를 세팅하여 새로운 페이지를 초기화 한다. 그리고 유저 프로그램으로 제어권을 넘긴다.
유저 프로그램이 실행될 때, lazy-loading으로 인해 콘텐츠가 아직 로드되지 않은 페이지에 접근하게 되면 페이지 폴트가 일어나게 된다. 이 페이지 폴트를 처리하는 과정에서 uninit_initialize 을 호출하고 이전에 당신이 세팅해 놓은 초기화 함수를 호출합니다.
이때 호출하는 익명 페이지를 위한 초기화 함수가 anon_initializer 이다.

Lazy Loading for Executable

지연 로딩에서는 프로세스가 실행을 시작할 때 당장 필요한 메모리 파트만 메인 메모리에 로드된다. 지연 로딩은 동시에 모든 바이너리 이미지들을 로드하는 즉시 로딩(eager loading)보다 상대적으로 오버헤드를 줄일 수 있다.

과제 설명서에는 다음과 같은 구현사항이 있다.

vm_alloc_page_with_initializer() 함수를 구현하십시오. 당신은 인자로 전달한 vm_type에 맞는 적절한 초기화 함수를 가져와야 하고 이 함수를 인자로 갖는 uninit_new 함수를 호출해야 합니다.

vm_alloc_page_with_initializer

위 함수는 초기화되지 않은 주어진 type의 페이지를 생성한다. 초기화되지 않은 페이지의 swap_in 핸들러는 자동적으로 페이지 타입에 맞게 페이지를 초기화하고 주어진 AUX를 인자로 삼는 INIT 함수를 호출한다.
초기화되지 않은 page를 생성하는 것이기 때문에 해당 가상주소를 가진 page가 spt에 존재해서는 안된다.

if (spt_find_page(spt, upage) == NULL)

이를 확인해 주고 인자로 들어오는 vm_type type을 통해 확인할 수 있는 page타입에 따라 해당되는 초기화 함수를 할당한다.
초기화되지 않은 page를 생성하는 것이기 때문에 해당 가상주소를 가진 page가 spt에 존재해서는 안된다. 이를 확인해 주고 인자로 들어오는 vm_type type을 통해 확인할 수 있는 page타입에 따라 해당되는 초기화 함수를 할당한다.

		bool *initializer_for_type;

		if (VM_TYPE(type) == VM_ANON)
			initializer_for_type = anon_initializer;
		else if (VM_TYPE(type) == VM_FILE)
			initializer_for_type = file_backed_initializer;
        else
			goto err;
            
        uninit_new(page, upage, init, type, aux, initializer_for_type);

위의 함수는 초기화되지 않은 주어진 type의 페이지를 생성한다. 초기화되지 않은 페이지의 swap_in 핸들러는 자동적으로 페이지 타입에 맞게 페이지를 초기화하고 주어진 AUX를 인자로 삼는 INIT 함수를 호출한다.

이후 page fault가 발생하면 핸들러는 연쇄적으로 함수를 호출하고 swap_in 함수를 호출하면서 마침내 uninit_initialize 함수를 호출한다. pintos에서는 이미 코드를 다 짜놓은 uninit_initialize 함수를 제공하고. 이를 수정할 수 있다.

static const struct page_operations uninit_ops = {
	.swap_in = uninit_initialize,
	.swap_out = NULL,
	.destroy = uninit_destroy,
	.type = VM_UNINIT,
};

uninit_initialize

/* Initalize the page on first fault */
static bool
uninit_initialize(struct page *page, void *kva)
{
	struct uninit_page *uninit = &page->uninit;

	/* Fetch first, page_initialize may overwrite the values */
	vm_initializer *init = uninit->init; // lazy_load_segment
	void *aux = uninit->aux;			 // lazy_args

	/* TODO: You may need to fix this function. */
	return uninit->page_initializer(page, uninit->type, kva) &&
		   (init ? init(page, aux) : true);
}

이 함수는 초기화되지 않은 페이지가 swap_in 함수를 호출하면 실행되는 함수이다.
data segment를 load하는 단계에서 uninit_new 함수를 호출하여 초기화되지 않은 uninit page를 생성하였다.
anon type의 파일이기 때문에 uninit->page_initializer로 anon_initializer가 할당될 것이다.

이 함수는 처음으로 page→operation에 있는 익명 페이지에 대한 핸들러를 설정한다. 이 함수는 익명 페이지를 초기화하는데 사용된다(예 - VM_ANON)

lazy load로 인한 page_fault가 발생하게 될 때 해당 page가 swap in이 되면서 초기화가 되고, type에 맞게 initializer_for_type (uninit->page_initialize)함수가 호출되고 page와 aux를 인자로 하여 init 함수 lazy_load_segment가 호출된다.

struct lazy_args *lazy_args = (struct lazy_args *)malloc(sizeof(struct lazy_args));
		// 이게 aux
		lazy_args->file = file;
		lazy_args->ofs = ofs;
		lazy_args->page_read_bytes = page_read_bytes;
		lazy_args->page_zero_bytes = page_zero_bytes;
		if (!vm_alloc_page_with_initializer(VM_ANON, upage,
											writable, lazy_load_segment, lazy_args))
			return false;

현재 코드는 메인 루프 안에서 파일로부터 읽을 바이트의 수와 0으로 채워야 할 바이트의 수를 측정합니다. load_segment 함수는 vm_alloc_page_with_initializer를 호출하는데, 제공할 aux 인자로써 보조 값들을 설정할 필요가 있다. 당신은 바이너리 파일을 로드할 때 필수적인 정보를 포함하는 구조체를 생성하는 것이 좋습니다.

이를 위해서 lazy_args 구조체를 생성하여 이러한 필수적인 정보를 포함하도록 하였다.

구현 사항

구현 전

구현 후

profile
daelkdev@gmail.com

0개의 댓글