• 首发公号:Rand_cs

共享内核空间

我们常说,每个进程都有自己的虚拟地址空间,但其中内核部分是共享的。

这就有个问题,如何共享的?

系统启动时创建了一张内核页表,里面记录着内核地址空间与物理地址空间的映射关系,而每次 fork 子进程时都会复制一份内核页表,所以说每个进程页表中的内核部分是“相同的”,因此可以说共享内核空间。

这里我将最初的内核页表称为“原本”,每个进程的内核部分称为“副本”,类似副本的设计都会存在存在一个问题——同步,意思是说,如果某个进程修改了内核空间的映射关系,那么需要将这种映射关系的改变同步到其他副本,如此才能保证逻辑正确。

最初分析 xv6 的 scheduler 的代码时,发现每次返回到调度器时都会切换到内核页表,如下所示:

void scheduler(void)
{
........
swtch(&(c->scheduler), p->context);
switchkvm();
........
}

当时我便在想,不切换行不行,于是将 switchkvm 注释掉,发现出了错,而且错误还不固定,有的是触发 page fault、有的是系统 reset(从 main 函数重新开始执行),未找到明确的复现规律。当时没有细想,初步便认为是因为各个进程页表的内核部分,也就是“副本”们之间没有同步,导致了种种错误。后来重新看代码的时候,发现问题应该不在内核页表同步。

xv6 不需要内核页表同步,因为 xv6 在启动的时候,内核地址空间的映射关系已经建立好了,而纵观代码也没有修改内核地址空间映射关系的地方,所以内核地址空间的映射关系应是一直不变的。可能有的朋友会觉得 kalloc 函数会更改内核映射,其实并没有,kalloc 只是分配内存,并没有修改映射关系,可以仔细看看 kalloc 前后的关于修改映射关系的代码,比如说 *pte = xxx, *pde = xxx,这才是修改页表修改映射关系。在 growproc->kalloc 调用链中,kalloc 分配的内存映射到了进程的用户空间,修改的是进程页表用户态部分的 pte,mappages->walkpgdir->kalloc 中分配的内存用作用户页表,修改的是进程页表用户态部分的 pde

这就相当于 Linux 中的直接映射区域,但是不存在 vmalloc 动态映射区域,所以 xv6 其实不需要内核页表的同步。假如说某个进程确实会改动内核映射关系,那么应该如何实现内核页表同步。同步内核页表是为了每个进入内核时都能看到相同的内核影响,所以

  • 第一种方式不需要同步内核页表,每次进程进入内核的时候,切换到“原本”内核页表,那么每个进程进入内核的时候使用的是同一份页表,当然就不需要同步
  • 第二种方式,那就是老老实实的同步页表,也就是说当“原本”或“副本”被修改的同时,也就将相关的修改同步到其他“副本”。这部分可以参考 Linux vmalloc 区同步的做法,当“原本”修改时,调用 sync_global_pgds 主动将修改同步到其他“副本”。当某个“副本”被修改时,Linux 先后有三种同步方式,最开始在 pagefault 中触发同步,但有竟态问题,有了第二种主动同步,但因为性能问题,又增加了第三种干掉同步的方式。Linux 内存管理的部分见 bin的技术小屋 这位大佬写的文章,本文不赘述,这应该是全网对 Linux 内存管理讲解的最详尽细致的文章了,值得一看。

这里在穿插一个问题,既然内核里面本来就有一份内核页表,那么进程页表何必再拷贝一份内核页表,反正进程在用户态时不能访问内核态,根本就用不到内核页表。每个进程页表只需要映射它自己的地址空间以及跳转到内核那一小段代码段即可,跳转到内核后,切换到内核页表,在内核办完事儿后再切换回进程页表,这个过程似乎没有问题,也就是根本就没必要拷贝整个内核页表到进程页表的内核部分,那为什么还要这么做呢,让内核地址空间和进程用户地址空间在同一张页表共存?

我想,这个问题应该是和架构强相关,在 arm 中有两个页表寄存器,ttbr0 存放进程页表,ttbr1 存放共享的内核页表,访问用户空间地址使用 ttbr0 寄存器,访问内核空间地址使用 ttbr1 寄存器。因为 arm 有两个寄存器,进程进出内核不需要进行页表切换。

但是 x86 架构只有一个页表寄存器,如果将用户页表和内核页表分开,那么进出内核势必造成页表切换,页表切换刷新 tlb,如果没有 ASID/PCID 等机制的话,性能损失太多

话说,内核页表和用户页表共存也会引发一些安全问题,比如之间大爆的 meldown 漏洞,以及相应的缓解方案 kpti,挺有意思,有兴趣的话可以看一下。

回到 xv6 上面来,那为什么 scheduler 中需要切换到内核页表?很不幸,这个问题始终还未能解决,写在这里便是和大家讨论一下,以及分享一下相关的一些东西,另外很有意思的是,当我在 ubuntu 虚拟机中跑注释掉 switchkvm 的 xv6 时,会引发各种问题,但是在 ubuntu 的宿主机上跑便没有问题(已考虑到虚拟机 cpu 核心分配的问题),这便很奇怪,想了很久未能弄明白,暂时存疑吧。元芳,你怎么看?

  • 首发公号:Rand_cs

