首页IT科技linux 页表存在哪(Linux内核学习笔记——页表的那些事。)

linux 页表存在哪(Linux内核学习笔记——页表的那些事。)

时间2025-09-26 06:02:32分类IT科技浏览4572
导读:接上两文,本文补充一下内核页表和用户页表创建、更新时机说明。...

接上两文                 ,本文补充一下内核页表和用户页表创建                 、更新时机说明                  。

Linux内核学习笔记——内核页表隔离KPTI机制

Linux内核学习笔记——内核页表隔离KPTI机制(源码分析)

KPTI中每个进程有两套页表——内核态页表与用户态页表(两个地址空间)                          。

内核态页表只能在内核态下访问                           ,可以创建到内核和用户的映射(不过用户空间受SMAP和SMEP保护)         。

内核页表:即书上说的主内核页表         ,在内核中其实就是一段内存                 ,存放在主内核页全局目录init_mm.pgd(swapper_pg_dir)中                          ,硬件并不直接使用         。

进程页表:每个进程自己的页表         ,放在进程自身的页目录task_struct.pgd中                          。

在保护模式下         ,从硬件角度看                          ,其运行的基本对象为“进程                 ”(或线程)                  ,而寻址则依赖于“进程页表                           ”         ,在进程调度而进行上下文切换时                          ,会进行页表的切换:即将新进程的pgd(页目录)加载到CR3寄存器中                 。从这个角度看                  ,其实是完全没有用到“内核页表         ”的,那么“内核页表                 ”有什么用呢?跟“进程页表                          ”有什么关系呢?

页表什么时候创建

内核页表中的内容为所有进程共享                          ,每个进程都有自己的“进程页表         ”                           ,“进程页表         ”中映射的线性地址包括两部分:

用户态 内核态

其中,内核态地址对应的相关页表项                 ,对于所有进程来说都是相同的(因为内核空间对所有进程来说都是共享的)                           ,而这部分页表内容其实就来源于“内核页表                          ”         ,即每个进程的“进程页表                  ”中内核态地址相关的页表项都是“内核页表         ”的一个拷贝(进程创建时候就产生了)         。

内核页表变化什么时候更新到用户页表

“内核页表                          ”由内核自己维护并更新                 ,在vmalloc区发生page fault时                          ,将“内核页表                  ”同步到“进程页表”中                           。以32位系统为例         ,内核页表主要包含两部分:

线性映射区 vmalloc区

其中         ,线性映射区即通过TASK_SIZE偏移进行映射的区域                          ,对32系统来说就是0-896M这部分区域                  ,映射对应的虚拟地址区域为TASK_SIZE~TASK_SIZE+896M                 。这部分区域在内核初始化时就已经完成映射         ,并创建好相应的页表                          ,即这部分虚拟内存区域不会发生page fault。

vmalloc区                  ,为896M~896M+128M,这部分区域用于映射高端内存                          ,有三种映射方式:vmalloc                           、固定         、临时                           ,这里就不详细展开了                           。

以vmalloc为例(最常使用),这部分区域对应的线性地址在内核使用vmalloc分配内存时                 ,其实就已经分配了相应的物理内存                           ,并做了相应的映射         ,建立了相应的页表项                 ,但相关页表项仅写入了“内核页表                          ”                          ,并没有实时更新到“进程页表中                           ”         ,内核在这里使用了“延迟更新”的策略         ,将“进程页表                 ”真正更新推迟到第一次访问相关线性地址                          ,发生page fault时                  ,此时在page fault的处理流程中进行“进程页表                           ”的更新                          。

源码分析

