Skip to content

Commit 20d6fb7

Browse files
committed
ch3: 规范化图像名字
1 parent 866ea5e commit 20d6fb7

30 files changed

+13
-101
lines changed

ch3-asm/ch3-02-arch.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@
6565

6666
下图是某一层的任务:将输入数据的0剔除,非0的数据依次输出,右边部分是解决方案。
6767

68-
![](../images/ch3-02-arch-hsm-zero.jpg)
68+
![](../images/ch3.2-1-arch-hsm-zero.jpg)
6969

7070
整个程序只有一个输入指令、一个输出指令和两个跳转指令共四个指令:
7171

@@ -86,7 +86,7 @@ X86其实是是80X86的简称(后面三个字母),包括Intel 8086、80286
8686

8787
在使用汇编语言之前必须要了解对应的CPU体系结构。下面是X86/AMD架构图:
8888

89-
![](../images/ch3-arch-amd64-01.ditaa.png)
89+
![](../images/ch3.2-2-arch-amd64-01.ditaa.png)
9090

9191
左边是内存部分是常见的内存布局。其中text一般对应代码段,用于存储要执行指令数据,代码段一般是只读的。然后是rodata和data数据段,数据段一般用于存放全局的数据,其中rodata是只读的数据段。而heap段则用于管理动态的数据,stack段用于管理每个函数调用时相关的数据。在汇编语言中一般重点关注text代码段和data数据段,因此Go汇编语言中专门提供了对应TEXT和DATA命令用于定义代码和数据。
9292

@@ -101,7 +101,7 @@ Go汇编为了简化汇编代码的编写,引入了PC、FP、SP、SB四个伪
101101

102102
四个伪寄存器和X86/AMD64的内存和寄存器的相互关系如下图:
103103

104-
![](../images/ch3-arch-amd64-02.ditaa.png)
104+
![](../images/ch3.2-3-arch-amd64-02.ditaa.png)
105105

106106
在AMD64环境,伪PC寄存器其实是IP指令计数器寄存器的别名。伪FP寄存器对应的是函数的帧指针,一般用来访问函数的参数和返回值。伪SP栈指针对应的是当前函数栈帧的底部(不包括参数和返回值部分),一般用于定位局部变量。伪SP是一个比较特殊的寄存器,因为还存在一个同名的SP真寄存器。真SP寄存器对应的是栈的顶部,一般用于定位调用其它函数的参数和返回值。
107107

ch3-asm/ch3-03-const-and-var.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ DATA ·num+8(SB)/8,$0
110110

111111
下图是Go语句和汇编语句定义变量时的对应关系:
112112

113-
![](../images/ch3-pkg-var-decl-01.ditaa.png)
113+
![](../images/ch3.3-1-pkg-var-decl-01.ditaa.png)
114114

115115
汇编代码中并不需要NOPTR标志,因为Go编译器会从Go语言语句声明的`[2]int`类型中推导出该变量内部没有指针数据。
116116

@@ -171,7 +171,7 @@ Go汇编语言通常无法区分变量是否是浮点数类型,与之相关的
171171

172172
IEEE754标准中,最高位1bit为符号位,然后是指数位(指数为采用移码格式表示),然后是有效数部分(其中小数点左边的一个bit位被省略)。下图是IEEE754中float32类型浮点数的bit布局:
173173

174-
![](../images/ch3-03-ieee754.jpg)
174+
![](../images/ch3.3-2-ieee754.jpg)
175175

176176
IEEE754浮点数还有一些奇妙的特性:比如有正负两个0;除了无穷大和无穷小Inf还有非数NaN;同时如果两个浮点数有序那么对应的有符号整数也是有序的(反之则不一定成立,因为浮点数中存在的非数是不可排序的)。浮点数是程序中最难琢磨的角落,因为程序中很多手写的浮点数字面值常量根本无法精确表达,浮点数计算涉及到的误差舍入方式可能也的随机的。
177177

@@ -300,13 +300,13 @@ func makechan(chanType *byte, size int) (hchan chan any)
300300

301301
首先查看前面已经见过的`[2]int`类型数组的内存布局:
302302

303-
![](../images/ch3-pkg-var-decl-02.ditaa.png)
303+
![](../images/ch3.3-3-pkg-var-decl-02.ditaa.png)
304304

305305
变量在data段分配空间,数组的元素地址依次从低向高排列。
306306

307307
然后再查看下标准库图像包中`image.Point`结构体类型变量的内存布局:
308308

