目录
1. 前言
本专题我们开始学习内存管理部分,本文为缺页中断处理相关学习笔记。本文主要参考了《奔跑吧, Linux内核》、ULA、ULK的相关内容。
前面概述部分主要介绍了arm64缺页中断的底层逻辑,本文主要以数据异常的do_page_fault为例来说明缺页的详细处理过程。主要介绍do_page_fault->handle_pte_fault的执行过程,handle_pte_fault主要采用vm_fault数据结构来管理很多参数,它主要通过vma首先判断addr所对应的pte是否为空,如果为空则进一步判断是匿名映射还是文件映射,并创建pte加入到pte table;如果不为空,则进一步根据pte页表项,触发页面换入、写时复制等
2. handle_pte_fault
handle_pte_fault(&vmf)
|--vmf->pte = pte_offset_map(vmf->pmd, vmf->address)
|--vmf->orig_pte = *vmf->pte
|--barrier()
|------//>>>>>>第1种情形:PTE为空
|--if (!vmf->pte)
| //第1.1种情形:vma是匿名映射线性区
| if (vma_is_anonymous(vmf->vma))
| return do_anonymous_page(vmf)//匿名映射处理
| //第1.2种情形:vma是文件映射线性区
| else
| return do_fault(vmf) //文件映射处理
|------//>>>>>>//第2种情形:PTE不为空
|--else
| //pte页表项PRESENT未置位,说明PTE还没有映射物理页面(没有页面或页面被交换出去)
| if (!pte_present(vmf->orig_pte))
| return do_swap_page(vmf) //从交换分区读回页面
| if (pte_protnone(vmf->orig_pte) && vma_is_accessible(vmf->vma))
| return do_numa_page(vmf);
| //如果是写内存触发的缺页异常
| if (vmf->flags & FAULT_FLAG_WRITE)
| if (!pte_write(entry)) //pte具有只读属性
| return do_wp_page(vmf) //写时复制(如父子进程共享的内存,一方需要写入时)
| entry = pte_mkdirty(entry)//pte可写时,标记为脏,可写为何会触发page fault?
| //标记pte项刚刚被访问过,以免页面被换出
| entry = pte_mkyoung(entry)
| //判断pte内容是否变化
\ if (ptep_set_access_flags(vmf->vma, vmf->address, vmf->pte, entry,
vmf->flags & FAULT_FLAG_WRITE))
update_mmu_cache(vmf->vma, vmf->address, vmf->pte)//刷新对应的TLB和高速缓存
handle_pte_fault主要采用vm_fault数据结构来管理很多参数,它主要通过vma首先判断addr所对应的pte是否为空,主要区分两种情形进行处理:
-
PTE为空:需要进一步区分是匿名映射还是文件映射发生的page fault,分别进行不同处理。
(1)do_anonymous_page
对于匿名映射则通过do_anonymous_page分别区分vma是只读和可写两种情况,分配物理页面,并设置pte到硬件页表;
(2)do_fault
对于文件映射则通过do_fault区分不同情况,总体是将文件内容读取到物理页面,并为此物理页面建立与缺页地址的映射关系
do_read_fault:缺页由读内存导致,do_read_fault将文件内容读取到vmf->page页面,并为此物理页面建立与缺页地址的映射关系
do_cow_fault:缺页由写内存导致,且是非共享映射。do_cow_fault分配vmf->page页面,并将文件内容读取到vmf->page页面,将vmf->page拷贝到vmf->cow_page,并为vmf->cow_page页面分配pte, 建立缺页地址与vmf->page页面的映射关系
do_shared_fault:缺页由写内存导致,且是共享映射。do_shared_fault将文件内容读取到vmf->page页面,并为此物理页面建立与缺页地址的映射关系 -
PTE不为空:需要进一步区分几种情况,如页面是否已经换出、是否是访问权限不匹配等分别作不同处理。
(1)do_numa_page
(2)do_swap_page
由于匿名物理页面被回收了,所以进程再次访问一块虚拟地址时,就会产生缺页中断,最终进入到 do_swap_page,在这个函数中会重新分配新的页面,然后再从swap分区读回这块虚拟地址对应的数据。
(3)do_wp_page
写时复制一般发生在父子进程之间,fork时copy_mm时会将父进程和子进程的页表项都设置为只读,无论是父进程或子进程执行写入都会触发page fault,通过此函数执行写时复制,分配新的page,拷贝旧page到新page, 并修改相应的页表项为读写。参数vmf->vma保存了线性区原有的读写权限
2.1 do_anonymous_page(匿名映射缺页中断)
do_anonymous_page(vmf)
| //防止共享的VMA进入匿名页面的缺页中断?
|--if (vma->vm_flags & VM_SHARED)
| return VM_FAULT_SIGBUS
| //分配一个pte,此时使用pte_offset_map不安全?
|--pte_alloc(vma->vm_mm, vmf->pmd)
|------//>>>>>>vma是只读的情况
|--if (!(vmf->flags & FAULT_FLAG_WRITE) && mm_forbids_zeropage(vma->vm_mm))
| //获取0页帧号,设置pte的special位
| entry = pte_mkspecial(pfn_pte(my_zero_pfn(vmf->address),vma->vm_page_prot))
| vmf->pte = pte_offset_map_lock(vma->vm_mm, vmf->pmd,vmf->address, &vmf->ptl)
| if (userfaultfd_missing(vma))
| return handle_userfault(vmf, VM_UFFD_MISSING)//传递page fault到用户空间
| goto setpte
|-------//>>>>>>vma可写的情况
|--page = alloc_zeroed_user_highpage_movable(vma, vmf->address) //分配可移动的匿名页面,底层通过alloc_page
| \--alloc_page_vma(GFP_HIGHUSER | movableflags,vma, vaddr)
|--mem_cgroup_charge(page, vma->vm_mm, GFP_KERNEL)
|--group_throttle_swaprate(page, GFP_KERNEL)
|--__SetPageUptodate(page)
|--entry = mk_pte(page, vma->vm_page_prot)//设置pte指向新分配的匿名页
|--entry = pte_sw_mkyoung(entry)
|--if (vma->vm_flags & VM_WRITE)
| entry = pte_mkwrite(pte_mkdirty(entry)) //设置页表项可写
|--vmf->pte = pte_offset_map_lock(vma->vm_mm, vmf->pmd, vmf->address,&vmf->ptl)//获取address的pte页表项地址
|--inc_mm_counter_fast(vma->vm_mm, MM_ANONPAGES)//递增进程的匿名页面数目anon_rss
|--page_add_new_anon_rmap(page, vma, vmf->address, false)//匿名页面添加到RMAP系统
|--lru_cache_add_inactive_or_unevictable(page, vma)
|--setpte:
|--set_pte_at(vma->vm_mm, vmf->address, vmf->pte, entry)//将pte页表项值entry设置到硬件page_table页表项
|--update_mmu_cache(vma, vmf->address, vmf->pte)
do_anonymous_page分别区分vma是只读和可写两种情况,分配物理页面,并设置pte到硬件页表
-
vma->vm_flags & VM_SHARED:防止共享的VMA进入匿名页面的缺页中断,发现是共享匿名映射,在缺页中断处理则退出?
-
pte_alloc:分配一个pte
-
vma是只读的情况:
(0)my_zero_pfn:获取0化页面的物理页帧号;
(1)pte_mkspecial:置pte的special位,表示这是一个特殊页面
(2)pte_offset_map_lock:获取address的pte页表项地址
(3)跳转到setpte
(4)set_pte_at:将pte页表项值entry设置到硬件page_table页表项
(5)update_mmu_cache:更新页表项的TLB cache
vma可写的情况:
(1)alloc_zeroed_user_highpage_movable:分配可移动的匿名页面,底层通过alloc_page
(2)mk_pte:设置pte指向新分配的匿名页
(3)pte_offset_map_lock:获取address的pte页表项地址
(4)page_add_new_anon_rmap:匿名页面添加到RMAP系统
(5)lru_cache_add_inactive_or_unevictable
(6)set_pte_at:将pte页表项值entry设置到硬件page_table页表项
(7)update_mmu_cache:更新页表项的TLB cache
page_add_new_anon_rmap(page, vma, vmf->address, false)
|--__SetPageSwapBacked(page)
|--atomic_set(&page->_mapcount, 0)/* increment count (starts at -1) */
|--__mod_lruvec_page_state(page, NR_ANON_MAPPED, nr)
\--__page_set_anon_rmap(page, vma, address, 1)
|--struct anon_vma *anon_vma = vma->anon_vma
|--if (PageAnon(page))
| return;
|--anon_vma = (void *) anon_vma + PAGE_MAPPING_ANON//最低位置1表示匿名映射
|--page->mapping = (struct address_space *) anon_vma
\--page->index = linear_page_index(vma, address)//保存了pte的索引
PageAnon: 通过page->mapping的bit0是否为1来判断是否为匿名页面, 为1则为匿名映射页,为0则为文件映射页,如果页面已经为匿名页说明已经将此页做过处理
关于page的mapping变量
page的mapping变量用于判断页是映射页还是匿名页。
(1)对于交换高速缓存页面,mapping指向交换分区的swapper_spaces的address_space对象
(2)对于匿名页面,mapping最低位是1,mapping保存vma->anon_vma的地址,anon_vma为匿名页所在线性区VMA链表的链表头
(3)对于文件映射页面,mapping最低位为0,mapping指向对应文件的address_space对象
(4) 对于KSM页面,mapping最低两位均为1
注:
a.mapping的bit0用于判断匿名页还是文件页
b.mapping的bit1用于判断是否为LRU页面
c.address_space对象地址在64bit系统RAM中按8字节对齐,所以最低位可以使用
d.PageAnon以页描述符为参数,返回mapping的最低位是否置1
下图可以看出page, 匿名线性区链表anon_vma, vma之间的关系
https://blog.csdn.net/u012489236/article/details/114734823
2.2 do_fault(文件映射缺页中断)
do_fault(struct vm_fault *vmf)
|--if (!vma->vm_ops->fault)
| //获取锁,防止获取的是pte的临时状态?
| vmf->pte = pte_offset_map_lock(vmf->vma->vm_mm,vmf->pmd,
| vmf->address,&vmf->ptl)
| //无效的pte
| if (unlikely(pte_none(*vmf->pte)))
| ret = VM_FAULT_SIGBUS;
| //有效的pte
| else
| ret = VM_FAULT_NOPAGE;//本次缺页不需要返回一个新的页面
| pte_unmap_unlock(vmf->pte, vmf->ptl)
| //缺页由读内存导致
|--else if (!(vmf->flags & FAULT_FLAG_WRITE))
| ret = do_read_fault(vmf)
| // 缺页由写内存导致,且是非共享映射
|--else if (!(vma->vm_flags & VM_SHARED))
| ret = do_cow_fault(vmf)//写时复制
| //缺页由写内存导致,且是共享映射
\--else
ret = do_shared_fault(vmf)
do_fault会区分不同情况,总体是将文件内容读取到物理页面,并为此物理页面建立与缺页地址的映射关系
-
pte_offset_map_lock:获取锁,防止获取的是pte的临时状态?其它CPU可能对pte进行读-修改-写入操作
-
do_read_fault:缺页由读内存导致,do_read_fault将文件内容读取到vmf->page页面,并为此物理页面建立与缺页地址的映射关系
-
do_cow_fault:缺页由写内存导致,且是非共享映射。do_cow_fault分配vmf->page页面,并将文件内容读取到vmf->page页面,将vmf->page拷贝到vmf->cow_page,并为vmf->cow_page页面分配pte, 建立缺页地址与vmf->page页面的映射关系
-
do_shared_fault:缺页由写内存导致,且是共享映射。do_shared_fault将文件内容读取到vmf->page页面,并为此物理页面建立与缺页地址的映射关系
2.2.1 do_read_fault
do_read_fault(vmf)
|--if (vma->vm_ops->map_pages && fault_around_bytes >> PAGE_SHIFT > 1)
| ret = do_fault_around(vmf)
| |--vmf->vma->vm_ops->map_pages(vmf, start_pgoff, end_pgoff)
|--__do_fault(vmf)
| |--ret = vma->vm_ops->fault(vmf)//将文件内容读取到vmf->page
| |--if (unlikely(!(ret & VM_FAULT_LOCKED)))
| lock_page(vmf->page) //为页面加锁?
|--ret |= finish_fault(vmf)
| |--page = vmf->page //获取缺页异常对应的物理页面
| |--alloc_set_pte(vmf, page) //分配pte, 建立虚拟地址与物理页面的映射关系
\--unlock_page(vmf->page);
do_read_fault将文件内容读取到vmf->page页面,并为此物理页面建立与缺页地址的映射关系
-
do_fault_around:若定义了vma->vm_ops->map_pages,可以在缺页异常地址附近提前映射尽可能多的页面(不会实际创建page cache),提高效率。fault_around_bytes是全局变量,默认65536字节,16个页面。do_fault_around以当前缺页地址为中心,通过vmf->vma->vm_ops->map_pages提前为缺页地址附近的页面映射pte,减少缺页中断发生的次数。
-
__do_fault:此处的vma->vm_ops->fault(vmf)会调用具体文件系统定义的fault回调(如f2fs为f2fs_filemap_fault),最终将调用filemap_fault,进而调用mapping->a_ops->readpage将文件内容读取到vmf->page页面
-
finish_fault:获取缺页异常对应的物理页面,并为此物理页面建立与缺页地址的映射关系
2.2.2 do_cow_fault
do_cow_fault(vmf)
|--struct vm_area_struct *vma = vmf->vma
|--vmf->cow_page = alloc_page_vma(GFP_HIGHUSER_MOVABLE, vma, vmf->address)//分配vmf->cow_page页面
|--ret = __do_fault(vmf)//将文件内容读取到vmf->page页面
|--copy_user_highpage(vmf->cow_page, vmf->page, vmf->address, vma)//将vmf->page拷贝到vmf->cow_page
|--__SetPageUptodate(vmf->cow_page)
|--ret |= finish_fault(vmf)//获取缺页异常对应的物理页面,并为此物理页面建立与缺页地址的映射关系
| |--page = vmf->cow_page //获取缺页异常对应的物理页面
| |--alloc_set_pte(vmf, page) //分配pte, 建立缺页地址与物理页面的映射关系
| |--page_add_new_anon_rmap(page, vma, vmf->address, false)//?
|--unlock_page(vmf->page) //解锁页面
\--put_page(vmf->page)
do_cow_fault:分配vmf->page页面,并将文件内容读取到vmf->page页面,并为vmf->page页面分配pte, 建立缺页地址与vmf->page页面的映射关系
-
alloc_page_vma:分配vmf->cow_page页面
-
__do_fault:将文件内容读取到vmf->page页面
-
copy_user_highpage:将vmf->page拷贝到vmf->cow_page
-
finish_fault:获取缺页异常对应的物理页面,并为此物理页面建立与缺页地址的映射关系
2.2.3 do_shared_fault
do_shared_fault(vmf)
|--ret = __do_fault(vmf)//将文件内容读取到vmf->page页面
|--if (vma->vm_ops->page_mkwrite)
| unlock_page(vmf->page)
| do_page_mkwrite(vmf) //通知用户空间,只读页面将变成可写
|--ret |= finish_fault(vmf) //获取缺页异常对应的物理页面,并为此物理页面建立与缺页地址的映射关系
| |--page = vmf->page //获取缺页异常对应的物理页面
| |--alloc_set_pte(vmf, page) //分配pte, 建立虚拟地址与物理页面的映射关系
| |--page_add_file_rmap(page, false)
\--ret |= fault_dirty_shared_page(vmf)
do_shared_fault将文件内容读取到vmf->page页面,并为此物理页面建立与缺页地址的映射关系
-
__do_fault:将文件内容读取到vmf->page页面
-
do_page_mkwrite(vmf) :通知用户空间,只读页面将变成可写
-
finish_fault:获取缺页异常对应的物理页面vmf->page,并为此物理页面建立与缺页地址的映射关系
下图可以看出page, 文件映射线性区rb_tree, vma之间的关系
from: https://blog.csdn.net/u012489236/article/details/114734823
2.3 do_numa_page
2.4 do_swap_page(页面换入)
swapin 的入口是do_swap_page,由于匿名物理页面被回收了,所以进程再次访问一块虚拟地址时,就会产生缺页中断,最终进入到 do_swap_page,在这个函数中会重新分配新的页面,然后再从swap分区读回这块虚拟地址对应的数据。
from:https://blog.csdn.net/qkhhyga2016/article/details/88722458
Linux swap机制是基于内存管理之下建立的,由于swap会对磁盘进行读写,所以设计上与page cache 相类似地建立了一个 swap cache 的缓存,提高swap的读写效率。swap core 是swap的核心层,主要完成管理各个swap分区,决策内存数据需要交换到哪个磁盘上,以及发起读写请求的工作。
swap分区的swapper_spaces[][]是一个全局数组,当用户通过 swapon 命令增加一个swap 分区时,会分配N个 struct address_space 变量,并加到全局数组变量swapper_spaces[][]中,一个struct address_space 管理的swap 空间大小是64M,即一个swap分区分成N个大小为64M的struct address_space,比如192M的swap 分区,在创建时被拆出了3个struct address_space,而全局数组变量swapper_spaces[][]的第一维元素是代表第几个swap分区,第二维存储的是该swap分区所有的64M大小的struct address_space
1.当内存管理模块需要回收一个匿名页面的时候,首先通过swap core 选择合适的swap 分区,并修改pte,指向swap分区的存储块位置,同时把该页面加入到 swap cache 缓存起来,然后再通过swap core 发起回写请求进行回写,等待回写结束后,内存管理模块释放该页面。
2.当用户需要访问处于swap分区的数据时,首先先通过内存管理模块确定pte的swap entry,然后在swap cache 中快速地查找swap entry 对应的物理页面,如果这时物理页面仍末被回收,就能找到对应的页面,则直接修改pte,指向该page,重新建立映射,如果没找到对应的页面,表明物理页面已经被回收了,则在swap core 中通过 swap entry 查找对应的swap 分区和数据地址,最后申请一个page并发起读操作(swapin),等待读操作完成后,修改pte,指向该page,重新建立映射
3.swapin 的入口是do_swap_page,由于匿名物理页面被回收了,所以进程再次访问一块虚拟地址时,就会产生缺页中断,最终进入到 do_swap_page,在这个函数中会重新分配新的页面,然后再从swap分区读回这块虚拟地址对应的数据。
2.5 do_wp_page(写时复制)
do_wp_page(vmf)
|--struct vm_area_struct *vma = vmf->vma
|--if (userfaultfd_pte_wp(vma, *vmf->pte))
| pte_unmap_unlock(vmf->pte, vmf->ptl)
| return handle_userfault(vmf, VM_UFFD_WP)
|--vmf->page = vm_normal_page(vma, vmf->address, vmf->orig_pte)
| //----处理特殊映射的写时复制------
| //page为NULL说明是一个特殊映射参考《内存管理基础-3.2 进程地址空间-brk系统调用》
|--if (!vmf->page)
| if ((vma->vm_flags & (VM_WRITE|VM_SHARED)) == //共享可写页面
| (VM_WRITE|VM_SHARED))
| return wp_pfn_shared(vmf)//处理可写并且共享的特殊映射页面
| else //非共享可写页面
| pte_unmap_unlock(vmf->pte, vmf->ptl)
| return wp_page_copy(vmf)//处理写时复制
| // -------处理普通映射的写时复制------
|--if (PageAnon(vmf->page)) //匿名页面
| struct page *page = vmf->page
| unlock_page(page)
| wp_page_reuse(vmf)//处理可以复用的页面
| return VM_FAULT_WRITE
|--else if (unlikely((vma->vm_flags & (VM_WRITE|VM_SHARED))== //共享可写页面
| (VM_WRITE|VM_SHARED)))
| return wp_page_shared(vmf)//处理可写的并且共享的普通映射页面
\--else //文件映射,非共享可写页面
return wp_page_copy(vmf)//处理写时复制
写时复制一般发生在父子进程之间,fork时copy_mm时会将父进程和子进程的页表项都设置为只读,无论是父进程或子进程执行写入都会触发page fault,通过此函数执行写时复制,分配新的page,拷贝旧page到新page, 并修改相应的页表项为读写。参数vmf->vma保存了线性区原有的读写权限
-
wp_pfn_shared:处理可写并且共享的特殊映射页面(包括VM_MIXEDMAP或VM_PFNMAP页面),并调用wp_page_reuse函数来复用缺页异常页面
-
wp_page_reuse:处理可以复用的页面
-
wp_page_copy:处理写时复制,分配新的page,拷贝旧页面到新页面,并根据vm_page_prot生成新的pte,添加到硬件页表
-
wp_page_shared:处理可写的并且共享的普通映射页面,并调用wp_page_reuse函数来复用缺页异常页面
2.5.1 wp_page_reuse
wp_page_reuse(vmf)
|--struct vm_area_struct *vma = vmf->vma
|--struct page *page = vmf->page
|--pte_t entry
|--flush_cache_page(vma, vmf->address, pte_pfn(vmf->orig_pte))//flush缺页异常页面的cache
|--entry = pte_mkyoung(vmf->orig_pte)//设置pte的PTE_AF bit
|--entry = maybe_mkwrite(pte_mkdirty(entry), vma)
|--if (ptep_set_access_flags(vma, vmf->address, vmf->pte, entry, 1))// 设置新的pte到页表
| update_mmu_cache(vma, vmf->address, vmf->pte)
\--pte_unmap_unlock(vmf->pte, vmf->ptl)
wp_page_reuse复用缺页异常页面,通过修改原页面的pte由PTE_RDONLY变为PTE_WRITE,并重新设置到页表
-
pte_mkyoung:设置pte的PTE_AF bit
-
pte_mkdirty: 如果pte具有PTE_WRITE属性,清除PTE_RDONLY
-
maybe_mkwrite:如果vma具有VM_WRITE属性,则对pte设置PTE_WRITE属性,清除PTE_RDONLY
-
ptep_set_access_flags:将新的pte设置到页表
2.5.2 wp_page_copy
static vm_fault_t wp_page_copy(struct vm_fault *vmf)
|--struct vm_area_struct *vma = vmf->vma;
| struct mm_struct *mm = vma->vm_mm;
| struct page *old_page = vmf->page;
| struct page *new_page = NULL;
|--if (unlikely(anon_vma_prepare(vma)))
| goto oom;
|--if (is_zero_pfn(pte_pfn(vmf->orig_pte))) //pte映射到0化页面
| new_page = alloc_zeroed_user_highpage_movable(vma,vmf->address)//分配一个全0的page
|--else //pte映射到非0化页面
| new_page = alloc_page_vma(GFP_HIGHUSER_MOVABLE, vma,vmf->address) //分配一个新的页面
| cow_user_page(new_page, old_page, vmf) //拷贝内容给新分配的页面
|--__SetPageUptodate(new_page)//表示新页面内容有效
|--mmu_notifier_range_init(&range, MMU_NOTIFY_CLEAR, 0, vma, mm,...)//初始化mm_notiifer告知系统oldpage将无效?
|--mmu_notifier_invalidate_range_start(&range)
| //由于分配页会导致进程阻塞,因此检测pte是否发生改变
|--vmf->pte = pte_offset_map_lock(mm, vmf->pmd, vmf->address, &vmf->ptl)
|--if (likely(pte_same(*vmf->pte, vmf->orig_pte))) //pte没有发生改变
| 根据old_page页面是匿名还是文件映射,增加或减少相应的计数
| entry = mk_pte(new_page, vma->vm_page_prot) //根据新页和vm_page_prot生成新的pte
| entry = pte_sw_mkyoung(entry)
| //copy_mm时会将父子进程的页表项设置为只读,此处是恢复页表项原有的读写权限?
| entry = maybe_mkwrite(pte_mkdirty(entry), vma);//设置PTE_WRITE,清PTE_RDONLY
| ptep_clear_flush_notify(vma, vmf->address, vmf->pte)//读取并清空pte,之后flush tlb
| page_add_new_anon_rmap(new_page, vma, vmf->address, false)//添加到RMAP系统
| lru_cache_add_inactive_or_unevictable(new_page, vma) //添加到活跃LRU链表
| set_pte_at_notify(mm, vmf->address, vmf->pte, entry)//新的 pte添加到硬件页表
| update_mmu_cache(vma, vmf->address, vmf->pte) //刷新TLB
| page_remove_rmap(old_page, false) //将旧页从rmap系统删除
| new_page = old_page
|--else //pte发生了改变
| update_mmu_tlb(vma, vmf->address, vmf->pte)//刷新tlb
|--put_page(old_page) //将旧页释放,表示当前进程不在拥有old_page
wp_page_copy分配新的page,拷贝旧页面到新页面,并根据vm_page_prot生成新的pte,添加到硬件页表
-
alloc_zeroed_user_highpage_movable/alloc_page_vma: 根据pte是否指向0化页面来决定是否分配全0的page;
-
cow_user_page:拷贝旧页面内容给新分配的页面
-
__SetPageUptodate: 表示新页面内容有效
-
mmu_notifier_range_init: 初始化mm_notiifer告知系统old_page将无效?
-
pte_offset_map_lock: 由于前面分配页会导致进程阻塞,因此检测pte是否发生改变
-
根据old_page页面是匿名还是文件映射,增加或减少相应的计数
-
mk_pte:根据新页和vm_page_prot生成新的pte
-
maybe_mkwrite:以fork为例,copy_mm时会将父子进程的页表项设置为只读,此处是恢复页表项原有的读写权限?
-
page_add_new_anon_rmap:新页添到RMAP系统
-
lru_cache_add_inactive_or_unevictable:新页添加到活跃LRU链表
-
page_remove_rmap:将旧页从rmap系统删除
-
put_page:将旧页释放,表示当前进程不在拥有old_page
参考文档
- linux内存管理笔记(三十八)----反向映射
- 奔跑吧,Linux内核