/* * 缺页地址位于内核空间。并不代表异常发生于内核空间         ,有可能是用户 * 态访问了内核空间的地址                  。 */ if (unlikely(fault_in_kernel_space(address))) { if (!(error_code & (PF_RSVD | PF_USER | PF_PROT))) { //检查发生缺页的地址是否在vmalloc区                          ,是则进行相应的处理 if (vmalloc_fault(address) >= 0) return; /* * 对于发生缺页异常的指针位于vmalloc区情况的处理                  ,主要是将 * 主内核页表向当前进程的内核页表同步                          。 */ static noinline __kprobes int vmalloc_fault(unsigned long address) { unsigned long pgd_paddr; pmd_t *pmd_k; pte_t *pte_k; /* Make sure we are in vmalloc area: */ /* 区域检查 */ if (!(address >= VMALLOC_START && address < VMALLOC_END)) return -1; WARN_ON_ONCE(in_nmi()); /* * Synchronize this tasks top level page-table * with the reference page table. * * Do _not_ use "current" here. We might be inside * an interrupt in the middle of a task switch.. */ /*获取pgd(最顶级页目录)地址,直接从CR3寄存器中读取         。 *不要通过current获取                          ,因为缺页异常可能在上下文切换的过程中发生                           , *此时如果通过current获取,则可能会出问题*/ pgd_paddr = read_cr3(); //从主内核页表中                 ,同步vmalloc区发生缺页异常地址对应的页表 pmd_k = vmalloc_sync_one(__va(pgd_paddr), address); if (!pmd_k) return -1; //如果同步后                           ,相应的PTE还不存在         ,则说明该地址有问题了 pte_k = pte_offset_kernel(pmd_k, address); if (!pte_present(*pte_k)) return -1; return 0; }

常见问题解答

问题一:页表到底是保存在内核空间中还是用户空间中?

创建和删除页表的确是在内核空间操作的                  。页表不能在用户空间进行操作一点都不奇怪                 ,你要知道页表的作用不仅仅是虚拟地址到物理地址的映射                          ,还有关键的权限访问控制和页面属性的记录                          。下图是armv8中level 1的页表格式         ,类似于x86中的PUD的结构:

可以看到该页表中只有"Outlook block address"是在表示下一级页表的地址         ,"Upper attributes"和"Lower attributes"是内核空间用到权限的控制位和页属性标志         。

问题2:页表访问                          ,软件是不是会频繁陷入内核?

这个需要结合场景分析         。访问页表是否会陷入内核                  ,这要看你是:

CPU地址翻译的过程中的页表访问; 增加修改页表项                          。

如果是第一种         ,CPU地址翻译                          ,那么这种访问是硬件完成的                  ,整个过程不需要代码参与,没有任何性能上的损失                 。

如果是第二种                          ,是会慢一些         。这种慢是为了安全                           ,如果页表在用户空间,那么用户就可能自己修改页表                 ,映射任意的内存地址                           ,访问任何内存         ,甚至是直接操作硬件                 ,进程间                 、内核的隔离保护就失去了意义                           。

问题3:内存申请                          ,软件是不是会频繁陷入内核创建新页表条目

你以为在用户进程中分配内存的时候         ,就马上通过系统调用陷入内核         ,然后进行页表操作吗?这个理解是不对的                 。

应用程序虽然可能频繁的malloc或者free                          ,但在页表层面上                  ,并不会频繁的创建                          、删除页表项         ,主要原因是                          ,malloc/free操作的接口都是C库的接口                  ,在C库里,还有另外一层次的封装                          ,来保证不会频繁的提交页表的操作申请。

内核如今已经发展的很成熟了                           ,当然不会这么傻                           。在你兴高采烈的分配好一块内存后,内核只是给你找了一块独一无二的虚拟内存空间                 ,并没有映射到物理内存                           ,所以根本没有页表的操作                          。只有你真正用到你的内存时         ,MMU发现无法进行虚拟内存到物理内存的转换                 ,只好抛出page fault异常                          ,然后进入内核进行物理内存的分配过程         ,接着就给你把页表创建好了         ,这个整个过程叫做惰性分配。

更重要的是                          ,其实libc库在进程创建的时候                  ,就已经把堆空间用内存池的方式管理起来         ,在进程分配小于128kb的内存时                          ,根本不需要内核进行任何操作                  ,因为堆这个段的虚拟内存早就映射好了物理内存                  。

问题4:那内核页表和普通的页表到底有什么区别?

对于所有进程来说它们页表中的内核空间页表部分都是一模一样的,它们都是从1号进程的init_mm结构中copy的                          ,只有用户空间的页表不尽相同                          。用户空间的页表是用来进行不同进程地址空间隔离的                           ,所以相同的虚拟地址可以映射到不同的物理地址,当然一般情况下这也是必须的                 ,而内核只有一个         。

声明:本站所有文章                           ,如无特殊说明或标注         ,均为本站原创发布                  。任何个人或组织                 ,在未征得本站同意时                          ,禁止复制         、盗用         、采集                          、发布本站内容到任何网站                  、书籍等各类媒体平台                          。如若本站内容侵犯了原著者的合法权益         ,可联系我们进行处理         。

创心域SEO版权声明:以上内容作者已申请原创保护,未经允许不得转载,侵权必究!授权事宜、对本内容有异议或投诉,敬请联系网站管理员,我们将尽快回复您,谢谢合作!

展开全文READ MORE
seo排名关键词搜索结果(SEO关键词排名优化报价——提升网站流量和品牌曝光的最佳选择) 谷歌浏览器怎么在新标签打开网页(如何设置谷歌Chrome浏览器打开新的标签页为指定网页(New Tab Redirect插件))