본문 바로가기
SW Jungle [예림]/PintOS

[PintOS] Project 3 - Anonymous Page (1)

by novxerim 2022. 10. 24.

https://velog.io/@yerimii11/PintOS-Project-3-Anonymous-Page-1 2022년 1월 25일에 작성된 게시글 아카이브입니다.  (사유: 블로그이전)

 

[PintOS] Project 3 - Anonymous Page (1)

이 프로젝트의 이 파트에서, anonymous page 라는 non-disk 기반 이미지를 구현합니다.익명 매핑에는 백업 파일이나 장치가 없습니다. (파일 기반 페이지와 달리) 명명된 파일 소스가 없기 때문에 익

velog.io


- Anonymous Page

이 프로젝트의 이 파트에서, anonymous page 라는 non-disk 기반 이미지를 구현합니다.

익명 매핑에는 백업 파일이나 장치가 없습니다. (파일 기반 페이지와 달리) 명명된 파일 소스가 없기 때문에 익명입니다. 익명 페이지는 스택 및 힙과 같은 실행 파일에서 사용됩니다.

include/vm/anon.h 의 익명페이지인 anon_page 를 설명하는 구조체가 있습니다. 현재는 empty상태이지만, 구현할 때 익명 페이지의 상태 또는 필요한 정보를 저장하기 위해 구성원을 추가할 수 있습니다. 또한 include/vm/page.h의 struct page를 보면, 페이지의 일반 정보가 포함되어 있습니다. 참고로 익명 페이지의 경우, struct anon_page anon이 페이지 구조체에 포함됩니다.

- Page Initialization with Lazy Loading

지연 로딩으로 페이지 초기화

지연 로딩은 메모리 로딩이 필요한 시점까지 지연되는 디자인(설계)입니다. 
하나의 페이지가 할당된다고 했을 때, 그것은 페이지에 해당하는 page struct는 있지만 전용(dedicated) 물리적 프레임이 없으며, 페이지의 실제 content가 아직 로드되지 않았다는 것을 의미합니다. 콘텐츠는 실제로 필요한 경우에만 로드되며 이는 page fault로 신호를 보냅니다.

페이지 유형은 3가지가 있으므로 페이지마다 초기화 루틴이 다릅니다. 아래 섹션에서 다시 설명하겠지만 여기서는 페이지 초기화 흐름(initialization flow)의 상위 수준 보기(high-level view)를 제공합니다. 
먼저, vm_alloc_page_with_initializer은 커널이 새 페이지 요청을 수신할 때 호출됩니다. 이니셜라이저는 페이지 구조를 할당하고 페이지 유형에 따라 적절한 이니셜라이저를 설정하여 새 페이지를 초기화하고 컨트롤을 다시 사용자 프로그램으로 반환합니다. 
사용자 프로그램이 실행되면서 어느 시점에서, 프로그램이 소유한 것으로 생각되는 페이지에 액세스하려고 하지만 아직 내용이 없을 때 페이지 폴트가 발생합니다. 오류 처리 절차 중에 uninit_initialize가 호출되고 이전에 설정한 이니셜라이저를 호출합니다. 이니셜라이저는 익명 페이지를 위한 anon_initializer와, file-backed pages를 위한 file_backed_initializer를 위한 것입니다.

페이지는 initialize->(page_fault->lazy-load->swap-in>swap-out->...)->destroy의 라이프 사이클을 가질 수 있습니다. 라이프 사이클의 각 전환(transition)마다 필요한 절차는 페이지 유형(또는 VM_TYPE)에 따라 다릅니다. 이전 단락 내용은 초기화의 예시였습니다(윗 문단 내용들). 이 프로젝트에서는 각 페이지 유형에 대해 이러한 전환 프로세스를 구현합니다.

- Lazy Loading for Executable (구현)

실행 파일에 대한 지연 로딩

지연 로딩에서는 프로세스가 실행을 시작할 때 즉시 필요한 메모리 부분만 주 메모리에 로드됩니다. 이것은 모든 바이너리 이미지를 한 번에 메모리에 로드하는 Eager loading에 비해 오버헤드를 줄일 수 있습니다.

