• 首发公号: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. 《c#高级编程》第2章C#2.0中的更改(三)——迭代器

    一.概念 C#迭代器(Iterator)是一种特殊类型的方法,它使得在使用循环遍历数据集合时更加简单和有效.使用迭代器可以通过简单地定义迭代器方法来自动实现枚举器模式. 当您需要访问一个数据集合中的每 ...

  2. 微信小程序,本地和真机测试都是好的,但体验版扫码显示空白页

    大概率是缓存导致的,删除掉你手机上的小程序[开发版] 和 [体验版],然后再扫码进入体验版就好了.

  3. kubernetes集群最新版安装

    原文地址:https://haiyux.cc/2022/09/21/k8s-install/ 虚拟机准备 我这里准备了三台虚拟机,分别部署一个master和两个node,操作系统位ubuntu 20. ...

  4. 用百度和神策做埋点为何pv差异很大?

    近期ClkLog收到一个客户反馈说我们与百度统计的PV数据差异很大.为了验证问题,开发进行了一次对页面浏览量统计的测试.针对同一个IP同一个时间的页面浏览量统计发现,百度的统计数据只有一条,而ClkL ...

  5. 第11課-Channel Study For Create Custom Restful Service

    这节课我们一起学习利用Mirth Connect的HTTP Listener源通道与JavaScript Writer目的通道搭建自定义Restful风格webapi服务. 1.新建名为'Custom ...

  6. 力扣58(java)-最后一个单词的长度(简单)

    题目: 给你一个字符串 s,由若干单词组成,单词前后用一些空格字符隔开.返回字符串中 最后一个 单词的长度. 单词 是指仅由字母组成.不包含任何空格字符的最大子字符串. 示例 1: 输入:s = &q ...

  7. 使用Databricks进行零售业需求预测的应用实践

    ​简介:本文从零售业需求预测痛点.商店商品模型预测的实践演示,介绍Databricks如何助力零售商进行需求.库存预测,实现成本把控和营收增长. 作者:李锦桂 阿里云开源大数据平台开发工程师 本文从零 ...

  8. 创新推出 | Serverless 场景排查问题利器:函数实例命令行操作

    ​简介: 实例命令行功能的推出希望能消除用户使用 Serverless 的"最后一公里",直接将真实的函数运行环境展现给用户,此后 Serverless 将不再是一个"黑 ...

  9. dotnet 6 引用 NAudio 的旧版本构建不通过

    本文告诉大家在使用 NAudio 的旧版本导致构建不通过问题,解决方法是升级到 1.10 或以上版本 在更新 dotnet 6 项目时,使用了 NAudio 的旧版本,构建失败,提示 MC1000 如 ...

  10. RT-Thread线程同步与线程通信

    一.线程同步 线程同步的使用场景 例如一项工作中的两个线程:一个线程从传感器中接收数据并且将数据写到共享内存中,同时另一个线程周期性的从共享内存中读取数据并发送去显示,下图描述了两个线程间的数据传递: ...