Non-disk based image called anonymous page.
An anonymous mapping has no backing file or device. It is anonymous because it does not have any named file source (unlike file-backed pages). Anonymous pages are used in executable, such as for stack and heap.
Anonymous page, struct anon_page anon
, is included in its struct page
, which contains generic information of a page.
Lazy loading is a design where the loading of memory is deferred until the point at which it is needed.
General page initalization flow
vm_alloc_page_with_initializer
is invoked when the kernel receives a new page request.uninit_initialize
is invoked and calls the initializer set earlier.anon_initializer
for anonymous pages and file_backed_initializer
for file-backed pages.When a process starts it execution, only the memory parts that are immediately needed are loaded onto the main memory. Reduces the overhead compared to eager loading as a result.
To support the lazy loading, a page type called VM_UNINIT
is used.
All pages are initially created as VM_UNINIT
pages!
Page structure for uninitialized pages - struct uninit_page
Functions for creating, initializing, and destroying uninitialized pages can be found in include/vm/uninit.c
.
page_fault
in userprog/exception.c
) transfers control to vm_try_handle_fault
in vm/vm.c
vm_alloc_page_with_initializer
to lazy load the segment.vm_alloc_page_with_initializer
bool vm_alloc_page_with_initializer (enum vm_type type, void *va,
bool writable, vm_initializer *init, void *aux);
VM_TYPE
macro defined in vm.h
can be handy.Things to Implement :
Fetch an appropriate initializer according to the passed vm_type
Create "uninit" page struct by calling uninit_new with the initializer fetched
Modify the field after calling the uninit_new
What this function does :
uninit_new()
👉🏻 lazy loading을 하기 때문에 페이지를 할당받아도, 해당 페이지에 들어갈 contents는 실제로 로딩을 하지는 않는다. 그저 페이지 struct를 만들어서 페이지 테이블에 넣어둘 뿐이다.
👉🏻 load 함수는 load_segment 함수를 부르며, load_segment는 VM_ANON 타입으로 vm_alloc_page_with_initializer를 부른다.
👉🏻 vm_alloc_page_with_initializer는 페이지를 할당받는 함수로, uninitialized type으로 페이지를 초기화 해주며, 해당 페이지에 들어갈 예정인 페이지 타입에 맞게 operation에 사용할 함수들과 init에 사용할 함수(i.g. lazy_load_segment()
) 등을 미리 넣어두고 이 페이지를 페이지 테이블에 넣어둔다.
👉🏻 그래서 나중에 페이지 테이블 엔트리에 들어가있는 해당 내용을 참조하고자 할 때 엔트리는 있지만 실제 페이지는 생성이 되어 있지 않으므로 page fault가 발생하며, 이렇게 처음으로 page fault 발생 시 현재 해당 페이지를 uninit 타입이기 때문에 uninit_initialize 함수를 호출하게 된다. (아마도)
uninit_initialize
The page fault handler follows its call chain, and finally reaches uninit_intialize
when it calls swap_in. We give the complete implementation for it. Although, you may need to modify the uninit_initialize
according to your design.
static bool uninit_initialize (struct page *page, void *kva);
vm_initializer
and aux
What this function does :
1. get uninit_page struct from page struct
2. run the initializer which was set previously according to the page type (VM_ANON
in the case of load_segment()
) => returns true if successful, else returns false
3. if there is an init function set in the uninit_page struct, run the init function with the aux
arguments also passed through uninit_page struct => returns true if successful, else returns false. if there is no init function, pass true
so that the results will only depend on the result of the initializer function
4. return true if both initializer and init succeedes, if any fails, return false
vm_anon_init
, anon_initializer
May modify vm_anon_init
and anon_initializer
in vm/anon.c
according to needs.
void vm_anon_init (void);
Initialize for anonymous page subsystem. In this function, you can setup anything related to the anonymous page.
👉🏻 당장은 어떤걸 initiate 해야하는지 모르기 때문에 일단 넘어감! 아마도 anonymous 페이지는 내용이 없는 상태로 시작하기 때문에 zero fill을 해주는 내용이 들어가지 않을까 생각함
bool anon_initializer (struct page *page,enum vm_type type, void *kva);
first sets up the handlers for the anonymous page in page->operations
might need to update some information in anon_page
, which is currently an empty struct
used as initializer for anonymous pages (i.e. VM_ANON
)
👉🏻 우선은 어떤게 필요할지 모르기 때문에 넘어감
load_segment
, lazy_load_segment
Implement load_segment
and lazy_load_segment
in userprog/process.c
.
Implement segment loading from executables. All of these pages should be loaded lazily, that is, only as the kernel intercepts page faults for them.
You'll need to modify the core of the program loader, which is the loop in load_segment
of userprog/process.c
. Each time around the loop, it makes a call to vm_alloc_page_with_initializer
to create a pending page object. When a page fault occurs, this is when the segment is actually loaded from the file.
static bool load_segment (struct file *file, off_t ofs, uint8_t *upage,
uint32_t read_bytes, uint32_t zero_bytes, bool writable);
Current code calculates the number of bytes to read from a file and the number of bytes to fill with zeros within the main loop. Then, it calls
vm_alloc_page_with_initializer
to create a pending object. You need to set up the auxiliary values asaux
argument that you will provide tovm_alloc_page_with_initializer
. You may want to create a structure that contains necessary information for the loading of binary.
Things to implement :
static bool lazy_load_segment (struct page *page, void *aux);
You may have noticed that
lazy_load_segment
is supplied as the fourth argument ofvm_alloc_page_with_initializer
inload_segment
. This function is the initializer for executable's pages and is invoked in times of page faults. It receives a page struct andaux
as arguments.aux
is the information you set up inload_segment
. Using this information, you have to find the file to read the segment from and eventually read the segment into memory.
This is called when the first page fault occurs on address VA. VA is available when calling this function.
Things to implement :
setup_stack
You should adjust the setup_stack
in userprog/process.c
to fit stack allocation into the new memory management system.
static bool setup_stack (struct intr_frame *if_);
The first stack page need not be allocated lazily.
You can allocate and initialize it with the command line arguments at load time, with no need to wait for it to be faulted in.
You might need to provide the way to identify the stack.
You can use the auxillary markers in vm_type
of vm/vm.h
(e.g. VM_MARKER_0
) to mark the page.
Create a PAGE of stack at the USER_STACK. Return true on success.
vm_try_handle_fault
Finally, modify vm_try_handle_fault
function to resolve the page struct corresponding to the faulted address by consulting to the supplemental page table through spt_find_page
.
/* Return true on success */
bool vm_try_handle_fault (struct intr_frame *f UNUSED, void *addr,
bool user UNUSED, bool write UNUSED, bool not_present UNUSED) {
struct supplemental_page_table *spt = &thread_current ()->spt;
struct page *page = NULL;
page = spt_find_page(spt, pg_round_down(addr));
if (page == NULL) {
return false;
}
return vm_do_claim_page (page);
}
👉🏻 Problem : 커널 주소는 걸러줘야 하나?
Reasoning :
(유저는 당연히 유저 주소만 사용이 가능하고) 커널이 페이지에 접근하려고 할 때, aliasing을 위해 유저 주소만 사용하도록 하는 제약을 사용한다면 커널 주소는 걸러주는게 맞을거라고 생각한다.
다른 커널 함수들에서 유저 주소만 사용하도록 신경써서 구현한다고 하더라도 잘못되는 경우가 있을 수 있기 때문에 커널 주소가 들어온건 아닌지 이 함수에서 확인된다면 좋을 것 같다고 생각했다.
근데 애초에 유저 주소만 취급하는 spt table에 대해서 spt_find_page를 하기 때문에, 이미 커널 주소는 걸러진거라고 볼 수 있지 않을까 싶다.
spt_find_table도 해당 주소에 대해 테이블 엔트리를 찾지 못하면 NULL 값을 리턴하기 때문에 page == NULL에서 모두 걸러질 수 있다.
👉🏻 그런데.. 결국 uaddr는 frame에 접근하기 위해서 kernel address를 사용하는데, 그러면 항상 kernel address를 사용하고 있는 것이 되는거 아닌가? 나중에 유저 프로그램이 주소에서 데이터를 읽을 때 sup talble을 거치나?
After implementing all the requirements, all of the tests in project 2 except fork should be passed.
pass tests/userprog/args-none
pass tests/userprog/args-single
pass tests/userprog/args-multiple
pass tests/userprog/args-many
pass tests/userprog/args-dbl-space
pass tests/userprog/halt
pass tests/userprog/exit
pass tests/userprog/create-normal
pass tests/userprog/create-empty
pass tests/userprog/create-null
pass tests/userprog/create-bad-ptr
pass tests/userprog/create-long
pass tests/userprog/create-exists
pass tests/userprog/create-bound
pass tests/userprog/open-normal
pass tests/userprog/open-missing
pass tests/userprog/open-boundary
pass tests/userprog/open-empty
pass tests/userprog/open-null
pass tests/userprog/open-bad-ptr
pass tests/userprog/open-twice
pass tests/userprog/close-normal
pass tests/userprog/close-twice
pass tests/userprog/close-bad-fd
pass tests/userprog/read-normal
pass tests/userprog/read-bad-ptr
FAIL tests/userprog/read-boundary
pass tests/userprog/read-zero
pass tests/userprog/read-stdout
pass tests/userprog/read-bad-fd
pass tests/userprog/write-normal
pass tests/userprog/write-bad-ptr
pass tests/userprog/write-boundary
pass tests/userprog/write-zero
pass tests/userprog/write-stdin
pass tests/userprog/write-bad-fd
pass tests/userprog/fork-once
pass tests/userprog/fork-multiple
pass tests/userprog/fork-recursive
FAIL tests/userprog/fork-read
FAIL tests/userprog/fork-close
FAIL tests/userprog/fork-boundary
FAIL tests/userprog/exec-once
FAIL tests/userprog/exec-arg
FAIL tests/userprog/exec-boundary
pass tests/userprog/exec-missing
pass tests/userprog/exec-bad-ptr
FAIL tests/userprog/exec-read
FAIL tests/userprog/wait-simple
FAIL tests/userprog/wait-twice
FAIL tests/userprog/wait-killed
pass tests/userprog/wait-bad-pid
FAIL tests/userprog/multi-recurse
FAIL tests/userprog/multi-child-fd
pass tests/userprog/rox-simple
FAIL tests/userprog/rox-child
FAIL tests/userprog/rox-multichild
pass tests/userprog/bad-read
pass tests/userprog/bad-write
pass tests/userprog/bad-read2
pass tests/userprog/bad-write2
pass tests/userprog/bad-jump
pass tests/userprog/bad-jump2
FAIL tests/vm/pt-grow-stack
pass tests/vm/pt-grow-bad
FAIL tests/vm/pt-big-stk-obj
pass tests/vm/pt-bad-addr
pass tests/vm/pt-bad-read
FAIL tests/vm/pt-write-code
FAIL tests/vm/pt-write-code2
FAIL tests/vm/pt-grow-stk-sc
pass tests/vm/page-linear
FAIL tests/vm/page-parallel
FAIL tests/vm/page-merge-seq
FAIL tests/vm/page-merge-par
FAIL tests/vm/page-merge-stk
FAIL tests/vm/page-merge-mm
pass tests/vm/page-shuffle
FAIL tests/vm/mmap-read
FAIL tests/vm/mmap-close
FAIL tests/vm/mmap-unmap
FAIL tests/vm/mmap-overlap
FAIL tests/vm/mmap-twice
FAIL tests/vm/mmap-write
FAIL tests/vm/mmap-ro
FAIL tests/vm/mmap-exit
FAIL tests/vm/mmap-shuffle
pass tests/vm/mmap-bad-fd
FAIL tests/vm/mmap-clean
FAIL tests/vm/mmap-inherit
FAIL tests/vm/mmap-misalign
FAIL tests/vm/mmap-null
FAIL tests/vm/mmap-over-code
FAIL tests/vm/mmap-over-data
FAIL tests/vm/mmap-over-stk
FAIL tests/vm/mmap-remove
pass tests/vm/mmap-zero
pass tests/vm/mmap-bad-fd2
pass tests/vm/mmap-bad-fd3
pass tests/vm/mmap-zero-len
FAIL tests/vm/mmap-off
FAIL tests/vm/mmap-bad-off
FAIL tests/vm/mmap-kernel
FAIL tests/vm/lazy-file
pass tests/vm/lazy-anon
FAIL tests/vm/swap-file
FAIL tests/vm/swap-anon
FAIL tests/vm/swap-iter
FAIL tests/vm/swap-fork
pass tests/filesys/base/lg-create
pass tests/filesys/base/lg-full
pass tests/filesys/base/lg-random
pass tests/filesys/base/lg-seq-block
pass tests/filesys/base/lg-seq-random
pass tests/filesys/base/sm-create
pass tests/filesys/base/sm-full
pass tests/filesys/base/sm-random
pass tests/filesys/base/sm-seq-block
pass tests/filesys/base/sm-seq-random
FAIL tests/filesys/base/syn-read
pass tests/filesys/base/syn-remove
FAIL tests/filesys/base/syn-write
pass tests/threads/alarm-single
pass tests/threads/alarm-multiple
pass tests/threads/alarm-simultaneous
pass tests/threads/alarm-priority
pass tests/threads/alarm-zero
pass tests/threads/alarm-negative
pass tests/threads/priority-change
pass tests/threads/priority-donate-one
pass tests/threads/priority-donate-multiple
pass tests/threads/priority-donate-multiple2
pass tests/threads/priority-donate-nest
pass tests/threads/priority-donate-sema
pass tests/threads/priority-donate-lower
pass tests/threads/priority-fifo
pass tests/threads/priority-preempt
pass tests/threads/priority-sema
pass tests/threads/priority-condvar
pass tests/threads/priority-donate-chain
FAIL tests/vm/cow/cow-simple
53 of 141 tests failed.
Implement supplemental_page_table_copy
and supplemental_page_table_kill
pass tests/userprog/args-none
pass tests/userprog/args-single
pass tests/userprog/args-multiple
pass tests/userprog/args-many
pass tests/userprog/args-dbl-space
pass tests/userprog/halt
pass tests/userprog/exit
pass tests/userprog/create-normal
pass tests/userprog/create-empty
pass tests/userprog/create-null
pass tests/userprog/create-bad-ptr
pass tests/userprog/create-long
pass tests/userprog/create-exists
pass tests/userprog/create-bound
pass tests/userprog/open-normal
pass tests/userprog/open-missing
pass tests/userprog/open-boundary
pass tests/userprog/open-empty
pass tests/userprog/open-null
pass tests/userprog/open-bad-ptr
pass tests/userprog/open-twice
pass tests/userprog/close-normal
pass tests/userprog/close-twice
pass tests/userprog/close-bad-fd
pass tests/userprog/read-normal
pass tests/userprog/read-bad-ptr
FAIL tests/userprog/read-boundary
pass tests/userprog/read-zero
pass tests/userprog/read-stdout
pass tests/userprog/read-bad-fd
pass tests/userprog/write-normal
pass tests/userprog/write-bad-ptr
pass tests/userprog/write-boundary
pass tests/userprog/write-zero
pass tests/userprog/write-stdin
pass tests/userprog/write-bad-fd
pass tests/userprog/fork-once
pass tests/userprog/fork-multiple
pass tests/userprog/fork-recursive
FAIL tests/userprog/fork-read
FAIL tests/userprog/fork-close
FAIL tests/userprog/fork-boundary
FAIL tests/userprog/exec-once
FAIL tests/userprog/exec-arg
FAIL tests/userprog/exec-boundary
pass tests/userprog/exec-missing
pass tests/userprog/exec-bad-ptr
FAIL tests/userprog/exec-read
FAIL tests/userprog/wait-simple
FAIL tests/userprog/wait-twice
FAIL tests/userprog/wait-killed
pass tests/userprog/wait-bad-pid
FAIL tests/userprog/multi-recurse
FAIL tests/userprog/multi-child-fd
pass tests/userprog/rox-simple
FAIL tests/userprog/rox-child
FAIL tests/userprog/rox-multichild
pass tests/userprog/bad-read
pass tests/userprog/bad-write
pass tests/userprog/bad-read2
pass tests/userprog/bad-write2
pass tests/userprog/bad-jump
pass tests/userprog/bad-jump2
FAIL tests/vm/pt-grow-stack
pass tests/vm/pt-grow-bad
FAIL tests/vm/pt-big-stk-obj
pass tests/vm/pt-bad-addr
pass tests/vm/pt-bad-read
FAIL tests/vm/pt-write-code
FAIL tests/vm/pt-write-code2
FAIL tests/vm/pt-grow-stk-sc
pass tests/vm/page-linear
FAIL tests/vm/page-parallel
FAIL tests/vm/page-merge-seq
FAIL tests/vm/page-merge-par
FAIL tests/vm/page-merge-stk
FAIL tests/vm/page-merge-mm
pass tests/vm/page-shuffle
FAIL tests/vm/mmap-read
FAIL tests/vm/mmap-close
FAIL tests/vm/mmap-unmap
FAIL tests/vm/mmap-overlap
FAIL tests/vm/mmap-twice
FAIL tests/vm/mmap-write
FAIL tests/vm/mmap-ro
FAIL tests/vm/mmap-exit
FAIL tests/vm/mmap-shuffle
pass tests/vm/mmap-bad-fd
FAIL tests/vm/mmap-clean
FAIL tests/vm/mmap-inherit
FAIL tests/vm/mmap-misalign
FAIL tests/vm/mmap-null
FAIL tests/vm/mmap-over-code
FAIL tests/vm/mmap-over-data
FAIL tests/vm/mmap-over-stk
FAIL tests/vm/mmap-remove
pass tests/vm/mmap-zero
pass tests/vm/mmap-bad-fd2
pass tests/vm/mmap-bad-fd3
pass tests/vm/mmap-zero-len
FAIL tests/vm/mmap-off
FAIL tests/vm/mmap-bad-off
FAIL tests/vm/mmap-kernel
FAIL tests/vm/lazy-file
pass tests/vm/lazy-anon
FAIL tests/vm/swap-file
FAIL tests/vm/swap-anon
FAIL tests/vm/swap-iter
FAIL tests/vm/swap-fork
pass tests/filesys/base/lg-create
pass tests/filesys/base/lg-full
pass tests/filesys/base/lg-random
pass tests/filesys/base/lg-seq-block
pass tests/filesys/base/lg-seq-random
pass tests/filesys/base/sm-create
pass tests/filesys/base/sm-full
pass tests/filesys/base/sm-random
pass tests/filesys/base/sm-seq-block
pass tests/filesys/base/sm-seq-random
FAIL tests/filesys/base/syn-read
pass tests/filesys/base/syn-remove
FAIL tests/filesys/base/syn-write
pass tests/threads/alarm-single
pass tests/threads/alarm-multiple
pass tests/threads/alarm-simultaneous
pass tests/threads/alarm-priority
pass tests/threads/alarm-zero
pass tests/threads/alarm-negative
pass tests/threads/priority-change
pass tests/threads/priority-donate-one
pass tests/threads/priority-donate-multiple
pass tests/threads/priority-donate-multiple2
pass tests/threads/priority-donate-nest
pass tests/threads/priority-donate-sema
pass tests/threads/priority-donate-lower
pass tests/threads/priority-fifo
pass tests/threads/priority-preempt
pass tests/threads/priority-sema
pass tests/threads/priority-condvar
pass tests/threads/priority-donate-chain
FAIL tests/vm/cow/cow-simple
53 of 141 tests failed.
Implement uninit_destroy
in vm/uninit.c
and anon_destroy
👉🏻uninit_destroy
의 경우, 메모리를 할당해주었던 aux를 free.