지연 로딩을 지원하기 위해 include/vm/vm.h의 ****VM_UNINIT에서 호출되는 페이지 유형을 소개합니다. 모든 페이지는 처음에 VM_INIT페이지로 생성됩니다. 우리는 또한 초기화되지 않은 페이지에 대한 페이지 구조(include/vm/uninit.h의 struct uninit_page)도 제공합니다.  초기화되지 않은 페이지를 생성, 초기화 및 파괴하는 기능은 include/vm/uninit.c에서 찾을 수 있습니다. 나중에 이 기능을 완료해야 합니다.

페이지 폴트에서 페이지 폴트 핸들러(page_fault in userprog/exception.c)는 제어를 vm/vm.c의 vm_try_handle_fault으로 전달하여 먼저 유효한 페이지 폴트인지 확인합니다. 유효 하다는 것은 유효하지 않은 액세스 오류를 의미합니다. 가짜 오류(bogus fault)인 경우, 일부 내용을 페이지에 로드하고 제어(control)를 사용자 프로그램에 반환(return)합니다.

가짜 페이지 오류(bogus page fault)에는 세 가지 경우가 있습니다 : 지연 로드(lazy-loaded), 교체된 페이지(swaped-out page) 및 쓰기 방지된 페이지(write-protected page)입니다. (참조 : Copy-on-Write(Extra)). 

지금은 첫 번째 경우인 lazy-loaded page를 고려합니다. 지연 로딩에 대한 페이지 폴트인 경우, 커널은 이전에 당신이 vm_alloc_page_with_initializer에 설정했던 이니셜라이저 중 하나를 호출하여 세그먼트를 느리게 로드(lazy load)합니다. userprog/process.c의 lazy_load_segment에서 구현해야 합니다.

💡 여기까지 요약

✔️ Anonymous Page

anonymous page 라는 non-disk 기반 이미지를 구현
익명 페이지는 스택 및 힙과 같은 실행 파일에서 사용됨


Page Initialization with Lazy Loading 지연 로딩으로 페이지 초기화

지연 로딩은 메모리 로딩이 필요한 시점까지 지연되는 디자인
콘텐츠는 실제로 필요한 경우에만 로드되며 이는 page fault로 신호를 보냄

  • 페이지 유형 3가지 중 - 페이지 초기화 흐름(initialization flow)의 상위 수준 보기(high-level view)를 제공
    • 순서
      • vm_alloc_page_with_initializer은 커널이 새 페이지 요청을 수신할 때 호출
      • 이니셜라이저 : 페이지 구조를 할당 → 페이지 유형에 따라 적절한 이니셜라이저를 설정하여 새 페이지를 초기화 → 컨트롤을 다시 사용자 프로그램으로 반환
        사용자 프로그램이 실행되면서 어느 시점에서, 프로그램이 소유한 것으로 생각되는 페이지에 액세스하려고 하지만 아직 내용이 없을 때 페이지 폴트가 발생. 
        오류 처리 절차 중에 uninit_initialize가 호출되고 이전에 설정한 이니셜라이저를 호출
      • 페이지는 initialize->(page_fault->lazy-load->swap-in>swap-out->...)->destroy의 라이프 사이클을 가짐
        라이프 사이클의 각 전환(transition)마다 필요한 절차는 페이지 유형(또는 VM_TYPE)에 따라 다름
      • 이 프로젝트에서는 각 페이지 유형에 대해 이러한 전환 프로세스를 구현

Lazy Loading for Executable (구현) 실행 파일에 대한 지연 로딩

지연 로딩에서는 프로세스가 실행을 시작할 때 즉시 필요한 메모리 부분만 주 메모리에 로드

  • 모든 바이너리 이미지를 한 번에 메모리에 로드하는 Eager loading에 비해 오버헤드를 줄임
    지연 로딩을 지원 - include/vm/vm.h의 VM_UNINIT에서 호출되는 페이지 유형
  • 모든 페이지는 처음에 VM_INIT페이지로 생성
  • 초기화되지 않은 페이지에 대한 페이지 구조(include/vm/uninit.h의 struct uninit_page)도 제공
    - 초기화되지 않은 페이지를 생성, 초기화 및 파괴하는 기능은 include/vm/uninit.c
  • 페이지 폴트에서 페이지 폴트 핸들러(page_fault in userprog/exception.c)는
    제어를 vm/vm.c의 vm_try_handle_fault으로 전달하여 먼저 유효한 페이지 폴트인지 확인
    - 유효 하다는 것은 유효하지 않은 액세스 오류
  • 가짜 오류(bogus fault)인 경우, 일부 내용을 페이지에 로드하고 제어(control)를 사용자 프로그램에 반환(return)합니다.

