프로젝트2까지 구현된 것
: 여러개의 thread에 대해서 동기화가 정상적으로 되며, 여러개의 프로그램을 동시에 load할 수 있다.
프로젝트3의 목표
: 기계의 main memory의 크기에 따른 제약이 있는 현재 상태에서 무한한 메모리를 가지고 있다는 환영을 만들어 메모리 크기에 대한 제약을 제거하는 것
vm.*
vm_type
에 대한 정의uninit.*
anon.*
file.*
inspect.*
block.*
=> modifyPages (Virtual Page)
Frames (Physical Frame, Page Frame)
Page Tables
threads/mmu.c
에 페이지 테이블 관리 코드가 제공되어있다.Swap Slots
You will need to design/implement the following data structures:
Supplemental page table
Enables page fault handling by supplementing the page table. See Managing the Supplemental Page Table below.
Frame table
Allows efficient implementation of eviction policy of physical frames See Managing the Frame Table below.
Swap table
Tracks usage of swap slots. See Managing the Swap Table below.
Possible Design Choices
Our Team's Design Choices
Supplemental page table은 다음 두가지 용도로 사용될 수 있으며, 그 외에도 디자인에 따라 용도를 추가할 수 있다.
👉🏻 Supplemental page table은 메모리 자원을 효과적으로/효율적으로 load, swap, free 등을 할 수 있도록 도와주는 역할을 한다.
Supplemental page table을 주로 사용하는 것은 page fault handler이다. 프로젝트2에서는 항상 버그로 표기했지만, 프로젝트3에서는 더이상 버그가 아니다. 이제 page fault는 페이지가 파일이나 swap 슬롯에서 가져와져야 한다는 뜻이다.
Page Fault Handler
: exception.c
의 page_fault()
-> vm.c
의 vm_try_handle_fault()
를 호출
mmu.c
의 함수를 사용할 수 있다.frame table은 각각의 frame의 entry를 관리한다.
frame table의 entry는 각 페이지에 대한 포인터를 가지고 있다. 그리고 그 외 내가 넣고싶은 정보들을 넣어줄 수 있다.
여유 프레임이 없는 경우 퇴출시킬 페이지를 결정하는데 frame table을 사용한다.
The frames used for user pages should be obtained from the "user pool," by calling palloc_get_page(PAL_USER)
. You must use PAL_USER
to avoid allocating from the "kernel pool," which could cause some test cases to fail unexpectedly.
palloc.c
as part of your frame table implementation, be sure to retain the distinction between the two pools.프레임 테이블에 있어서 비어있는 프레임을 취득하는 것이 가장 중요한 작업인데,
비어있을 때는 쉽지만 비어있는게 없다면 페이지를 프레임에서 퇴출시켜야 한다.
swap 영역이 다 찼는데 swap을 해서 프레임에 자리를 만들어야 하는 상황이라면 pamic kernel을 일으키면 된다. 실제 OS들은 이러한 상황을 방지하거나 복구하기 위한 다양한 정책들이 있지만 우리의 프로젝트 범위는 벗어난다.
Eviction Process
하드웨어의 지원이 있음 -> 각각의 PTE에 저장되는 비트
aliases
: two (or more) pages that refer to the same frame
aliased 프레임에 접근한다면(읽기/쓰기) access를 하는데 사용된 PTE에서만 accessed, dirty 비트가 변경되게 됨. 다른 alias에서는 업데이트가 이루어지지 않음
사용중인/사용중이지 않은 swap slot을 관리하는 역할
프레임을 퇴출시킬 때 swap slot을 선정할 수 있도록 해준다.
페이지가 다시 읽어들이는 경우나 swap 영역에 페이지를 저장했던 프로세스가 종료되는 경우 이 swap 영역들 역시 free 되어야 한다.
From the vm/build
directory, use the command pintos-mkdisk swap.dsk --swap-size=n
to create a disk named swap.dsk
that contains a n-MB swap partition. Afterward, swap.dsk
will automatically be attached as an extra disk when you run pintos. Alternatively, you can tell pintos to use a temporary n-MB swap disk for a single run with --swap-size=n
.
Swap slots should be allocated lazily, that is, only when they are actually required by eviction. Reading data pages from the executable and writing them to swap immediately at process startup is not lazy. Swap slots should not be reserved to store particular pages.
A secondary interface is to "map" the file into virtual pages, using the mmap
system call. The program can then use memory instructions directly on the file data.
Suppose file foo
is 0x1000
bytes (4 kB, or one page) long. If foo
is mapped into memory starting at address 0x5000
, then any memory accesses to locations 0x5000. . .0x5fff
will access the corresponding bytes of foo
.
Your submission must be able to track what memory is used by memory mapped files. This is necessary to properly handle page faults in the mapped regions and to ensure that mapped files do not overlap any other segments within the process.
struct page {
const struct page_operations *operations;
void *va; /* Address in terms of user space */
struct frame *frame; /* Back reference for frame */
union {
struct uninit_page uninit;
struct anon_page anon;
struct file_page file;
#ifdef EFILESYS
struct page_cache page_cache;
#endif
};
};
struct page_operations {
bool (*swap_in) (struct page *, void *);
bool (*swap_out) (struct page *);
void (*destroy) (struct page *);
enum vm_type type;
};
A union is a special data type that allows us to store different types of data in a memory region. There are multiple members in the union, but only one member can contain a value at a time. This means that a page in our system can be a uninit_page, anon_page, file_page, or page_cache. For example, if a page is an anonymous page (See Anonymous Page), then the page struct will have the field struct anon_page anon
as one of its members. anon_page
will contain all the necessary information we need to keep for an anonymous page.
A function pointer is a pointer, just like any other pointers you've learned sofar, that points to a function, or an executable code within the memory. Function pointers are useful because they provide a simple way to call a specific function to execute based on run-time values without any checking. In our case, simply calling destroy (page)
is sufficient at code-level, and the compiler will choose the appropriate destroy
routine depending on the page type by calling the right function pointer.
Uninitialized Page
An uninitialized page, also known as a zero-filled page or a demand-zero page, is a type of memory page that is allocated but not explicitly initialized with any data. When an uninitialized page is allocated, it is not associated with any specific file or content.
Uninitialized pages are typically used in virtual memory systems as a form of optimization. Instead of wasting time and resources to explicitly initialize every newly allocated page with zeros or random values, the operating system can allocate uninitialized pages and rely on the virtual memory hardware to automatically provide zero-filled pages on demand.
When a program accesses an uninitialized page for the first time, the virtual memory hardware intercepts the access and triggers a page fault. The operating system then maps a zero-filled page into the virtual memory space of the program, satisfying the page fault. From the program's perspective, it appears as if the uninitialized page was initially filled with zeros.
Anonymous Page
An anonymous page, also known as a private page, is a type of memory page that is not associated with any specific file on disk. It is typically used for storing data that is generated or modified during the execution of a program. Anonymous pages are commonly used for program stacks, heap memory, and dynamically allocated memory.
Anonymous pages are called "anonymous" because they do not have a direct relationship with a file. Their contents are not persisted on disk, and they exist only in memory. When the program terminates, the contents of anonymous pages are typically deallocated and lost.
File-Backed Page
A file-backed page, also known as a mapped page, is a type of memory page that is associated with a specific file on disk. It represents a portion of the file that has been loaded into memory for efficient access by the program. File-backed pages are commonly used for memory-mapped files, shared libraries, and other file-based resources.
When a file-backed page is accessed, the operating system loads the corresponding portion of the file into memory, allowing the program to read from or write to that memory location as if it were a regular memory access. Changes made to file-backed pages are typically written back to the disk, ensuring persistence across program executions.
File-backed pages provide benefits such as memory sharing between multiple processes, efficient file I/O operations, and the ability to treat file data as if it were part of the program's memory space.
void supplemental_page_table_init (struct supplemental_page_table *spt);
struct page *spt_find_page (struct supplemental_page_table *spt, void *va);
bool spt_insert_page (struct supplemental_page_table *spt, struct page *page);
supplemental_page_table_init
is called at initd
and __do_fork
Which means spt is initiated when a user process is created.
What about kernel thread?
Page tables and supplemental page tables are dedicated to each thread(process).
Kernel accesses these tables of each process and doesn't need to have a copy of the tables or anything.
What about the virtual memory space of the operating system itself?
I am guessing that it just uses the kernel virtual address from the kernel pool which directly maps to the physical address.
/* The representation of "frame" */
struct frame {
void *kva;
struct page *page;
};
static struct frame *vm_get_frame (void);
bool vm_do_claim_page (struct page *page);
bool vm_claim_page (void *va);