读Linux内核源码情景分析(一)

听完邱老师讲的内核态与用户态一课后,自己收获很多,当时邱老师问了我两个问题。

你知道你目前所写的代码跑在哪个进程上?你知道你在用户态写的代码为什么不能直接调用内核态接口,为什么要一层系统调用吗?

当时,自己很懵逼,大写的懵逼,我从来没考虑过我写的程序在哪跑,我就只知道,没有bug,能跑就算OK。于是,我默默的拿起了这本书,疯狂自己自补~

一、Linux内核简介:



二、intelX86CPU寻址方式(实模式)



从文章不难知道,Intel最早是先设计增加了段寄存器,也就设计出了分段式管理,段寻址后面说,这里先解释下16位如何变20位地址。

有两个地址,一个是有地址总线来的16位的地址加上段寄存器的内部地址,生成一个新的地址,

也就是段的基地址,拿到段基地址之后,便可在段表中查找对应的段内偏移,得到最终的访问内存的地址,这个地址也是物理内存中的绝对地址。那么CPU就可归纳数据总线的地址:


8086得CPU在内存寻址方面第一次引入了一个非常重要的概念—-段。在8086之前都是4位机和8位机的天下,那是并没有段的概念。当程序要访问内存时都是要给出内存的实际物理地址,这样在程序源代码中就会出现很多硬编码的物理地址。段寄存器的产生源于Intel 8086 CPU体系结构中数据总线与地址总线的宽度不一致。也就是为了实现16位8086 CPU实现20位地址总线位宽。为了支持分段机制,Intel在8086的CPU里新增了4个寄存器,分别是代码段CS,数据段DS,堆栈段SS和其他ES。这样一来,一个物理地址就由两个部分组成,分别是“段地址”:“段内偏移量”。在实模式中,通常寻址时都是通过段寄存器+通用寄存器,即基址+变址的方式进行寻址。例如,ES=0x1000,DI=0xFFFF,那么这个数据ES:DI在内存里的绝对物理地址就是:

AD(Absolute Address)=(ES)*(0x10)+(DI)=0x1FFFF 

段基地址左移4位然后加上段内偏移量就得到了物理内存里的绝对地址,经过这么一个变换,就可以得到一个20位的地址,8086就可以对20位的1M内存空间进行寻址了。


很明显,这种方式可以寻址的最高地址为0xFFFF:0xFFFF,其地址空间为0x00000~0x10FFEF,因为8086的地址总线是20位,最大只能访问到1MB的物理地址空间,即物理地址空间是0x00000~0xFFFFF。当程序访问0x100000~0x10FFEF这一段地址时,因为其逻辑上是正常的,CPU并不会认为其访问越界而产生异常,但这段地址确实没有实际的物理地址与其对应,怎么办?此时CPU采取的策略是,对于这部分超出1M地址空间的部分,自动将其从物理0地址处开始映射。也就是说,系统计算实际物理地址时是按照对1M求模运算的方式进行的,在有些技术文献里你会看到这种技术被称之为wrap-around。还是通过一幅图来描述一下吧: 


根据前面的讲解我们可以发现段基址有个特征,其低4位全为0,也就是说每个段的起始地址一定是16的整数倍,这是分段的一个基本原则。这样每个段的最小长度是16字节,而最大长度只能是64KB。这里我们可以计算一下,1MB的物理地址空间能划分成多少个段。

如果每个段的长度为16字节,这样1MB物理地址空间最多可以划分成64K个段;

如果每个段的长度为64KB,那么1MB的物理地址空间最多能划分成16个段。

8086这种分段基址虽然实现了寻址空间的提升,但是也带来一些问题:

  • 同一个物理地址可以有多种表示方法。例如0x01C0:0x0000和0x0000:0x1C00所表示的物理地址都是0x01C00。
  • 地址空间缺乏保护机制。对于每一个由段寄存器的内容确定的“基地址”,一个进程总是能够访问从此开始64KB的连续地址空间,而无法加以限制。另一方面,可以用来改变段寄存器内容的指令也不是什么“特权指令”,也就是说,通过改变段寄存器的内容,一个进程可以随心所欲地访问内存中的任何一个单元,而丝毫不受限制。不能对一个进程的内存访问加以限制,也就谈不上对其他进程以及系统本身的保护。与此相应,一个CPU如果缺乏对内存访问的限制,或者说保护,就谈不上什么内存管理,也就谈不上是现代意义上的中央处理器。

8086和后来的80186,这种只能访问1MB地址空间的工作模式,我们将其称之为“实模式”。我的理解就是“实际地址模式”,因为通过段基址和段偏移算出来的地址,经过模1MB之后得出来的地址都是实际内存的物理地址。

虽然现在CPU已经发展到了64位的酷睿6代,但是仍然保持着实模式这个工作模式。CPU的实模式是为了与8086处理器兼容而设置的。在实模式下,CPU处理器就相当于一个快速的8086处理器。CPU处理器被复位或加电的时候以实模式启动。这时候处理器中的各寄存器以实模式的初始化值工作。CPU处理器在实模式下的存储器寻址方式和8086基本一致,由段寄存器的内容乘以16作为基地址,加上段内的偏移地址形成最终的物理地址,这时候它的32位地址线只使用了低20位,即可访问1MB的物理地址空间。在实模式下,CPU处理器不能对内存进行分页机制的管理,所以指令寻址的地址就是内存中实际的物理地址。在实模式下,所有的段都是可以读、写和执行的。实模式下CPU不支持优先级,所有的指令相当于工作在特权级(即优先级0),所以它可以执行所有特权指令,包括读写控制寄存器CR0等。这实际上使得在实模式下不太可能设计一个有保护能力的操作系统。实模式下不支持硬件上的多任务切换。实模式下的中断处理方式和8086处理器相同,也用中断向量表来定位中断服务程序地址。中断向量表的结构也和8086处理器一样,每4个字节组成一个中断向量,其中包括两个字节的段地址和两个字节的偏移地址。应用程序可以任意修改中断向量表的内容,使得计算机系统容易受到病毒、木马等的攻击,整个计算机系统的安全性无法得到保证。

三、i386的也是内存管理机制


  在页式系统中,指令所给出的地址分为两部分:逻辑页号和页内地址。

        原理:CPU中的内存管理单元(MMU)按逻辑页号通过查进程页表得到物理页框号,将物理页框号与页内地址相加形成物理地址(见图4-4)。
        逻辑页号,页内偏移地址->查进程页表,得物理页号->物理地址:


上述过程通常由处理器的硬件直接完成,不需要软件参与。通常,操作系统只需在进程切换时,把进程页表的首地址装入处理器特定的寄存器中即可。一般来说,页表存储在主存之中。这样处理器每访问一个在内存中的操作数,就要访问两次内存:

       第一次用来查找页表将操作数的 逻辑地址变换为物理地址;

       第二次完成真正的读写操作。       

      这样做时间上耗费严重。为缩短查找时间,可以将页表从内存装入CPU内部的关联存储器(例如,快表) 中,实现按内容查找。此时的地址变换过程是:在CPU给出有效地址后,由地址变换机构自动将页号送人快表,并将此页号与快表中的所有页号进行比较,而且这 种比较是同时进行的。若其中有与此相匹配的页号,表示要访问的页的页表项在快表中。于是可直接读出该页所对应的物理页号,这样就无需访问内存中的页表。由于关联存储器的访问速度比内存的访问速度快得多。

四、Linux内核源码中C语言


五、Linux内核源码中汇编语言


猜你喜欢

转载自blog.csdn.net/Agoni_xiao/article/details/78540898