가짜 페이지 오류(bogus page fault) 3가지
1. 지연 로드(lazy-loaded) 2. 교체된 페이지(swaped-out page) 3. 쓰기 방지된 페이지(write-protected page)

1번 케이스인 lazy-loaded page
지연 로딩에 대한 페이지 폴트인 경우, 커널은 이전에 당신이 vm_alloc_page_with_initializer에 설정했던 이니셜라이저 중 하나를 호출하여 세그먼트를 느리게 로드(lazy load) (userprog/process.c의 lazy_load_segment).

구현1. 시작~


.
.
.

vm_alloc_page_with_initializer()을 구현하세요. 전달된 vm_type과 함께 호출한 uninit_new에 따라 적절한 이니셜라이저를 fetch 해야합니다.


- 구현1.

bool vm_alloc_page_with_initializer (enum vm_type type, void *va,
        bool writable, vm_initializer *init, void *aux);

주어진 유형으로 초기화되지 않은 페이지를 만듭니다. uninit 페이지의 swap_in 핸들러는 자동으로 페이지를 타입에 따라 초기화하고, 주어진 AUX로 INIT을 호출합니다. 
페이지 구조가 있으면 페이지를 프로세스의 보조 페이지 테이블에 삽입하십시오.  vm.h에 정의된 VM_TYPE매크로를 사용하면 편리할 수 있습니다.

페이지 오류 핸들러는 호출 체인(call chain)을 따라가다가 swap_in을 호출할 때 마침내  uninit_intialize에 도달합니다. 우리는 그것에 대한 완전한 구현을 제공합니다. 하지만 디자인에 따라 uninit_initialize 를 수정해야 할 수도 있습니다.

  • 코드
    /* Create the pending page object with initializer. 
    	If you want to create a page, do not create it directly 
    	and make it through this function or `vm_alloc_page`. */
    /* 이니셜라이저를 사용하여 보류 중인 페이지 객체를 만듭니다. 
    	페이지를 생성하려면 직접 작성하지 말고, 이 함수 또는 'vm_alloc_page'를 통해 수행합니다. */ -> 이 부분 구현 안된 듯? 다른 함수에서 쓰나?
    
    bool vm_alloc_page_with_initializer (enum vm_type type, void *upage, bool writable, vm_initializer *init, void *aux) {
    
    	ASSERT (VM_TYPE(type) != VM_UNINIT)
    
    	struct supplemental_page_table *spt = &thread_current ()->spt;
    
    	/* Check wheter the upage is already occupied or not. */
    	if (spt_find_page (spt, upage) == NULL) {
    		/* TODO: Create the page, fetch the initialier according to the VM type, 
    							and then create "uninit" page struct by calling uninit_new. 
    							You should modify the field after calling the uninit_new. */
    		/* P3 추가 */
    		bool (*initializer)(struct page *, enum vm_type, void *);
    		switch(type){
    			case VM_ANON: case VM_ANON|VM_MARKER_0: // 왜 두번째 케이스에서 저렇게 이중(?)으로 체크하지? 하나 주석처리해도 될 듯. -> 케이스1 or 케이스2 (|:비트연산자or)
    				initializer = anon_initializer;
    				break;
    			case VM_FILE:
    				initializer = file_backed_initializer;
    				break;
    		}
    
    		struct page *new_page = malloc(sizeof(struct page));
    		uninit_new (new_page, upage, init, type, aux, initializer);
    
    		new_page->writable = writable;
    		new_page->page_cnt = -1; // only for file-mapped pages
    
    		/* TODO: Insert the page into the spt. */
    		spt_insert_page(spt, new_page); // should always return true - checked that upage is not in spt
    
    			return true;
    	}
    err:
    	return false;
    }
  • vm_type
        // [include>vm>vm.h]
        enum vm_type {
        	/* page not initialized */
        	VM_UNINIT = 0,
        	/* page not related to the file, aka anonymous page */
        	VM_ANON = 1,
        	/* page that realated to the file */
        	VM_FILE = 2,
        	/* page that hold the page cache, for project 4 */
        	VM_PAGE_CACHE = 3,
        
        	/* Bit flags to store state */
        
        	/* Auxillary bit flag marker for store information. 저장 정보에 대한 보조 비트 플래그 마커
        	You can add more markers, until the value is fit in the int. */
        	VM_MARKER_0 = (1 << 3),
        	VM_MARKER_1 = (1 << 4),
        
        	/* DO NOT EXCEED THIS VALUE. */
        	VM_MARKER_END = (1 << 31),
        };
  • anon_initializer (구현4.)
    - 코드
            /* Initialize the file mapping */
            bool
            anon_initializer (struct page *page, enum vm_type type, void *kva) {
            	struct uninit_page *uninit = &page->uninit;
            	memset(uninit, 0, sizeof(struct uninit_page));
            
            	/* Set up the handler */
            	page->operations = &anon_ops;
            
            	struct anon_page *anon_page = &page->anon;
            	anon_page->swap_sec = -1;
            	return true;
            }
  • file_backed_initializer (구현)
    - 스켈레톤
            // [vm>file.c]
            /* Initialize the file backed page */
            bool
            file_backed_initializer (struct page *page, enum vm_type type, void *kva) {
            	/* Set up the handler */
            	page->operations = &file_ops;
            
            	struct file_page *file_page = &page->file;
            }
  • 정답코드
            /* Initialize the file backed page */
            bool
            file_backed_initializer (struct page *page, enum vm_type type, void *kva) {
            	struct uninit_page *uninit = &page->uninit;
            	// vm_initializer *init = uninit->init;
            	void *aux = uninit->aux;
            
            	/* Set up the handler */
            	page->operations = &file_ops;
            
            	memset(uninit, 0, sizeof(struct uninit_page));
            
            	struct lazy_load_info *info = (struct lazy_load_info *)aux;
            	struct file_page *file_page = &page->file;
            	file_page->file = info->file;
            	file_page->length = info->page_read_bytes;
            	file_page->offset = info->offset;
            	return true;
            }
  • uninit_new

