- 论坛徽章:
- 0
|
COW的整个过程
Copy on write:
设计思路:当要拷贝一个虚拟地址A的可写的页面的时候, 由于lazy copy的原则,我们不想实际的区分配页面并且拷贝页面内容,尤其是不确定即使拷贝了,以后不一定要写访问那里的数据。 所以我们的做法是,将页相的属性值pte设为不可写, 然后当有进程再次往该虚拟地址写数据的时候,就出发页异常,然后由页面异常来处理,
分配页面初始化并且拷贝原页的内容。
Eg:linux在fork的时候拷贝父进程的资源的时候。具体在copy_page_range()中。
int copy_page_range(struct mm_struct *dst,
struct mm_struct *src,
struct
vm_area_struct *vma)
此函数的目的是将父进程的src中一个vm_area_struct 拷贝到另外一个进程dst 中。
简单的来说,当拷贝src中一个可写的虚拟地址A的时候:
假设现在A指向物理地址 phad
1:先根据dst来确定是否新的进程的页表已经ok。如果没有,还需要pmd_alloc(),pte_alloc()来分配和建立页表对应关系。极端情况下需要另外分配一个页面(此过程中有可能会调用get_free_pages, 如果是pte_alloc_one()的话,还要将整个页面清0 ),由确定的页目录表,页面表就已经确定了一个新进程的虚拟地址B。
2:dst中B指向的页面表项,和src中A指向的页面表项都设置成只读,
即仅仅拷贝页面指针和属性。(这时不同的虚拟地址指向相同的物理地址, A ,B都指向 phad)
3:over。 哈哈就这么简单
当以后新进程需要向B写入数据的时候,由B去找pte项
发现pte的值是只读的就会触发页面异常。进入do_page_fault()。
此时的异常处理就要看error_code的值是多少了。
/* error_code:
* bit 0 == 0 means no page found, 1 means
protection fault
* bit 1 == 0 means read, 1 means write
* bit 2 == 0 means kernel, 1 means user-mode
*/
我们假设页面是在内存中的,仅仅是由于写保护引起的异常。
那么会在do_page_fault()的检查error_code & 3 中转到handle_mm_fault()中去。代码中有段注释
/*
* We need the page table lock to synchronize
with kswapd
* and the SMP-safe atomic PTE updates.
*/
要求主要处理过程handle_pte_fault()要在一个spin_lock的保护下进行。那么我们深入进去看看到底是怎么一个处理过程。
static inline int handle_pte_fault(struct
mm_struct *mm,
struct
vm_area_struct * vma, unsigned long address,
int
write_access, pte_t * pte)
{
pte_t
entry;
entry
= *pte;
if
(!pte_present(entry)) {
/*
* If it truly wasn't present, we know that
kswapd
* and the PTE updates will not touch it later.
So
* drop the lock.
*/
if
(pte_none(entry))
return
do_no_page(mm, vma, address, write_access, pte);
return
do_swap_page(mm, vma, address, pte, entry, write_access);
}
*********@********
if
(write_access) {
if
(!pte_write(entry))
return
do_wp_page(mm, vma, address, pte, entry);
entry
= pte_mkdirty(entry);
}
entry
= pte_mkyoung(entry);
establish_pte(vma,
address, pte, entry);
spin_unlock(&mm->page_table_lock);
return
1;
}
在我们的写保护情景中, 会直接跑到*********@********处,if (write_access)为真,我们的pte的内容是不允许写的,因此if (!pte_write(entry))也为真。进入do_wp_page()。
进而会调用copy_cow_page()。
copy_cow_page里面
if
(from == ZERO_PAGE(address)) {
clear_user_highpage(to,
address);
return;
}
copy_user_highpage(to,
from, address);
如果不是ZERO_PAGE的话, 那么在copy_user_highpage()中调用copy_user_page()来进行真正的拷贝。但是令人迷惑的是在此函数中
void mmx_copy_page(void *to, void *from)
{
if(in_interrupt())
slow_copy_page(to,
from);
else
fast_copy_page(to,
from);
}
在中断里反而调用slow_copy_page, 而之外调用fast_copy_page??? 为什么呢?不能理解。(不过google一下,好像fast_copy_page里面有很多prefetch,好像在kernel的时候有问题,所以一般中断都在kernel space中所以就不用了)
本文来自ChinaUnix博客,如果查看原文请点:http://blog.chinaunix.net/u2/69528/showart_1814115.html |
|