• 首发公号: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. 【笔记】Java相关大杂烩②

    [笔记]Java相关大杂烩② if单分支情况下,如果没有加 {},那么默认只包含第一条语句. if 和 else 分支后面如果包含多条语句,那么需要使用 {} 括起来. 不能随意地使用数学上的表达方式 ...

  2. 力扣636(java)-函数的独占时间(中等)

    题目: 有一个 单线程 CPU 正在运行一个含有 n 道函数的程序.每道函数都有一个位于  0 和 n-1 之间的唯一标识符. 函数调用 存储在一个 调用栈 上 :当一个函数调用开始时,它的标识符将会 ...

  3. Spark如何对源端数据做切分?

    简介: 典型的Spark作业读取位于OSS的Parquet外表时,源端的并发度(task/partition)如何确定?特别是在做TPCH测试时有一些疑问,如源端扫描文件的并发度是如何确定的?是否一个 ...

  4. 千万级可观测数据采集器--iLogtail代码完整开源

    简介: 2022年6月29日,阿里云iLogtail开源后迎来首次重大更新,正式发布完整功能的iLogtail社区版.本次更新开源全部C++核心代码,该版本在内核能力上首次对齐企业版,开发者可以构建出 ...

  5. CNCF TOC 委员张磊:不断演进的云原生给我们带来了什么?

    简介: 任何一种云原生技术,它不再是某种能力的弥补,而是更多地将云的能力以某种方式更简单.更高效地透出给我的应用去使用.无论是容器.K8s 还是 Service Mesh,他们都是在不同的环节帮助应用 ...

  6. WPF 已知问题 BitmapDecoder.Create 不支持传入 Asynchronous 的文件流

    这是在 GitHub 上有小伙伴报的问题,在 WPF 中,不支持调用 BitmapDecoder.Create 方法,传入的 FileStream 是配置了 FileOptions.Asynchron ...

  7. Vue3 和 Vue2 的异同及开发中具体区别

    目录 总体来说 性能提升 树摇(Tree shaking) 碎片化节点(Fragment) 传送门 (Teleport) Suspense 更好的TypeScript支持 Composition AP ...

  8. Listener监听器,实现一个显示在线用户人数

    Listener监听器,实现一个显示在线用户人数 每博一文案 关于后半身,脾气越温,福报越深. 师傅说:惜命最好的方式不是养生,而是管好自己的情绪. 坏毛病都是惯出来的,但好脾气都是磨出来的,与人生气 ...

  9. 自动生成robot自动化测试用例

    背景:java项目使用swagger管理接口,随着需求的开发接口也有增加,要从swagger界面中去查找出新增的接口是件很费时,效率很低的事情. 适用情况: java项目且适用swagger管理接口 ...

  10. 累计预扣法个税,怎么算?(附excel)

    累计预扣法个税计算 依法纳税是每个公民的义务,但看着每个月递增的个税,你可能会发出疑问,这到底是怎么算的?这就要引出2019年1月1日实施新实施的个税法,累计预扣法.即自2019年1月1日起,居民个人 ...