目录

  • 1. 前言
  • 2. handle_pte_fault
    • 2.1 do_anonymous_page(匿名映射缺页中断)
    • 2.2 do_fault(文件映射缺页中断)
      • 2.2.1 do_read_fault
      • 2.2.2 do_cow_fault
      • 2.2.3 do_shared_fault
    • 2.3 do_numa_page
    • 2.4 do_swap_page(页面换入)
    • 2.5 do_wp_page(写时复制)
      • 2.5.1 wp_page_reuse
      • 2.5.2 wp_page_copy
  • 参考文档

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是否为空,主要区分两种情形进行处理:

  1. 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页面,并为此物理页面建立与缺页地址的映射关系

  2. 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到硬件页表

  1. vma->vm_flags & VM_SHARED:防止共享的VMA进入匿名页面的缺页中断,发现是共享匿名映射,在缺页中断处理则退出?

  2. pte_alloc:分配一个pte

  3. 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会区分不同情况,总体是将文件内容读取到物理页面,并为此物理页面建立与缺页地址的映射关系

  1. pte_offset_map_lock:获取锁,防止获取的是pte的临时状态?其它CPU可能对pte进行读-修改-写入操作

  2. do_read_fault:缺页由读内存导致,do_read_fault将文件内容读取到vmf->page页面,并为此物理页面建立与缺页地址的映射关系

  3. do_cow_fault:缺页由写内存导致,且是非共享映射。do_cow_fault分配vmf->page页面,并将文件内容读取到vmf->page页面,将vmf->page拷贝到vmf->cow_page,并为vmf->cow_page页面分配pte, 建立缺页地址与vmf->page页面的映射关系

  4. 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页面,并为此物理页面建立与缺页地址的映射关系

  1. 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,减少缺页中断发生的次数。

  2. __do_fault:此处的vma->vm_ops->fault(vmf)会调用具体文件系统定义的fault回调(如f2fs为f2fs_filemap_fault),最终将调用filemap_fault,进而调用mapping->a_ops->readpage将文件内容读取到vmf->page页面

  3. 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页面的映射关系

  1. alloc_page_vma:分配vmf->cow_page页面

  2. __do_fault:将文件内容读取到vmf->page页面

  3. copy_user_highpage:将vmf->page拷贝到vmf->cow_page

  4. 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页面,并为此物理页面建立与缺页地址的映射关系

  1. __do_fault:将文件内容读取到vmf->page页面

  2. do_page_mkwrite(vmf) :通知用户空间,只读页面将变成可写

  3. 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保存了线性区原有的读写权限

  1. wp_pfn_shared:处理可写并且共享的特殊映射页面(包括VM_MIXEDMAP或VM_PFNMAP页面),并调用wp_page_reuse函数来复用缺页异常页面

  2. wp_page_reuse:处理可以复用的页面

  3. wp_page_copy:处理写时复制,分配新的page,拷贝旧页面到新页面,并根据vm_page_prot生成新的pte,添加到硬件页表

  4. 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,并重新设置到页表

  1. pte_mkyoung:设置pte的PTE_AF bit

  2. pte_mkdirty: 如果pte具有PTE_WRITE属性,清除PTE_RDONLY

  3. maybe_mkwrite:如果vma具有VM_WRITE属性,则对pte设置PTE_WRITE属性,清除PTE_RDONLY

  4. 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,添加到硬件页表

  1. alloc_zeroed_user_highpage_movable/alloc_page_vma: 根据pte是否指向0化页面来决定是否分配全0的page;

  2. cow_user_page:拷贝旧页面内容给新分配的页面

  3. __SetPageUptodate: 表示新页面内容有效

  4. mmu_notifier_range_init: 初始化mm_notiifer告知系统old_page将无效?

  5. pte_offset_map_lock: 由于前面分配页会导致进程阻塞,因此检测pte是否发生改变

  6. 根据old_page页面是匿名还是文件映射,增加或减少相应的计数

  7. mk_pte:根据新页和vm_page_prot生成新的pte

  8. maybe_mkwrite:以fork为例,copy_mm时会将父子进程的页表项设置为只读,此处是恢复页表项原有的读写权限?

  9. page_add_new_anon_rmap:新页添到RMAP系统

  10. lru_cache_add_inactive_or_unevictable:新页添加到活跃LRU链表

  11. page_remove_rmap:将旧页从rmap系统删除

  12. put_page:将旧页释放,表示当前进程不在拥有old_page

参考文档

  1. linux内存管理笔记(三十八)----反向映射
  2. 奔跑吧,Linux内核