: page를 uninit으로 만들어서 spt에 올려두는 과정이다. (그러나 실제 type을 담아둔다)

        // [vm>uninit.c]
        /* DO NOT MODIFY this function */
        void uninit_new (struct page *page, void *va, vm_initializer *init, enum vm_type type, void *aux,
        		bool (*initializer)(struct page *, enum vm_type, void *)) {
        	ASSERT (page != NULL);
        
        	*page = (struct page) {
        		.operations = &uninit_ops,
        		.va = va,
        		.frame = NULL, /* no frame for now */
        		.uninit = (struct uninit_page) {
        			.init = init,
        			.type = type,
        			.aux = aux,
        			.page_initializer = initializer,
        		}
        	};
        }
  • spt_insert_page
        /* Insert PAGE into spt with validation. */
        bool spt_insert_page (struct supplemental_page_table *spt UNUSED, struct page *page UNUSED) {
        	int succ = false;
        	/* P3 추가 */
        	struct hash_elem *e = hash_find(&spt->spt_hash, &page->hash_elem);
        	if(e != NULL) // page already in SPT
        		return succ; // false, fail
        
        	// page not in SPT
        	return insert_page(&spt->spt_hash, page);
        	return succ = true;
        }

- 구현2.

static bool uninit_initialize (struct page *page, void *kva);

첫 번째 fault에서 페이지를 초기화합니다. 템플릿 코드는 먼저 vm_initializer 와 aux를 가져오고, 함수 포인터를 통해 해당 page_initializer를 호출합니다. 
디자인에 따라 기능을 수정해야 할 수도 있습니다.

  • 사용자 프로그램이 실행되면서 어느 시점에서, 프로그램이 소유한 것으로 생각되는 페이지에 액세스하려고 하지만 아직 내용이 없을 때 페이지 폴트가 발생. 오류 처리 절차 중에 uninit_initialize가 호출되고 이전에 설정한 이니셜라이저를 호출
    • 이니셜라이저 : 페이지 구조를 할당 → 페이지 유형에 따라 적절한 이니셜라이저를 설정하여 새 페이지를 초기화 → 컨트롤을 다시 사용자 프로그램으로 반환
  • 코드 → 구현 되어 있음. 안 건드려도 되는 듯 !
    // [vm>uninit.c]
    /* 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;
    	void *aux = uninit->aux;
    
    	/* TODO: You may need to fix this function. */
    	return uninit->page_initializer (page, uninit->type, kva) && (init ? init (page, aux) : true);
    }

