Linux0.11源码趣读

用源码打开操作系统“黑盒子”

Linux,Linux 0.11,内核,源码,操作系统,

闪客 “低并发编程”公号作者

03|做好访问内存的最基础准备工作

讲述:闪客 大小:6.34MB 时长:00:06:37
00:00
1.0×

你好,我是闪客。

这一讲我们要探索的问题是,操作系统是怎么为程序访问内存的方式做初步规划的?

上一讲咱们说到,操作系统的代码最开头的 512 字节的数据,先从硬盘的启动区移动到了内存 0x7c00 处,然后又立刻被移动到 0x90000 处,并且跳转到 0x90000 加上 go 这个标签所代表的偏移量。

图片

那我们接下来,就继续把目光放在 go 这个标签的位置,跟着 CPU 执行的步伐往后看。

go: mov ax,cs
    mov ds,ax
    mov es,ax
    mov ss,ax
    mov sp,#0xFF00

一眼望去,全都是 mov 操作,那就很好办了。这段代码的直接意思很容易理解,就是把 cs 寄存器的值分别复制给 dsesss 寄存器,然后又把 0xFF00 给了 sp 寄存器。

回顾下 CPU 寄存器的图解:

图片

由此也可以看出,其实操作系统最开始这几行代码的难点并不在翻译,而是在于它要完成什么事情,而要理解它们要完成的事情,就需要计算机体系结构的知识,说白了就是指 Intel CPU 的使用说明。

如果你能把 Intel CPU 手册阅读一遍并且有个大体的认识,那这几行代码就不在话下了。

但对于大部分软件工程师来说,Intel CPU 手册还是过于底层了,所以建议你课后真正花时间去系统了解一下。这里你先听我往下讲就好了,研读 Linux0.11 源码需要用到的核心知识点我都会告诉你。

这些寄存器是干嘛的?

cs 寄存器表示代码段寄存器,CPU 即将要执行的代码在内存中的位置,就是由 cs:ip 这组寄存器配合指向的,其中 cs 是基址,ip 是偏移地址。

由于之前执行过一个段间跳转指令,还记得不?

jmpi go,0x9000

这个指令用另一种伪代码表示就是:

cs = 0x9000
ip = go

所以现在 cs 寄存器里的值就是 0x9000,ip 寄存器里的值是 go 这个标签的偏移地址。那么刚刚说的三个 mov 指令就分别给 ds、es 和 ss 寄存器赋值为了 0x9000,也就是 cs 寄存器里的值。

ds 是数据段寄存器,作为访问内存数据时的基地址。之前我们说过了,当时它被赋值为 0x07c0,是因为之前的代码在 0x7c00 处,现在代码已经被挪到了 0x90000 处,所以现在自然又改赋值为 0x9000 了。

es 是扩展段寄存器,先不用理它。

ss 是栈段寄存器,后面要配合栈指针寄存器 sp 来表示此时的栈顶地址。而此时 sp 寄存器被赋值为 0xFF00 了,所以目前的栈顶地址,就是 ss:sp 所指向的地址 0x9FF00 处。

我们再把开头的图解回顾一下:

图片

嗯,栈顶地址其实之前的图中就已经画出来了,但没解释为什么,现在你知道它的来源了吧?至于栈是什么,栈顶又是什么,如果你完全没有概念,那就先别放在心上,知道它有用就好了。

CPU 访问内存的三种途径

刚刚讲一堆寄存器的作用,总结一下就是:CPU 访问内存有三种途径——访问代码的 cs:ip,访问数据的 ds:XXX,以及访问栈的 ss:sp。

其中, cs 作为访问指令的代码段寄存器,被赋值为了 0x9000。ds 作为访问数据的数据段寄存器,也被赋值为了 0x9000。ss 和 sp 作为栈段寄存器和栈指针寄存器,分别被赋值为了 0x9000 和 0xFF00,由此计算出栈顶地址 ss:sp 为 0x9FF00,之后的压栈和出栈操作就以这个栈顶地址为基准。

概括来说,这一部分其实就是把代码段寄存器 cs数据段寄存器 ds栈段寄存器 ss栈指针寄存器 sp 分别设置好了值,方便后续使用。

我们再从操作系统的角度拔高一下,其实操作系统正在做的事儿就是初步规划了内存,给程序如何访问代码、如何访问数据还有如何访问栈制定了规则。其中访问代码和访问数据的规划方式,就是设置了一个基址;访问栈就是把栈顶指针,指向了一个远离代码位置的地方,仅此而已。

图片

所以,千万别多想,就这么点事儿。

总结时刻

好了,到这里,操作系统的一些最最最最基础的准备工作,就做好了。我们再从头回顾一下前3讲的内容。

第1讲:代码从硬盘移到内存,又从内存挪了个地方,放在了 0x90000 处。

第2讲:数据段寄存器 ds代码段寄存器 cs 此时都被设置为了 0x9000,也就是为跳转代码和访问内存数据,设置了一个内存的基址地址,以方便代码的编写。

第3讲:栈顶地址被设置为了 0x9FF00,具体表现为栈段寄存器 ss 为 0x9000,栈指针寄存器 sp 为 0xFF00。

好了,接下来我们应该干什么呢?回忆一下,我们目前仅仅把硬盘中 512 字节加载到内存中了,但操作系统还有很多代码仍然在硬盘里,不能抛下他们不管呀。所以下一步,自然是把仍然在硬盘里的操作系统代码“请”到内存中来。

好,我是闪客。后面的世界越来越精彩,欲知后事如何,且听下回分解。