深入理解计算机系统第2章,第3章阅读笔记
深入理解计算机系统第2章,第3章阅读笔记
大概阅读了第2章和第3章,记录一些个人看来有用的知识点
大小端
为什么会有大小端这种概念呢?
首先内存的寻址是以字节为单位的,如图。但是比如说在Java中,很多基本数据类型在内存中都不止一个字节,比如说int占4字节,double占8字节。(为什么没有6字节这种奇怪的长度呢?个人认为是为了内存对齐的简洁性。内存对齐会在下面简单介绍)
这些多字节的在内存的排序问题就是大小端问题。某些机器按照从最低到最高的顺序存储对象,另一些机器按照从最高到最低的顺序存储对象。前一种称为小端法,后一种称为大端法。
比如,对于int型整数0x01234567(16进制),他的低位到高位为67 -> 45 -> 23 -> 01,(前面提到内存是以字节为单位寻址的)大端法小端法表示如图:

补码表示
补码表示是我在第2章学到的一个非常重要的概念,在本科学习中,老师传授的只是补码怎么计算出来一些比较生硬的概念,看完第2章的补码部分才真正有所了解。
补码第一位表示负权重,比如1000,第一位表示负权重-8(2的3次方)。对于正数来说,负权重位为0,对于负数来说,负权重为1。举例来说:(最低位到最高位)
- 对于正数
00011, 值为1+2+0+0+0 = 3。 - 对于负数
10011, 值为1+2+0+0+(-16) = -13
两者相加为
-10,具体计算过程为00011 + 10011直接进行位加法得10110,值为0+2+4+0+(-16) = -10。这也是为什么要使用补码的原因之一,负数和正数的加法会比较简单,可以考虑一下如果用原码计算的话需要哪些操作。