필요에 따라 vm/anon.c의  vm_anon_init 및 anon_initializer를 수정할 수도 있습니다. (구현3, 4)


- 구현3.

void vm_anon_init (void);

익명 페이지 하위시스템(subsystem)에 대해 초기화합니다. 
이 함수에서는 익명 페이지와 관련된 모든 것을 설정할 수 있습니다.

  • 스켈레톤 코드
    /* Initialize the data for anonymous pages */
    void
    vm_anon_init (void) {
    	/* TODO: Set up the swap_disk. */
    	swap_disk = NULL;
    }
  • 정답코드 (맨 밑 두 줄 다시 보기)
    /* Initialize the data for anonymous pages */
    void
    vm_anon_init (void) {
    	/* TODO: Set up the swap_disk. */
    	swap_disk = disk_get(1, 1);
    
    #ifdef DBG
    	printf("disk size : %d\n", disk_size(swap_disk));
    #endif
    
    	bitcnt = disk_size(swap_disk)/SECTORS_IN_PAGE; // #ifdef Q. disk size decided by swap-size option? 
    	swap_table = bitmap_create(bitcnt); // each bit = swap slot for a frame
    }

- 구현4.

bool anon_initializer (struct page *page,enum vm_type type, void *kva);

이 함수는 먼저 page->operations의 익명 페이지에 대한 핸들러를 설정합니다. 
현재 비어 있는 구조체인 anon_page의 일부 정보를 업데이트해야 할 수도 있습니다. 
이 함수는 익명 페이지(예 : VM_ANON)에 대한 이니셜라이저로 사용됩니다. → vm_anon(익명페이지)를 초기화시키는 이니셜라이저이다.

  • 스켈레톤 코드
    // [vm>anon.c]
    /* Initialize the file mapping */
    bool
    anon_initializer (struct page *page, enum vm_type type, void *kva) {
    	/* Set up the handler */
    	page->operations = &anon_ops;
    	struct anon_page *anon_page = &page->anon;
    }
  • 정답코드
    /* Initialize the file mapping */
    bool
    anon_initializer (struct page *page, enum vm_type type, void *kva) {
    	struct uninit_page *uninit = &page->uninit;
    	memset(uninit, 0, sizeof(struct uninit_page));
    
    	/* Set up the handler */
    	page->operations = &anon_ops;
    
    	struct anon_page *anon_page = &page->anon;
    	anon_page->swap_sec = -1;
    	return true;
    }
  • struct page (page→operations)
    /* The representation of "page".
     * This is kind of "parent class", which has four "child class"es, which are
     * uninit_page, file_page, anon_page, and page cache (project4).
     * DO NOT REMOVE/MODIFY PREDEFINED MEMBER OF THIS STRUCTURE. */
    struct page {
    	const struct page_operations *operations;
    	void *va;              /* Address in terms of user space */
    	struct frame *frame;   /* Back reference for frame */
    
    	/* Your implementation */
    	/* P3 추가 */
    	struct hash_elem hash_elem; /* Hash table element for SPT */
    	bool writable;
    	int page_cnt; // only for file-mapped pages
    
    	/* Per-type data are binded into the union.
    	 * Each function automatically detects the current union */
    	union {
    		struct uninit_page uninit;
    		struct anon_page anon;
    		struct file_page file;
    #ifdef EFILESYS
    		struct page_cache page_cache;
    #endif
    	};
    };
  • struct page_operations (&anon_ops)
    /* DO NOT MODIFY this struct */
    static const struct page_operations anon_ops = {
    	.swap_in = anon_swap_in,
    	.swap_out = anon_swap_out,
    	.destroy = anon_destroy,
    	.type = VM_ANON,
    };
  • struct anon_page
    // [include>vm>anon.h]
    struct anon_page {
    	int swap_sec; // sector where swapped contents are stored
    };
  • memset

userprog/process.c의  load_segment와 lazy_load_segment를 구현하세요.  
실행 파일에서 세그먼트 로딩를 구현하세요. 이러한 모든 페이지는 지연 로드되어야 합니다(should be loaded lazily).
즉, 커널이 페이지 폴트를 가로챌(intercepts) 때만 로드해야 합니다.

