• 首发公号: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. 顺通ERP:精细敏捷的设计理念,得到了消费者的喜爱

    顺通ERP是近年来备受关注的一款ERP品牌,其设计精细,操作便捷,备受消费者喜爱.那么,顺通ERP到底怎么样呢?属于什么档次呢? 首先,从品质上来看,顺通ERP具备高度的稳定性,能够确保企业的日常运营 ...

  2. Java面试题:为什么HashMap不建议使用对象作为Key?

    HashMap 是一种基于哈希表的动态数据结构,它允许使用任意不可变对象作为键(key)来存储和检索数据.然而,在某些情况下,使用对象作为 HashMap 的键可能会遇到一些问题. 首先,我们需要明确 ...

  3. 基于 Flink CDC 实现海量数据的实时同步和转换

    简介: 徐榜江在 5.21 Flink CDC Meetup 的分享. 摘要:本文整理自 Apache Flink Committer,Flink CDC Maintainer,阿里巴巴高级开发工程师 ...

  4. 使用 Arthas 排查 SpringBoot 诡异耗时的 Bug

    简介: 公司有个渠道系统,专门对接三方渠道使用,没有什么业务逻辑,主要是转换报文和参数校验之类的工作,起着一个承上启下的作用.最近,在优化接口的响应时间,优化了代码之后,但是时间还是达不到要求:有一个 ...

  5. SpringBoot项目实现日志打印SQL明细(包括SQL语句和参数)几种方式

    前言 我们在开发项目的时候,都会连接数据库.有时候遇到问题需要根据我们编写的SQL进行分析,但如果不进行一些开发或者配置的话,这些SQL是不会打印到控制台的,它们默认是隐藏的.下面给大家介绍几种常用的 ...

  6. WPF 修复引用库报错 所使用的 PresentationFramework 6

    本文记录一个 WPF 构建的坑,这是 WPF 仓库的运维管理大意挖的坑.将会在大家使用低版本的 dotnet 6 如 6.0.1 版本时,引用其他人使用高 dotnet 6 版本,如 dotnet 6 ...

  7. aspnetcore两种上传图片(文件)的方式

    aspnetcore上传图片也就是上传文件有两种方式,一种是通过form-data,一种是binary. 先介绍第一种form-data: 该方式需要显示指定一个IFormFile类型,该组件会动态通 ...

  8. vue+vant+js实现购物车原理小demo(中级版有选择)

    增加只计算已选的的购物车商品功能.效果图: main.js: Vue.use(Stepper); Vue.use(Checkbox); Vue.use(CheckboxGroup); 上代码: < ...

  9. 什么是NASM

    什么是NASM NASM是一个汇编器的名称,全称是Netwide Assembler,支持x86与x64架构的CPU(注意不支持ARM架构). 我们知道,在x86和x64架构上有多个操作系统,比较流行 ...

  10. csapp-bomblab(自信满满版)

    反汇编bomb文件 要查看机器代码文件的内容,有一类称为反汇编器(disassembler,assembler是汇编程序,dis-加在某些词语前表示相反的意思)的程序非常有用.这些程序根据机器代码产生 ...