Linux内核中,CPU是如何读写数据的

2023-12-20 15:31:21

哈喽,我是子牙,一个很卷的硬核男人

深入研究计算机底层、Windows内核、Linux内核、Hotspot源码……聚焦做那些大家想学没地方学的课程。为了保证课程质量及教学效果,一年磨一剑,三年先后做了这些课程:手写JVM、手写OS、带你用纯汇编写OS、手写64位多核OS、实战Linux内核…

这篇文章聊什么呢?如题。比如你写了这样的代码

代码的运行效果就是在屏幕上输出这个进程的ID及字符串:ziya。如果我问你:CPU是如何读到内存中的字符串ziya并输出的,阁下如何作答?说到这,我就想起了大司马跟鬼屎东那对活宝:你说赵信在哪个野区?

我会从这三个层面为大家分析这个问题

代码演示

一般讲底层,大家看到的,几乎都是讲原理,这从来不是我的风格!说白了,不能手撕代码,都是实力不足,网上搜点相关的文章看看,然后用自己的语言表达出来,只知其一不知其二,误导人

我写了一个Linux内核驱动,将CPU读写数据的过程详细打印了出来,贴出来给大家看看

有的小伙伴说:没看到字符串ziya啊!内存中存储的是每个字符的anscii码

我知道你也想体验,关注公众号【硬核子牙】回复【CPU读写数据】可免费领取我写的驱动。命令行输入如下指令安装驱动

如果你不按照这个传参,驱动内部会给你报错,不让你用,保护你的电脑(驱动程序出问题,你的操作系统就挂了,只能重启。当然,如果我动点手脚,你可能只能重装系统了,这就是所谓的黑客行为)

我的开发环境是Ubuntu16,内核我升级成了5.6,如果你跟我的环境不一样,可能存在兼容性问题。当前只是为了写文章演示给大家看,没做兼容性、没做过多测试,如果你使用时有问题,属于正常现象,后面我会抽空完善这个程序,届时会将代码开放给大家去研究

驱动中默认是无法向屏幕终端输出内容的,只能写入内核日志,所以你可以这样查看Linux内核日志

接下来我将这个过程中的每一步展开讲讲

CPU读写数据完整框架

我先说完整框架,再将每个相关知识点展开讲讲

先说下CPU读写数据的完整框架

从图中可以看出来,CPU读取数据时,会有两个部件参与工作:段部件、页部件,段部件没有专业名词,页部件就是鼎鼎大名的MMU。

这里面会涉及到三个跟内存地址相关的名词:

1、逻辑地址:汇编代码中看到的地址

2、线性地址:CPU段部件产出的地址,又名虚拟地址

3、物理地址:实际内存(RAM)的地址

其中段部件的工作机制是:输入逻辑地址,输出线性地址,又叫虚拟地址。页部件的工作机制是:输入线性地址,输出物理地址,CPU拿到这个物理地址,就可以读到内存上的数据,与MMU一起参与工作的,还有一个缓存模块:TLB

如果CPU没有开启分页,那页部件是不参与工作的,这时候段部件产出的就是物理地址。那如何开启CPU的虚拟内存呢?如图

关于CPU的段部件,在不同CPU架构上也是有差异的,比如x86 CPU上,是要做严格的检查的,但是在x64 CPU上,强制使用平坦模型,不再检查段界限、段属性。是不是没get到,接着往后看

CPU读写数据细节

接下来说CPU读写数据的细节

当CPU要读内存0x1000处的数据,它会:
1、读ds段寄存器的值,按照[段选择子]格式拆出:Index、TI、RPL

RPL是什么呢?是CPU的请求特权级,就是CPU读写数据是以什么权限发起的这个请求,一般用户态的权限是3,内核态的权限是0。当CPU发起请求时,这个RPL就会变成CPU的CPL,即当前请求特权级

CPU的段部件的工作,需要依赖操作系统提供的两张表:全局描述符表gdt、局部描述符表ldt。区别是什么呢?gdt中定义的内存段是约束所有进程的,ldt中定义的内存段,是约束某个进程的。一般来说,os不用ldt,只用gdt,所以几乎TI的值都是0

这里面为什么是0呢?因为x64 CPU不做检查了,Linux就没填充值。我们看x86下,比如它的值是0x33

0x33,按照段选择子进行拆,index=6,TI=0,RPL=3。接下来就是查gdt表,找到index=6的具体数据

gdt表长啥样子呢?它的key就是类似数组的索引,它的值是段描述符

段描述符结构长这样子

2、取到gdt表中index=6位置的值,就是数据段描述符,然后做各种检查,产出线性地址:

  1. 检查p位,如果为1,继续,为0,结束
  2. 检查DPL,如果DPL > CPL,继续,反之,结束,CPU报错#GP异常
  3. 检查s,为1继续,为0,结束
  4. 检查type,小于8,继续,反之,结束
  5. 拿到base,与逻辑地址相加,得到线性地址,将线性地址与limit比较,越界CPU报错#PF异常,反之继续
  6. 输出线性地址

如果是x64 CPU,直接产出线性地址,所以Linux内核,在进入用户态的时候,是不配置ds、ss的

数据段描述符每个位的详细介绍如图

MMU工作原理

本篇文章篇幅已经足够长,这部分内容准备再写一篇文章细说,顺便给大家详细讲讲内存PAE技术

关注公众号【硬核子牙】,品读计算机底层硬核文章

手写x64多核OS

文章中分享的硬核内容,都在我的课程《逆向思维实战Linux内核》中,如果是你想学的,可以咨询班主任,领取免费试看视频,了解一下我的讲课风格与技术实力,再做打算

我们的实战Linux内核二期开始招生了,当前预售价,仅50个名额,还剩26个。课程共十二大专题,三个月时间,带你手写x86单核os、手写x64多核os打底,再带你以手写Linux驱动的方式实战Linux内核。只有这样学,才能真正学会Linux内核,才能做实验,而不是只是停留在原理层面,无法论证…

学完以后,你可以

为了保证课程质量,课程所有内容由子牙老师亲授!Linux内核中,少部分代码是体系相关的,主要受CPU架构的影响,绝大多数代码是体系无关的。这套课程是基于x86_64架构,你如果是ARM、MIPS、RISCV架构,也可以学,技多不压身!其他CPU架构,后面会陆续补上,体系架构无关的代码是不会动的,所以如果你报名了这套课程,后面补差价即可

BTW,报名我们的任一课程,后续的所有新课

文章来源:https://blog.csdn.net/b98982016/article/details/135106836
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。