userprog/process.c의 load_segment에 있는 루프인 프로그램 로더의 핵심(core)을 수정해야 합니다. 루프를 돌 때마다, 보류 중인 페이지 객체(a pending page object)를 만들기 위해 vm_alloc_page_with_initializer를 호출해 냅니다. 페이지 폴트가 발생한다는 것은, 파일에서 세그먼트가 실제로 로드되는 경우입니다.


- 구현5.

static bool load_segment (struct file *file, off_t ofs, uint8_t *upage, uint32_t read_bytes, uint32_t zero_bytes, bool writable);

현재 코드는 파일에서 읽을 바이트 수와 메인 루프 내에서 0으로 채울 바이트 수를 계산합니다. 그런 다음, 보류 중인 객체(a pending object)를 만들기 위해 vm_alloc_page_with_initializer를 호출합니다. 
우리는 vm_alloc_page_with_initializer에 제공할 aux인수로 보조 값을 설정해야 합니다. 이진 로드(binary loading)에 필요한 정보를 포함하는 구조를 만들고 싶을 수도 있습니다.

  • 코드 * 여기 컨테이너 추가
    // [userprog>process.c]
    /* Loads a segment starting at offset OFS in FILE at address UPAGE.
      In total, READ_BYTES + ZERO_BYTES bytes of virtual memory are initialized, as follows:
      - READ_BYTES bytes at UPAGE must be read from FILE starting at offset OFS.
      - ZERO_BYTES bytes at UPAGE + READ_BYTES must be zeroed.
      The pages initialized by this function must be writable by the user process if WRITABLE is true, read-only otherwise.
      Return true if successful, false if a memory allocation error or disk read error occurs. */
    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);
    
    	file_seek(file, ofs);
    	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;
    		size_t page_zero_bytes = PGSIZE - page_read_bytes;
    
    		/* Get a page of memory. */
    		uint8_t *kpage = palloc_get_page(PAL_USER);
    		if (kpage == NULL)
    			return false;
    
    		/* Load this page. */
    		if (file_read(file, kpage, page_read_bytes) != (int)page_read_bytes)
    		{
    			palloc_free_page(kpage);
    			return false;
    		}
    		memset(kpage + page_read_bytes, 0, page_zero_bytes);
    
    		/* Add the page to the process's address space. */
    		if (!install_page(upage, kpage, writable))
    		{
    			printf("fail\n");
    			palloc_free_page(kpage);
    			return false;
    		}
    
    		/* Advance. */
    		read_bytes -= page_read_bytes;
    		zero_bytes -= page_zero_bytes;
    		upage += PGSIZE;
    	}
    	return true;
    }
  • file_seek
    /* Sets the current position in FILE to NEW_POS bytes from the start of the file. */
    void file_seek (struct file *file, off_t new_pos) {
    	ASSERT (file != NULL);
    	ASSERT (new_pos >= 0);
    	file->pos = new_pos;
    }
  • file_read
    /* Reads SIZE bytes from FILE into BUFFER, starting at the file's current position.
     * Returns the number of bytes actually read, which may be less than SIZE if end of file is reached.
     * Advances FILE's position by the number of bytes read. */
    off_t file_read (struct file *file, void *buffer, off_t size) {
    	off_t bytes_read = inode_read_at (file->inode, buffer, size, file->pos);
    	file->pos += bytes_read;
    	return bytes_read;
    }
`typedef int32_t off_t;`
  • install_page
    // [userprog>process.c]
    #ifndef VM
    ..
    ..
    /* Adds a mapping from user virtual address UPAGE to kernel virtual address KPAGE to the page table.
     * If WRITABLE is true, the user process may modify the page; otherwise, it is read-only.
     * UPAGE must not already be mapped.
     * KPAGE should probably be a page obtained from the user pool with palloc_get_page().
     * Returns true on success, false if UPAGE is already mapped or if memory allocation fails. */
    static bool
    install_page (void *upage, void *kpage, bool writable) {
    	struct thread *t = thread_current ();
    
    	/* Verify that there's not already a page at that virtual address, then map our page there. */
    	return (pml4_get_page (t->pml4, upage) == NULL
    			&& pml4_set_page (t->pml4, upage, kpage, writable));
    }
    #else
    /* From here, codes will be used after project 3.
     * If you want to implement the function for only project 2, implement it on the
     * upper block. */ // 딱히 더 쓰진 않아도 될 듯

