第六学 linux内核——内存寻址——简介

时间:2022-03-01 06:50:28

        操作系统首先是一组软件,这是大家必须首先要搞清的问题。那么这是什么样的一组软件呢?它是沟通用户和硬件的一组软件。那么作为操作系统的设计者,它能不能只关心软件的实现呢,由操作系统作为用户和硬件的媒介这点来说,显然不行。于是我们说,操作系统是一组充分挖掘了硬件设计机制的软件,即我们依赖一个体系结构来设计我们的操作系统。这样就有了一个问题,若我们整个操作系统都依赖某一体系结构来设计的话,它的硬件潜能却是能发挥到最大,但这毫无疑问带来了操作系统的可移植性的降低,甚至根本不可移植。因而作为操作系统的设计者,我们必须清楚的在硬件相关的代码和硬件无关的代码之间划清界限,这事关你的操作系统的可移植性。linux以强大的可移植性著称,所以在这点上做得相当好(linux整个内核代码在usr/src下,其硬件相关的代码就在该目录下的arch目录下,在这个目录下你能找到linux支持的各种体系结构硬件部分的代码)。

        作为操作系统软件的实现者,我们不大关心这个体系结构的运算节点是怎么运作的,我们更多的关心我们的代码怎么放在内存中执行,即内存寻址问题。所以我们着重讨论内存寻址问题,这里以x86体系结构为例。

        第一个问题:为什么会存在内存寻址问题?

        设我们只有一个4G大小的内存,没有任何寻址机制,那么作为程序员,我们必须清楚的知道自己的每一条指令对应的内存单元,这对程序员来说是很辛苦的事。8086引入了一个非常重要的概念——段。8086用一个段寄存器来实现了一个简单的地址映射机制,用户编程可以不在基于内存的物理地址,而直接基于这个段中存放的虚拟地址,8086用一个简单的加法器实现了逻辑地址到物理地址的映射,即段寄存器值左移四位和程序的逻辑地址相加(8086是20位寻址空间,段寄存器是16位)。x86体系结构其地址总线和数据总线都是32位的,但由于要保证向下兼容(系列机的商业模式导致的)所以段寄存器仍然是16位的,但16位寄存器无法存储32位地址作为基址,所以它的地址映射做得比较复杂。那我们回顾一下问题:为什么会存在内存寻址问题?一个很给力的答案:就是为了解放程序员,可以让我们做到硬件无关的编程。

        第二个问题:x86的寄存器

        学过汇编的童鞋都知道,我们若想用汇编编程,必须知道的就是该结构的寄存器相关知识。作为操作系统的设计,因为它硬件相关,所以也必须知道这点,而且在linux内核源码中就有用嵌入式汇编完成的一些功能。

        x86结构有8个32位的通用寄存器,并且向下兼容,也就是我们可以访问到它的半字和字节部分的值。具体可以看相关的文档,下面我们讲内存寻址有关的寄存器。

        x86结构有6个16位的段寄存器,上边说过,由于它不能存放32位的基地址,所以其地址映射机制做得相当复杂。既然不能存32位基址,那它存啥?存的是一个叫选择符的东西,而这个选择符中存着一段程序的基址。具体的段机制我们以后再讲,这里只是大概描述一下。

        指令指针寄存器存放的是下一条指令的偏移量,这个偏移量是相对于目前正在运行的代码段寄存器而言的,偏移量通过段寄存器的映射基址,就能确定它在内存中的物理地址。

        下面介绍四个分页机制相关的寄存器,先提醒一下,这些寄存器是相当重要的。

        x86有4个32位的用于分页控制的寄存器:CR0, CR1, CR2, CR3。

        CR0中包含了6个预定义标志,这里只介绍内核中用到的0位和31位。31位用于分页允许,为1时表示开启分页机制,为0时表示不用分页机制,这时只用段机制实现地址映射。第0位是保护允许位, 为1时启动保护模式。

        CR1目前没用。

        CR2是缺页线性地址寄存器,保存最后一次出现缺页的全32位线性地址。

        CR3就非常重要了。它就相当于段寄存器,不过它是32位的,它实现了分页机制中逻辑地址到物理地址的映射。分页机制中再细细介绍。

        第三个问题:逻辑地址、线性地址、物理地址

        逻辑地址即程序员的编程空间所用的地址,之所以说它是逻辑地址是因为段寄存器的存在,我们程序员编程几乎不用管任何地址,只是在加载程序的时候会把每条指令的偏移量拿去用段寄存器做映射,从而存到内存中(若无分页机制存在的话)。

        线性地址是指一段连续的,不分段的,范围从0到4GB的地址空间,一个线性地址就是线性地址空间中的一个绝对地址。

        物理地址就是我们的内存的实际地址。

        把逻辑地址映射到物理地址是通过一个硬件单元实现的,8086里边是一个简单的加法器,而x86里边把它替换成了一个比较复杂的电路,叫MMU(内存管理单元):它包含两个部件,一个分段部件,完成逻辑地址到线性地址的转换;一个分页部件,完成线性地址到物理地址的映射。