fork()
時會把整個 page table 也複製給 child, 但是大多數時候,child 並不會使用這個 page table
常常都是在 fork()
去執行 exec()
這時 exec()
又會把原本的 page table 蓋掉, 這樣太浪費 physical memory 了,
比較好的作法是:fork()
時先讓 parent 跟 child 都指向同樣的 pa , 直到要寫入時,才真正的把 physical memory 的內容複製一份
fork()
使用 uvmcopy()
把 page table 複製過去
fork()
後 child 跟 parent 的 page table 都是 map 到相同的 pa現在的 uvmcopy()
就是把 old
的東西複製到 new
根據題目的需求,我們必須要做到以下幾件事情:
new
跟 old
的 pte_w
都關掉,這樣想要寫入的時候,就會進入 trap() 處理new
跟 old
都要立起 pte_c
,這樣在 trap() 中就可以認出真正的 write page faultpte_c
fork()
fork()
使用 uvmcopy()
把 child 的 page table 建立出來,fork()
結束之後,child 跟 parent 會得到相同的 virtual memoryuvmcopy()
fork()
使用 uvmcopy()
把 parent 的 page table 複製一份給 child
uvmcopy()
: parent 的 memory 內容真正的複製出來 (使用了 kalloc()
)在 kernel/trap.c: usertrap()
(r_scause() == 15
)
pte_c
off: 真正的 page faultpte_c
on: 準備執行 copy on write
kalloc()
與 kfree()
原版執行 kfree()
的時候就是真的把這個 page free 掉了
可是 cow 的版本中會有問題
使用 cow 了話,有可能會有很多個 page table 指向同一個 page,
而這時候什麼時候要 free 掉這個 page 就成為了一個很大的問題
因為其中一個 process 要 free 掉這個 page 時,這個 page 可能正同時被其他 process 使用
解決方法:使用 counter 去計算各個 page 被多少個 process 使用,如果沒有任何 process 使用,
才需要真正的 free 掉這個 page
uvmcopy()
中,刪掉 child 以及 parent 的所有 pte_w
pte_w
所記載的資訊去哪了kalloc()
出一個新的 page 並且把 old page 的內容複製到 new page,並且 set pte_w
copyout()
中使用跟 page fault 時一樣的方法
uvmcopy()
:usertrap()
:uncopied_cow()
: 還沒有被 copy 出來的 cow pagecow_alloc()
:copyout()
:
- modify
uvmcopy()
to map the parent's physical pages into the child, instead of allocating new pages. clearpte_w
in the ptes of both child and parent for pages that havepte_w
set.
uvmcopy()
, 讓 fork()
使用 uvmcopy()
複製 parent 的 va 內容給 child 時page_table
map 到 parent 的 physical page讓 usertrap()
有辦法分辨真正的 write page fault 與 copy-on-write
PTE_C
PTE_W
on 的 page:
PTE_W
變成 offPTE_C
變成 onPTE_W
off 的 page
PTE_W
維持原本的 offPTE_W
維持原本的 off如此一來 usertrap()
遇到 r_scause() ==並且
PTE_C 為 on 15
,
就進入 copy-on-write 的情形處理
- modify
usertrap()
to recognize page faults. when a write page-fault occurs on a cow page that was originally writeable, allocate a new page with kalloc(), copy the old page to the new page, and install the new page in the pte withpte_w
set. pages that were originally read-only (not mappedpte_w
, like pages in the text segment) should remain read-only and shared between parent and child; a process that tries to write such a page should be killed.
修改 userteap()
kernel/trap.c: usertrap()
:kernel/vm.c: uncopied_cow()
kernel/vm.c: cow_alloc()
- ensure that each physical page is freed when the last pte reference to it goes away – but not before. a good way to do this is to keep, for each physical page, a "reference count" of the number of user page tables that refer to that page. set a page's reference count to one when kalloc() allocates it. increment a page's reference count when fork causes a child to share the page, and decrement a page's count each time any process drops the page from its page table. kfree() should only place a page back on the free list if its reference count is zero. it's ok to to keep these counts in a fixed-size array of integers. you'll have to work out a scheme for how to index the array and how to choose its size. for example, you could index the array with the page's physical address divided by 4096, and give the array a number of elements equal to highest physical address of any page placed on the free list by kinit() in kalloc.c. feel free to modify kalloc.c (e.g., kalloc() and kfree()) to maintain the reference counts.
因為一個已分配的 memory 可能會被多個 process 使用,我們必須要紀錄這個 page 有多少個 process 使用才行,如果使用人數為 0 時,才要真正的把這個 page 刪除掉。
kinit()
時初始化kalloc()
時增加kfree()
時增加複習 xv6 的 memory layout
kinit()
可以看到,kalloc()
可以使用的範圍就存放在 end
~ phystop
end
: 從 kernel/kernel.ld
可以得知他被存放在 kernel 的 .text
與 .data
之後
end
的位置不太確定,反正 linker 會幫我們弄好end
是 free memory 的開始kalloc()
可以使用的範圍phystop
: phycical address 的實際上的最大的 address = 0x88000000從 end
與 phystop
先定義一些 macro
kfree()
kalloc()
init
kernel/kalloc.c
:
- modify copyout() to use the same scheme as page faults when it encounters a cow page.
copy 出去的時候,不要指到跟 kernel 一樣的 pa, (要自己 copy 一份給 user mode 使用)
kernel/vm.c: copyout()
:freelist
並沒有被正確的初始化
pa2cnt(p) = 1
in freerangepanic: mappages: remap
出現了 panic: mappages: remap
的錯誤
照裡來說不應該出現 remap 的情況
cow_alloc()
由 usertrap()
遇到 write page fault 時呼叫
在 cow_alloc()
的情況下應該要
在 cow_alloc()
中的 uvmunmap()
必須要 do_free
這樣
先用執行
cowtests
來發現錯誤會比較容易
pipe() failed
來看看是為什麼
sys_pipe()
中的 copyout()
發生了錯誤filetest()
的流程cowalloc()
in copyout()
6.1810
lab cow
mit 6.s081 xv6 lab6 cow 实验记录
https://five-embeddev.com/riscv-isa-manual/latest/supervisor.html#sec:scause
11分鐘讀懂 linker scripts