- 구현6.

static bool lazy_load_segment (struct page *page, void *aux);

아마 load_segment  vm_alloc_page_with_initializer 함수에 있는 네 번째 인수로 lazy_load_segment가 제공된다는 것을 눈치 채셨을 것입니다. 
이 함수는 실행 파일 페이지에 대한 이니셜라이저이며 페이지 폴트일 때 호출됩니다. page struct와 aux인수를 받습니다. aux는 load_segment에서 설정한 정보입니다. 이 정보를 사용하여, 세그먼트를 읽을 파일을 찾고 결국 세그먼트를 메모리로 읽어야 합니다.

  • 코드
    // [userprog>process.c]
    /* 실행 파일 페이지에 대한 이니셜라이저, 페이지폴트일 때 호출 됨. */
    static 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. */
    	
    	// void *aux = lazy_load_info; (aux는 load segment에서 설정한 정보이다.)
    	// 이 정보를 사용하여, 세그먼트를 읽을 파일을 찾고 -> 세그먼트를 메모리로 읽어야 한다.
    
    	struct lazy_load_info * lazy_load_info = (struct lazy_load_info *)aux;
    	struct file * file = lazy_load_info->file;
    	size_t page_read_bytes = lazy_load_info->page_read_bytes;
    	size_t page_zero_bytes = lazy_load_info->page_zero_bytes;
    	off_t offset = lazy_load_info->offset;
    
    	file_seek(file, offset);
    
    	//vm_do_claim_page(page);
    	ASSERT (page->frame != NULL); 	//이 상황에서 page->frame이 제대로 설정돼있는가?
    	void * kva = page->frame->kva;
    	if (file_read(file, kva, page_read_bytes) != (int)page_read_bytes)
    	{
    		//palloc_free_page(page); // #ifdef DBG Q. 여기서 free해주는거 맞아?
    		free(lazy_load_info);
    		return false;
    	}
    
    	memset(kva + page_read_bytes, 0, page_zero_bytes);
    	free(lazy_load_info);
    
    	file_seek(file, offset); // may read the file later - reset fileobj pos
    	
    	return true;
    }
- `struct lazy_load_info` **(추가 구현)**
    
        // [includ>vm>vm.h]
        /* P3 추가 */
        struct lazy_load_info {
        	struct file *file;
        	size_t page_read_bytes;
        	size_t page_zero_bytes;
        	off_t offset;
        };
  • 수정사항
    // [vm>anon.c] 추가
    const int SECTORS_IN_PAGE = 8; // 4kB / 512 (DISK_SECTOR_SIZE)

    // [include>vm>anon.h] 추가
    struct bitmap *swap_table; // 0 - empty, 1 - filled
    int bitcnt;
    
    struct anon_page {
        int swap_sec; // sector where swapped contents are stored /* 추가 */
    };
    // [userprog>syscall.c] 추가
    void check_address(const uint64_t *uaddr) {
    struct thread *curr = thread_current();
    	// if (user_addr = NULL || !(is_user_vaddr(user_addr)) || pml4_get_page(curr->pml4, user_addr) == NULL) {
    	if (uaddr == NULL || !(is_user_vaddr(uaddr))) { /* P3 수정 */
    		exit(-1);
    	}
    }

스택 할당을 새 메모리 관리 시스템에 맞게 userprog/process.c의 setup_stack을 조정 해야 합니다. 
첫 번째 스택 페이지는 지연 할당될 필요가 없습니다. faulted가 발생할 때까지 기다릴 필요 없이, 로드 시 command line 인수를 사용하여 이를 할당하고 초기화할 수 있습니다. 그리고 스택을 식별하는 방법을 제공해야 할 수도 있습니다.  vm/vm.h 파일의 vm_type에 있는 보조 마커(auxillary)(예 : VM_MARKER_0)를 사용하여 페이지를 표시(mark)할 수 있습니다.

마지막으로, spt_find_page를 통해 보충 페이지 테이블을 참조하여 오류가 발생한 주소에 해당하는 page struct를 해결하도록 vm_try_handle_fault함수를 수정합니다.

모든 요구 사항을 구현한 후, fork를 제외한 프로젝트2의 모든 테스트를 통과해야 합니다.

댓글