xv6 内核空间共享的更多相关文章

  1. linux内核空间与用户空间信息交互方法

    linux内核空间与用户空间信息交互方法     本文作者: 康华:计算机硕士,主要从事Linux操作系统内核.Linux技术标准.计算机安全.软件测试等领域的研究与开发工作,现就职于信息产业部软件与 ...

  2. linux进程用户内存空间和内核空间

    When a process running in user mode requests additional memory, pages are allocated from the list of ...

  3. Linux用户空间与内核空间(理解高端内存)

    Linux 操作系统和驱动程序运行在内核空间,应用程序运行在用户空间,两者不能简单地使用指针传递数据,因为Linux使用的虚拟内存机制,用户空间的数据可能被换出,当内核空间使用用户空间指针时,对应的数 ...

  4. Linux用户空间与内核空间

    源:http://blog.csdn.net/f22jay/article/details/7925531 Linux 操作系统和驱动程序运行在内核空间,应用程序运行在用户空间,两者不能简单地使用指针 ...

  5. 【转】地址空间、内核空间、IO地址空间

    http://blog.csdn.net/wuxinke_blog/article/details/8769131 有这么一系列的问题,是否在困扰着你:用户程序编译连接形成的地址空间在什么范围内?内核 ...

  6. linux 用户空间与内核空间——高端内存详解

    摘要:Linux 操作系统和驱动程序运行在内核空间,应用程序运行在用户空间,两者不能简单地使用指针传递数据,因为Linux使用的虚拟内存机制,用户空间的数据可能被换出,当内核空间使用用户空间指针时,对 ...

  7. Linux 内核空间与用户空间

    本文以 32 位系统为例介绍内核空间(kernel space)和用户空间(user space). 内核空间和用户空间 对 32 位操作系统而言,它的寻址空间(虚拟地址空间,或叫线性地址空间)为 4 ...

  8. linux内核空间和用户空间详解

    linux驱动程序一般工作在内核空间,但也可以工作在用户空间.下面我们将详细解析,什么是内核空间,什么是用户空间,以及如何判断他们.Linux简化了分段机制,使得虚拟地址与线性地址总是一致,因此,Li ...

  9. linux用户空间和内核空间(内核高端内存)_转

    转自:Linux用户空间与内核空间(理解高端内存) 参考: 1. 进程内核栈.用户栈 2. 解惑-Linux内核空间 3. linux kernel学习笔记-5 内存管理   Linux 操作系统和驱 ...

  10. linux 用户态和内核态以及进程上下文、中断上下文 内核空间用户空间理解

    1.特权级         Intel x86架构的cpu一共有0-4四个特权级,0级最高,3级最低,ARM架构也有不同的特权级,硬件上在执行每条指令时都会对指令所具有的特权级做相应的检查.硬件已经提 ...

随机推荐

  1. ASP.NET CORE 框架揭秘读书笔记系列——命令行程序的创建(一)

    一.dotnet --info 查看本机开发环境 dotnet --info  会显示本机安装的SDK版本.运行时环境.运行时版本 二.利用命令行创建.NET项目 我们不仅可以利用脚手架模版创建各种类 ...

  2. 力扣1127(MySQL)-用户购买平台(困难)

    题目: 支出表: Spending 这张表记录了用户在一个在线购物网站的支出历史,该在线购物平台同时拥有桌面端('desktop')和手机端('mobile')的应用程序.这张表的主键是 (user_ ...

  3. 力扣175(MySQL)-组合两个表(简单)

    题目: 表: Person 表: Address 编写一个SQL查询来报告 Person 表中每个人的姓.名.城市和州.如果 personId 的地址不在 Address 表中,则报告为空  null ...

  4. 力扣43(java)-字符串相乘(中等)

    题目: 给定两个以字符串形式表示的非负整数 num1 和 num2,返回 num1 和 num2 的乘积,它们的乘积也表示为字符串形式. 注意:不能使用任何内置的 BigInteger 库或直接将输入 ...

  5. 力扣564(java)-寻找最近的回文数(困难)

    题目: 给定一个表示整数的字符串 n ,返回与它最近的回文整数(不包括自身).如果不止一个,返回较小的那个. "最近的"定义为两个整数差的绝对值最小. 示例 1: 输入: n = ...

  6. 配置审计(Config)变配报警设置

    简介: 本文作者[紫极zj],本篇将主要介绍通过配置审计的自定义规则等服务,对负载均衡进行预警行为的相关介绍. 前言 配置审计(Config)将您分散在各地域的资源整合为全局资源列表,可便捷地搜索全局 ...

  7. [Pholcus] Go项目 Pholcus 编写静态规则文件, 0 到 1

    1. 初始化项目包,go mod init [module-path] 比如:go mod init github.com/abc/efg 2. 新建一个目录放置我们编写的规则 go 文件. 3. m ...

  8. Kimi:文本解析利器,你相信光么?

    缘起 第一次接触 kimi 是在微信群,开始以为是推广薅羊毛产品,后来在其他渠道也了解到 kimi,据说是"国产之光".我知道很多同学苦不能使用魔法久矣,索性就先踩踩这个" ...

  9. 用友BIP全面预算

    全面预算是企业在经营过程中制定并实施的一种财务管理工具,它考虑了企业的各个方面,包括销售.采购.生产.财务.人力资源等,以全面的视角规划和控制企业的财务活动. 用友BIP全面预算数智化解决方案利用了& ...

  10. MYSQL CONVERT、JSON_EXTRACT函数的使用总结

    一.CONVERT.CONCAT.COUNT函数联合查询 CONVERT()函数用于将值从一种数据类型转换为表达式中指定的另一种数据类型. MySQL还允许它将指定的值从一个字符集转换为另一个字符集. ...