Linux 从头学 01:CPU 是如何执行一条指令的?
扫描二维码
随时随地手机看文章
【Linux 从头学】是什么
这两年多以来,我的本职工作重心一直是在 x86 Linux 系统这一块,从驱动到中间层,再到应用层的开发。
这张表描述了Linux系统中几个段描述符信息。
这么一个庞然大物,如何下手才能真正的学好Linux呢?!
一句话:以基础知识为主!
- 先确定最终目标的目标:学习 Linux 操作系统;
- 这几本书写的都是汇编语言,以及比较基础的底层知识。我们会淡化汇编语言部分,把重点放在与 Linux 操作系统有关联的原理部分;
- 不会严格按照书中的内容、顺序来输出文章,而是把几本书中内容相关的部分放在一起学习、讨论;
- 有些内容,可以与 Linux 2.6 版本中的相关部分进行对比分析,这样的话在以后学习 Linux 内核部分时,可以找到底层的支撑;
- 最后,希望我自己能坚持这个系列,也算是给自己的一个梳理吧。
现在就开始吧!
古老的 Intel8086 处理器
8086是Intel公司的第一款16位处理器,诞生于1978年,应该比各位小伙伴的年龄都大一些。
也就是说:在8086处理器的内部,能够一次性处理、传输、暂时存储的最大长度是16位,因此,我们说它是 16 位结构的 CPU。
- 运算器一次最多可以处理 16 位的数据;
- 寄存器的最大宽度为 16 位;
- 寄存器和运算器之间的通路为 16 位;
主存储器是什么?
计算机的本质就是对数据的存储和处理,那么参与计算的数据是从哪里来的呢?那就是一个称作 存储器(Storage 或 Memory)的物理器件。
CPU就通过地址总线来确定:对内存中的哪一个存储单元中的数据进行访问。
寄存器是什么?
在CPU内部,一些都是代表 0 或 1 的电信号,这些二进制数字的一组电信号出现在处理器内部线路上,它们是一排高低电平的组合,代表着二进制数中的每一位。
8086中的寄存器都是16位的,可以存放2个字节,或者说1个字。高字节在前(bit8 ~ bit15),低字节在后(bit0 ~ bit7)。
刚才说了,这些寄存器都是16位的。由于需要与以前更古老的处理器兼容,其中的4个寄存器:AX、BX、CX、DX 还可以当成 2 个 8 位的寄存器来使用。
mov AL, 5D 表示把 5D 送入 AL 寄存器(8 位)
三个总线
当我们启动一个应用程序的时候,这个程序的代码和数据都被加载到物理内存中。
在计算机中,有专门连接CPU和其他芯片的数据,称为总线。
- 确定存储单元的地址(地址信息);
- 器件的选择,读或写的命令(控制信息);
- 读或写的数据(数据信息);
地址总线:用来确定存储单元的地址;
控制总线: CPU 对外部器件进行控制;
数据总线: CPU 与内存或其他器件之间传送数据;
8086 有20根地址线,称作地址总线的宽度,它可以寻址 2 的 20 次方个内存单元。
CPU 如何对内存进行寻址?
在Linux 2.6内核代码中,编译器产生的地址叫做虚拟地址(也称作:逻辑地址),这个逻辑地址经过段转换之后,变成线性地址,线性地址再经过分页转换,就得到最终物理内存上的物理地址。
还记得文章开头的那张段描述符的表格吗?
第一个16位的地址称为段地址,第二个16位的地址称为偏移地址。
物理地址 = 段地址 x 16 偏移地址例如:我们编写的程序,在加载到内存中之后,放在一个内存空间中。
我们是如何控制 CPU 的?
CPU其实是一个很纯粹、很呆板的一个东西,它唯一做的事情就是:到 CS:IP 这两个寄存器指定的内存单元中取出一条指令,然后执行这条指令:
当然了,还需要预先定义一套指令集,在内存中的指令区中,存储的都必须是合法的指令,否则 CPU 就不认识了。
- 扫描所有的输入端口,锁存在输入映象区;
- 执行一个运算、控制逻辑,得到一些列输出信号,锁存到输出映象区;
- 把输出映象区的信号,刷新到输出端口;
在一个全新的 PLC 中,其中第 2 个步骤中需要的运算、控制逻辑可能就不存在。
对于CPU来说,想让它执行某个内存单元的指令,只要修改寄存器CS和IP即可。
CPU 执行指令流程
现在我们已经明白了地址转换、内存的寻址,距离CPU执行一条指令需要的最小单元还剩下:指令缓冲区和控制电路。
以第一条指令来举例,它一共经过5个步骤:
以上就是一条指令的执行最基本步骤,当然,现代处理器的指令执行流程,比这里的要复杂的多得多。
- 把 CS:IP 内容送入地址加法器,计算得到 20 位的物理地址 20000H;
- 控制电路把 20 位的地址,送入到地址总线;
- 内存中 20000H 单元处的指令 B8 23 01,经过数据总线被送到指令缓冲区;
- 指令偏移寄存器 IP 的值要加 3,指向下一条等待被执行的偏移地址(因为指令码 B8 代表当前指令的长度是 3 个字节);
- 执行指令缓冲区中的指令: 把数值 0123H 送入寄存器 AX 中;
------ End ------
万丈高楼平地起!