机器级代码
第二章的基础之一大概就是这些汇编语言指令,读起来较为吃力,简单总结一下大概有以下几类指令。
- 数据传送指令,以
movl,movq等为代表,具体为mov后面加一个表示数据大小的字母,l表示四字节,q表示8字节 - 栈操作指令,具体为
push,pop,后面同样有表述数据大小的字母。需要注意的是内存中的栈是以高字节向低字节扩展的,栈其实就是内存中的一块区域,%rsp指针记录栈顶指针,时常变化。 - 计算指令。包括加减乘除,移位运算,移位又可以包括算术移位和逻辑移位,具体证明移位之后值的变化的数学理论在第二章中有,书上写得很详细。
- 控制跳转指令。主要指
jmp指令,ret等,包括直接跳转和间接跳转。直接跳转是从直接跳转到内存中的某个位置,而间接跳转是内存中的位置具体来自寄存器。
大概就是这四种,个人认为如果不是专门做这方面的话没有必要详记,简单了解一下即可。
过程控制以及循环
过程控制及循环的话主要指if,for,while,switch等语句的汇编级别实现。
条件跳转if
在编程语言中是不推荐使用goto这种关键字的,但是在汇编语言层面,很重要。我们平常所写的条件跳转的话,转化成机器级别后逻辑会不太一样。对于以下语句
if (test-expr)
then-statement
else
else-statement
在机器级别的指令如下:
t = test-expr;
if (!t)
goto false;
then-statement
goto done;
false:
else-statement
done:
举个求两个数的最大值的例子
func int max (unsigned int a, unsigned int b) {
int max = 0;
if (a >= b) {
max = a;
} else {
max = b;
}
return max;
}
根据上面的理论,机器级别的伪代码逻辑表示为:
func int max (unsigned int a, unsigned int b) {
int max = 0;
boolean test-expr = (a >= b);
if (!test-expr)
goto false;
max = a;
goto done;
false:
max = b;
done:
return max;
}
至于汇编语言层面的,我就不写了,因为其实无非就是这种思想在汇编语言上的复现,在考虑使用一些寄存器的问题。
至于为什么判断(!test-expr),感兴趣的同学可以具体了解一下,这样做的目的是对于只有if的情况,可以一定程度上减少汇编程序的长度,提高运行效率。
循环
循环在Java等语言中有三种形式,分别是有while,do while,for。这三种格式用机器级别的语言表示的话,无非是jmp指令再配合一些其他的指令进行变换。
do-while循环
do-while循环一般形式如下:
int cnt = 0;
do {
cnt++;
} while (cnt < 5)
翻译到机器指令层面,大概执行流程如下:
loop:
do
cnt++;
boolean t = (cnt < 5);
if (t)
goto loop;
while循环
while循环的一般格式如下:
int cnt = 0;
while (cnt < 5) {
cnt++;
}
while循环翻译的话,有两种表示,第一种表示是jump to middle方式,这种方式可以理解为从循环的末尾开始执行,意思是循环从cnt++完后的部分开始执行,大概的执行流程如下:
goto test;
loop:
cnt++;
test:
boolean t =( cnt < 5) ;
if (t)
goto loop;
另一种方式叫guarded-do,GCC在较高编译优化等级的情况下,会使用这种方式。
int cnt = 0;
boolean t = (cnt < 5);
if (!t)
goto done;
loop:
cnt++;
boolean t = (cnt < 5);
if (t)
goto loop;
done:
这种方式可以理解为,进入循环前,先判断是否满足条件,如果不满足,直接退出;如果满足的话,再开始循环。(guarded这个单词的意思上可以体会到这种思想)
for循环
for循环的处理都是转化成while循环进行的。比如对于代码
int cnt = 0;
for(int i = 0; i < 5; i++) {
cnt++;
}
转化为while循环的格式为:
int cnt = 0;
int i = 0;
while (i < 5) {
cnt++;
i++;
}
由于while循环有上面两种格式,所以转化成的结果,也有两种形式。
jump-to-middle:
int cnt = 0;
int i = 0;
goto test;
loop:
cnt++;
i++;
test:
boolean t = (i < 5);
if (t)
goto loop;
guarded-do:
int cnt = 0;
int i = 0;
boolean t = (i < 5);
if (!t)
goto done;
loop:
cnt++;
i++
boolean t = (i < 5);
if (t)
goto loop;
done:
switch语句
switch语句也是利用jmp指令实现的,需要了解的是一个叫跳转表的概念,含义就是由于switch要根据条件跳转到case,所以会把每一条case的地址,记录到一个表中,switch维护了这样一种数据结构,称为跳转表。
函数
在函数中,要解决的问题主要有一下几个:
- 传递控制。比如Q调用P,在调用时,要把程序计数器
PC指针指向P的第一条指令,并记录Q执行到哪一行,然后在返回时能将PC指针还原到Q执行数的下一行。(PC指针指向要执行的下一条指令) - 传递数据。Q调用P,需要传递参数给P,P调用完Q,需要返回给Q一个或者多个数值。
- 分配释放内存。Q调用P,需要给P分配内存,P调用完成,需要释放这些内存。
这些东西基本都是通过在栈上分配内存以及寄存器的配合使用来完成的,具体就不再阐述了。
数组
数组中需要注意的一个点是,不论是一维数组还是多维数组,在内存中的存储都是按照一维存储的,具体的存储方式的话,如图:

内存数据对齐
数据对齐可以提高系统的性能,这也是x86-64硬件支持的。
什么是数据对齐呢,就是某种类型对象的地址必须是K:1,2,4,8的倍数。比如说系统每次读取8的倍数,int类型占4个字节,如果这个int的起始地址不是4的倍数,那么就会将这个数读到两个不同的地方去。如图:


我以书上的一个例子为例。对于C语言中的结构体,如下:
struct {
int i;
char c;
int j
}
如果从上到下按顺序存储的话,这个结构体在内存中的布局理论上是这样的:

但是,其实真正的情况不可能是这样的,因为要进行内存的对齐,如图:

数据的对齐原则如下:
| 要求对齐到内存的倍数是 | 类型 |
|---|---|
| 1 | char |
| 2 | short |
| 4 | int float |
| 8 | double, long |
补充及其他
我们平常会听到很多x86这样的词,包括在安装一些编程软件的时候,到底什么是x86呢?
我在网上扣了一个定义:X86架构(The X86 architecture)泛指一系列基于Intel 8086且向后兼容的中央处理器指令集架构。
8086是16位处理器之一,第一代芯片- 后面经过一系列的发展,比较有名的是
IA32,x86的32位架构一般又被称作IA-32,全名为“Intel Architecture, 32-bit”,他支持32位操作系统。 IA32的64位扩展称为x86-64,也就是现在所支持的64位操作系统
到此为止,后面补充的话就在这个下面补充了。
浙公网安备 33010602011771号