内存管理基础学习笔记 - 4.3 缺页中断处理 - handle_pte_fault相关推荐

  1. 内存管理基础学习笔记 - 3.2 进程地址空间 - brk系统调用

    目录 1. 前言 2. SYSCALL_DEFINE1(brk, unsigned long, brk) |- -__do_munmap |- -do_brk_flags |- - -get_unma ...

  2. 内存管理基础学习笔记 - 3.1 进程地址空间 - VMA线性区

    目录 1. 前言 2. 进程地址空间 3. 用户空间与内核空间的隔离 4. 进程地址空间主要数据结构 struct mm_struct struct vm_area_struct 5. vma的标志属 ...

  3. 内存管理基础学习笔记 - 5.2 页面回收 - kswapd

    目录 1. 前言 2. kswapd_init 3.kswapd |- -balance_pgdat |- - -pgdat_balanced |- - -kswapd_shrink_node |- ...

  4. 内存管理基础学习笔记 - 2. 内核地址空间 - SLAB

    目录 1. 前言 2. slab总体说明 3. kmem_cache_create |- -__kmem_cache_create |- - -setup_cpu_cache 4. kmem_cach ...

  5. 《深入理解LINUX内存管理》学习笔记(一)

    引子 为什么要写这个笔记: 1,这本书的中文版翻译了太垃圾,没法阅读.阅读英文原版,可以很好的理解作者的思路.作此笔记备忘 2,一直以来学习LINUX kernel的知识缺乏系统化,借对这本书的学习, ...

  6. Arm V8内存管理架构.学习笔记

    目录 第1章 分级存储架构 1.1基础认识 1.1.1 从数据通路描述 1.1.2 从数据交换单位描述 1.1.3 Cache数据一致性拓扑结构 1.2 系统层内存模型 1.2.1 内存属性 1.2. ...

  7. RT-Thread 静态内存管理(学习笔记)

    本文参考自[野火EmbedFire]<RT-Thread内核实现与应用开发实战--基于STM32>,仅作为个人学习笔记.更详细的内容和步骤请查看原文(可到野火资料下载中心下载) 文章目录 ...

  8. RT-Thread 动态内存管理(学习笔记)

    本文参考自[野火EmbedFire]<RT-Thread内核实现与应用开发实战--基于STM32>,仅作为个人学习笔记.更详细的内容和步骤请查看原文(可到野火资料下载中心下载) 文章目录 ...

  9. arm64的ioremap_ARMv8 内存管理架构.学习笔记

    第1章分级存储架构 1.1基础认识 通常为了保证计算机的整体性能,内存和CPU之间的通信需保证很高的传输速率,然而这受限制于内存的大小和昂贵的硬件实现,传输速率和内存容量大小的关系遵循"Sm ...

  10. 1.C#基础学习笔记3---C#字符串(转义符和内存存储无关)

    技术qq交流群:JavaDream:251572072  教程下载,在线交流:创梦IT社区:www.credream.com ------------------------------------- ...

最新文章

  1. python爬虫怎么做毕业设计_python语言爬虫做成毕业设计的话,怎样答辩演示,或者怎样把爬虫复杂化?...
  2. [转]sqlserver 数据类型 及使用考虑
  3. 浅谈湖仓一体化对上层机器学习业务的促进
  4. 数据结构一:链表(linux链表)
  5. php 处理 http 请求,PHP的http请求处理类
  6. 一个IP绑定多个域名(虚拟主机)设置方法
  7. 死锁产生的原因及条件、如何避免死锁
  8. 手把手教你理解卷积神经网络
  9. 如何计算出int的数据范围
  10. python scapy模块安装_python scapy模块安装与依赖模块
  11. 使用Context和Hooks来管理状态
  12. Spring Cloud教程 (二)应用程序上下文服务层次结构
  13. 所有进程的信息 linux,LINUX下获取所有进程信息
  14. leetcode刷题--python
  15. solidworks迈迪设计宝_机械入门|那些看起来很牛X的机械结构,是如何设计的?...
  16. python拼接sql语句字符串 无效字符,Python拼接SQL字符串的方法
  17. dirent struct_struct dirent中d_name长度问题
  18. Azure 安全网络篇 - DMZ 区域设计实践
  19. java自学笔记(4)-Stanford CS106A 弹球动画 20.9.9
  20. 地级市空气污染、空气质量、PM2.5日度数据

热门文章

  1. [work] 清华朱军 NIPS 2017对抗样本攻防竞赛总结(附学习资料)
  2. 【C语言】动态内存管理(heap)
  3. 一篇很哇塞的MyBatis入门到精通
  4. 7-2 列出叶结点 (俺没有测试样例,别想了)
  5. 80后的我们为什么不结婚
  6. 再次领先全国,第一只波士顿机器狗落户上海,等你来撸
  7. 台式电脑怎么调出计算机,台式电脑连接笔记本显示器的方法步骤
  8. 通过 TensorFlow 实现 AI 语音降噪提升 QQ 音视频通话质量
  9. c语言将时速转换成配速,配速与时速换算(跑步配速和时速换算)
  10. JavaWeb登录案例带验证码(mysql+servlet+jsp+idea)