linux 写时复制 COW 过程梳理
来源:互联网 发布:控制usb hub端口 编辑:程序博客网 时间:2024/04/28 22:08
最后一次谈到缺页,是在一年多以前,http://blog.csdn.net/chenyu105/article/details/7061845
那时结个了草率的尾,定格在了handle_pte_fault,留下一句:I will be back
为避免来回翻阅,将handle_mm_fault再次粘上:
这里介绍一下pmd_alloc(mm, pud, address)以及pte_alloc_map(mm, pmd, address)
执行pmd_alloc的流程:如果pmd表不存在,则调用__pmd_alloc创建此pmd表,
并将此pmd表的所有成员都指向invalid_pte_table,最后返回vaddr对应的pmd entry指针(指向一个pte表,当然可能是invalid)。
执行pte_alloc_map(mm, pmd, address)的流程:如果pte表是invalid,则分配一个pte表,并返回pte表中此
应的pte entry值。这个pte entry 的值,要么是0(未访问过时),要么是有效的页框号pfn。
接着将此pte指针传入handle_pte_fault函数,表示要对此页做进一步处理。
如果pte entry的值是0,则pte_present(entry)返回0,表示此页框不在内存中。
页不在内存中,有两种情况,一种是因为还没有分配页框;另外一种是分配了页框,但是页面数据
被交换到磁盘上。第一种情况,即我们常说的请求调页,通常是由于第一次访问了mmap的地址引起。
有了上面的铺垫,我们来看看写时复制是个什么情况。
COW的原因有两种,一种是访问mmap(MAP_PRIVATE,PROT_READ,fd)空间,另外一种是访问fork出来的进程空间。
现在来看mmap(MAP_PRIVATE)的流程。
MAP_PRIVATE的意思是写时复制COW,man mmap里对此的解释是
上面这段话最重要的一句,就是在第一次写空间的时候出发拷贝。
因此,隐含的两个要点就是,
1) 触发写时复制的时机是在缺页时,而不是mmap时
2) 缺页时如何区分写时复制引起的异常,答案是在缺页异常里,如果发现是写操作,并且对MAP_PRIVATE的
vma执行的操作,那么就认为是一个写时复制引起的异常,需要进一步做拷贝。
在第一次访问MAP_PRIVATE出来的空间时,走请求调页,对于MAP_PRIVATE标志,文件映射和共享内存映射才有
效,而匿名映射无效,因此走do_linear_fault
以后再访问这块虚拟地址,就访问的是原数据的拷贝了,这就是MAP_PRIVATE设计的初衷。
再来看fork时的cow。 fork时,会把父进程页表的属性改为只读,这样下次访问就会产生异常。
具体是在copy_mm的copy_one_pte里,有一句判断区间是否为cow属性(即是否可写,可写的话就去掉写属性)
如果是的话,就去掉写属性。
之后的缺页,根据handle_pte_fault,因为满足pte_present(entry)(页在内存里,缺页是因为权限问题)
所以尝试检查是否COW引起,条件是对不可写空间执行了写操作
最后走cow流程即do_wp_page
那时结个了草率的尾,定格在了handle_pte_fault,留下一句:I will be back
为避免来回翻阅,将handle_mm_fault再次粘上:
int handle_mm_fault(struct mm_struct *mm, struct vm_area_struct *vma,unsigned long address, unsigned int flags){pgd_t *pgd;pud_t *pud;pmd_t *pmd;pte_t *pte;if (unlikely(is_vm_hugetlb_page(vma)))return hugetlb_fault(mm, vma, address, flags);pgd = pgd_offset(mm, address);pud = pud_alloc(mm, pgd, address);pmd = pmd_alloc(mm, pud, address);pte = pte_alloc_map(mm, pmd, address);return handle_pte_fault(mm, vma, address, pte, pmd, flags);}
这里介绍一下pmd_alloc(mm, pud, address)以及pte_alloc_map(mm, pmd, address)
执行pmd_alloc的流程:如果pmd表不存在,则调用__pmd_alloc创建此pmd表,
并将此pmd表的所有成员都指向invalid_pte_table,最后返回vaddr对应的pmd entry指针(指向一个pte表,当然可能是invalid)。
执行pte_alloc_map(mm, pmd, address)的流程:如果pte表是invalid,则分配一个pte表,并返回pte表中此
address对应的pte_t指针,其值is hoped to filled by page frame number, pfn,
但很多时候,新分配出来的pte表是全0的一个page,因此他的成员pte entry也就是0了,即*(pte_t) = 0
可以看出,在走到handle_pte_fault时,页表已建好对应vaddr的从pgd->pmd->pud->pte的路径,并搜索出对应的pte entry值。这个pte entry 的值,要么是0(未访问过时),要么是有效的页框号pfn。
接着将此pte指针传入handle_pte_fault函数,表示要对此页做进一步处理。
static inline int handle_pte_fault(struct mm_struct *mm,struct vm_area_struct *vma, unsigned long address,pte_t *pte, pmd_t *pmd, unsigned int flags){pte_t entry;entry = *pte;if (!pte_present(entry)) {if (pte_none(entry)) {if (vma->vm_ops) {if (likely(vma->vm_ops->fault))return do_linear_fault(mm, vma, address,pte, pmd, flags, entry);}return do_anonymous_page(mm, vma, address, pte, pmd, flags);}if (pte_file(entry))return do_nonlinear_fault(mm, vma, address,pte, pmd, flags, entry);return do_swap_page(mm, vma, address,pte, pmd, flags, entry);}if (flags & FAULT_FLAG_WRITE) {if (!pte_write(entry))return do_wp_page(mm, vma, address,pte, pmd, ptl, entry);entry = pte_mkdirty(entry);}flush_tlb_page(vma, address);}
如果pte entry的值是0,则pte_present(entry)返回0,表示此页框不在内存中。
页不在内存中,有两种情况,一种是因为还没有分配页框;另外一种是分配了页框,但是页面数据
被交换到磁盘上。第一种情况,即我们常说的请求调页,通常是由于第一次访问了mmap的地址引起。
有了上面的铺垫,我们来看看写时复制是个什么情况。
COW的原因有两种,一种是访问mmap(MAP_PRIVATE,PROT_READ,fd)空间,另外一种是访问fork出来的进程空间。
现在来看mmap(MAP_PRIVATE)的流程。
MAP_PRIVATE的意思是写时复制COW,man mmap里对此的解释是
The MAP_SHARED and MAP_PRIVATE options describe the disposition of write references to the underlying object. If MAP_SHARED is specified, write references will change the memory object. If MAP_PRIVATE is specified, the initial write reference will create a private copy of the memory object page and redirect the mapping to the copy. The private copy is not created until the first write; until then, other users who have the object mapped MAP_SHARED can change the object. Either MAP_SHARED or MAP_PRIVATE must be specified, but not both. The mapping type is retained across fork(2).
上面这段话最重要的一句,就是在第一次写空间的时候出发拷贝。
因此,隐含的两个要点就是,
1) 触发写时复制的时机是在缺页时,而不是mmap时
2) 缺页时如何区分写时复制引起的异常,答案是在缺页异常里,如果发现是写操作,并且对MAP_PRIVATE的
vma执行的操作,那么就认为是一个写时复制引起的异常,需要进一步做拷贝。
在第一次访问MAP_PRIVATE出来的空间时,走请求调页,对于MAP_PRIVATE标志,文件映射和共享内存映射才有
效,而匿名映射无效,因此走do_linear_fault
static int do_linear_fault(struct mm_struct *mm, struct vm_area_struct *vma,unsigned long address, pmd_t *pmd,pgoff_t pgoff, unsigned int flags, pte_t orig_pte){ret = vma->vm_ops->fault(vma, &vmf); //分配一个新页面/* * 根据注释,满足下面1,2条件时,会进行cow */if (flags & FAULT_FLAG_WRITE) { //1.如果是写操作引起的异常if (!(vma->vm_flags & VM_SHARED)) { //2. 此空间是MAP_PRIVATE标志page = alloc_page_vma(vma, address); //分配新页copy_user_highpage(page, vmf.page, address, vma);//将原页数据拷贝到新页 }entry = mk_pte(page, vma->vm_page_prot); //按新页生成pte entry//将此页置可写,以避免其他路径也触发COW if (flags & FAULT_FLAG_WRITE)entry = maybe_mkwrite(pte_mkdirty(entry), vma); set_pte_at(mm, address, page_table, entry); //设置到页表}
以后再访问这块虚拟地址,就访问的是原数据的拷贝了,这就是MAP_PRIVATE设计的初衷。
再来看fork时的cow。 fork时,会把父进程页表的属性改为只读,这样下次访问就会产生异常。
具体是在copy_mm的copy_one_pte里,有一句判断区间是否为cow属性(即是否可写,可写的话就去掉写属性)
if (is_cow_mapping(vm_flags)) {ptep_set_wrprotect(src_mm, addr, src_pte);pte = pte_wrprotect(pte);}
如果是的话,就去掉写属性。
之后的缺页,根据handle_pte_fault,因为满足pte_present(entry)(页在内存里,缺页是因为权限问题)
所以尝试检查是否COW引起,条件是对不可写空间执行了写操作
if (flags & FAULT_FLAG_WRITE) {if (!pte_write(entry))
最后走cow流程即do_wp_page
- linux 写时复制 COW 过程梳理
- String COW 写时复制
- COW技术(写时复制技术)
- 50-写时复制COW机制
- 关于linux内核fork后cow(写时复制)的代码分析
- 关于linux内核fork后cow(写时复制)的代码分析
- linux进程地址空间(2) 缺页异常详解(3)写时复制COW详解
- 数组指针特例-写时复制cow(copy on write)
- String系列——写时复制COW
- PHP内核探索:写时复制COW机制
- linux的写时复制
- linux 写时复制 copyonwrite
- Linux写时复制技术
- linux写时复制技术
- Linux写时拷贝技术(copy-on-write)--COW
- [linux 0.11]写时复制的实现
- 关于Linux下的写时复制
- Linux/UNIX写时复制技术
- 别把用户的高期望混同于好体验
- 动机和机会:推动商业发展的引擎
- jsp网页中的按钮点击跳转事件
- 解决win8总提示无.NETFRAMEWORK问题
- openstack删除卷很慢的问题
- linux 写时复制 COW 过程梳理
- PHP Extension开发基础
- Ubuntu常用命令大全
- IOS QQ2013内测版破解方法
- 4个信号表明你是一个失败的领导
- PHP哈希表碰撞攻击原理
- 成功谈判 你需要几个锦囊?
- oracle table()函数用法
- 一个人的宽度决定了他的高度