Linux后端程式成长关键技术---底层体系结构
计算机程式的Bug千奇百怪,要想能顺利的解决疑难杂症,必须对计算机的底层原理非常熟悉。比如在实际生产中不光我们的应用会出问题,操作系统也可能有Bug,硬件也可能有Bug。因此,只有更加深入的理解了原理,才能更加方便我们解决问题。
本文对计算机的体系结构底层原理进行简要的介绍。这些知识对于帮助我们解决疑难问题会有很大的帮助。做程式开发应该深入原理,不仅要知其然,还要知其所以然。
计算机的工作模式

对于一个计算机来说,最核心的是CPU,CPU是计算机的大脑,所有装置都围绕其展开CPU通过总线(Bus)与其他装置连线,在这些装置中,最为重要的是内存(Memory)单靠CPU是无法完成计算任务的,很多复杂的计算任务都需要将中间结果储存下来,然后基于中间结果进行下一步的计算CPU和内存是完成计算的核心元件CPU本身无法储存这么多的中间结果,因此需要依赖于内存
CPU
CPU包含三部分:运算单元、资料单元和控制单元运算单元只管计算,但它不知道应该算哪些资料,运算结果应该放在哪里运算单元计算的资料如果每次都要经过总线,直接到内存里面现拿,速度会很慢,因此出现了资料单元资料单元包括CPU内部的快取和暂存器组,空间很小,但速度很快控制单元是一个统一的指挥中心,可以获得下一条指令,然后执行这条指令这个指令会指导运算单元取出资料单元中的某几个资料,计算出结果,然后放在资料单元的某个地方计算过程
1. 每个程序都有一个程式放在硬盘上,是二进位制的,在里面储存的是一行一行的指令,这些指令会操作一些资料2. 程序开始执行,会有独立的内存空间,相互隔离但不连续 - 程式会分别载入到程序A和程序B的内存空间里面,形成各自的程式码段
3. 程式在执行过程中要操作的资料和产生的计算结果,都会放在资料段(内存)里

4. 在CPU的控制单元里面,有一个指令指标暂存器,记录的是下一条指令在内存中的地址 - 控制单元会不停地将程式码段的指令拿进来,先放入指令暂存器
5. 指令的组成部分:做什么操作 + 操作哪些资料 - 要执行指令,需要将第一部分交给运算单元,将第二部分交给资料单元
6. 资料单元根据资料的地址,从资料段里读取资料到资料暂存器,最终会有指令将资料写回到内存中的资料段
7. CPU里有两个暂存器,专门储存当前处理程序的程式码段起始地址和资料段起始地址,图中的当前程序为程序A
8. CPU和内存通过总线传输资料,总线上有两类资料 - 地址总线(Address Bus):地址资料,位数决定了能访问的地址有多广 - 资料总线(Data Bus):真正的资料,位数决定了一次效能拿多少资料
x86架构
型号
8086的原理

通用暂存器
为了暂存资料,8086处理器内部有8个16位的通用暂存器,属于CPU内部的资料单元分别是AX、BX、CX、DX、SP、BP、SI和DI其中AX、BX、CX和DX可以分成两个8位的暂存器来使用,其中H就是High,L就是Low这样,比较长的资料也能暂存,比较短的资料也能暂存控制单元
IP暂存器(Instruction Pointer Register)即指令指标暂存器- 指向程式码段中下一条指令的位置
- CPU会根据IP暂存器不断地将指令从内存的程式码段中,载入到CPU的指令伫列中,然后交给运算单元去执行
切换程序- 每个程序都分为程式码段和资料段
- 为了指向不同程序的地址空间,有4个16位的段暂存器,分别是CS、DS、SS和ES
CS(Code Segment Register)是程式码段暂存器,通过它可以找到程式码在内存中的位置DS(Data Segment Register)是资料段暂存器,通过它可以找到资料在内存中的位置SS(Stack Segment Register)是栈暂存器,但凡与函式呼叫相关的操作,都与栈紧密相关- A呼叫B,B呼叫C
- 当A呼叫B的时候,要执行B函式的逻辑,因而A执行的相关资讯会被push到栈里
- 当B呼叫C的时候,同理,B执行的相关资讯会被push到栈里,然后才执行C函式的逻辑
- 当C执行完毕后,先pop出来的是B,B接着呼叫C函式之后的指令执行下去
- B执行完毕后,再pop出来的是A,A接着执行,直至结束

载入内存资料
如果需要载入内存中的资料,可以通过DS找到内存中的资料,载入到通用暂存器对于一个段,有一个起始地址,而段内的具体位置,称为偏移量CS和DS都存放着一个段的起始地址程式码段的偏移量放在IP暂存器资料段的偏移量放在通用暂存器CS和DS都是16位的(起始地址),IP暂存器和通用暂存器也都是16位的(偏移量),但8086的地址总线是20位的凑20位:起始地址 无论真正的内存有多大,对于只有20位地址总线的8086来说,能够区分的地址也就2^20=1M(定址单位为Byte)如果想访问1M+X的地方,在总线上超过20位的部分根本发不出去,最后访问的还是1M内的X位置偏移量只有16位的,所以一个段的最大大小为2^16=64K因此对于8086的CPU来说,最多只能访问1M的内存空间,还要分成多个段,每个段最大为64K
32位处理器
在32位的CPU中,有32根地址总线,可以访问2^32=4G的内存x86架构是开放的,因此32位的CPU需要相容原来的架构相容
1. 通用暂存器 - 将8个16位的通用暂存器扩充套件到8个32位的通用暂存器,但依然保留16位和8位的使用方式 - 高16位不能分成两个8位使用,因为这是不相容的
2. IP暂存器 - 指向下一条指令的指令指标暂存器IP,会扩充套件成32位的,同样相容16位
3. 段暂存器(Segment Register) - CS、DS、SS和ES仍然是16位,但不再是段的起始地址,段的起始地址放在内存的某个地方(表格)
- 表格中的一项是段描述符(Segment Descriptor),里面才是段真正的起始地址 - 而段暂存器里面储存的是这个表格中的某一项,称为选择子(Selector)
- 获取段起始地址的流程:先间接地从段暂存器中找到表格中的一项,再从表格中的一项拿到段真正的起始地址
- 为了快速拿到段的起始地址,段暂存器会从内存中拿到CPU的描述符快取内存器中
- 这种模式与8086的模式不相容,但非常灵活,可以保持未来的相容性
真实模式 VS 保护模式
在32位的架构下,将前一种模式称为真实模式(Real Pattern),后一种模式称为保护模式(Protected Pattern)系统刚刚启动的时候,CPU处于真实模式,此时和原来的模式是相容的。即32位的CPU,也支援在原来的模式下执行,速度会快一点当需要更多内存时,可以遵循一定的规则,进行一系列的操作,然后切换到保护模式,就能够用到32位CPU更强大的能力如果不能无缝相容,但通过切换模式相容,也是可以接受的系统互动

常用汇编指令
mov, call, jmp, int, ret, add, or, xor, shl, shr, push, pop, inc, dec, sub, cmp
本文作者: zhongmingmao
本文连结: http://zhongmingmao.me/2019/04/09/linux-x86/