309-
![](../images/ch3-pkg-var-decl-03.ditaa.png)
309+
![](../images/ch3.3-4-pkg-var-decl-03.ditaa.png)
310310

311311
变量也时在data段分配空间,变量结构体成员的地址也是依次从低向高排列。
312312

ch3-asm/ch3-04-func.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ TEXT ·Swap(SB), NOSPLIT, $0
3838

3939
下图是Swap函数几种不同写法的对比关系图:
4040

41-
![](../images/ch3-func-decl-01.ditaa.png)
41+
![](../images/ch3.4-1-func-decl-01.ditaa.png)
4242

4343
第一种是最完整的写法:函数名部分包含了当前包的路径,同时指明了函数的参数大小为32个字节(对应参数和返回值的4个int类型)。第二种写法则比较简洁,省略了当前包的路径和参数的大小。如果有NOSPLIT标注,会禁止汇编器为汇编函数插入栈分裂的代码。NOSPLIT对应Go语言中的`//go:nosplit`注释。
4444

@@ -79,7 +79,7 @@ TEXT ·Swap(SB), $0-32
7979

8080
下图是Swap函数中参数和返回值在内存中的布局图:
8181

82-
![](../images/ch3-func-decl-02.ditaa.png)
82+
![](../images/ch3.4-2-func-decl-02.ditaa.png)
8383

8484

8585
下面的代码演示了如何在汇编函数中使用参数和返回值:
@@ -136,7 +136,7 @@ func Foo(FP *SomeFunc_args_and_returns) {
136136

137137
Foo函数的参数和返回值的大小和内存布局:
138138

139-
![](../images/ch3-func-arg-01.ditaa.png)
139+
![](../images/ch3.4-3-func-arg-01.ditaa.png)
140140

141141

142142
下面的代码演示了Foo汇编函数参数和返回值的定位:
@@ -208,7 +208,7 @@ func Foo() {
208208

209209
下面是Foo函数的局部变量的大小和内存布局:
210210

211-
![](../images/ch3-func-local-var-01.ditaa.png)
211+
![](../images/ch3.4-4-func-local-var-01.ditaa.png)
212212

213213
从图中可以看出Foo函数局部变量和前一个例子中参数和返回值的内存布局是完全一样的,这也是我们故意设计的结果。但是参数和返回值是通过伪FP寄存器定位的,FP寄存器对应第一个参数的开始地址(第一个参数地址较低),因此每个变量的偏移量是正数。而局部变量是通过伪SP寄存器定位的,而伪SP寄存器对应的是第一个局部变量的结束地址(第一个局部变量地址较大),因此每个局部变量的便宜量都是负数。
214214

@@ -239,7 +239,7 @@ func sum(a, b int) int {
239239

240240
下图展示了三个函数逐级调用时内存中函数参数和返回值的布局:
241241

242-
![](../images/ch3-func-call-frame-01.ditaa.png)
242+
![](../images/ch3.4-5-func-call-frame-01.ditaa.png)
243243

244244
为了便于理解,我们对真实的内存布局进行了简化。要记住的是调用函数时,被调用函数的参数和返回值内存空间都必须由调用者提供。因此函数的局部变量和为调用其它函数准备的栈空间总和就确定了函数帧的大小。调用其它函数前调用方要选择保存相关寄存器到栈中,并在调用函数返回后选择要恢复的寄存器进行保存。最终通过CALL指令调用函数的过程和调用我们熟悉的调用println函数输出的过程类似。
245245

ch3-asm/ch3-06-func-again.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99

1010
和C语言函数不同,Go语言函数的参数和返回值完全通过栈传递。下面是Go函数调用时栈的布局图:
1111

12-
![](../images/ch3-func-stack-frame-layout-01.ditaa.png)
12+
![](../images/ch3.6-1-func-stack-frame-layout-01.ditaa.png)
1313

1414
首先是调用函数前准备的输入参数和返回值空间。然后CALL指令将首先触发返回地址入栈操作。在进入到被调用函数内之后,汇编器自动插入了BP寄存器相关的指令,因此BP寄存器和返回地址是紧挨着的。再下面就是当前函数的局部变量的空间,包含再次调用其它函数需要准备的调用参数空间。被调用的函数执行RET返回指令时,先从栈恢复BP和SP寄存器,接着取出的返回地址跳转到对应的指令执行。
1515

images/ch2-qsort-v2.plantuml

Lines changed: 0 additions & 88 deletions
This file was deleted.

images/ch2-qsort-v2.uml.png

-49.1 KB
Binary file not shown.

0 commit comments

Comments
 (0)