26

深入Go底层原理,重写Redis中间件实战

 1 year ago
source link: https://studygolang.com/articles/35634
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

download百度网盘

什么是plan9汇编

我们知道,CPU是只认二进制指令的,也就是一串的0101;人类无法记住这些二进制码,于是发明了汇编语言。汇编语言实际上是二进制指令的文本形式,它与指令可以一一对应。

每一种CPU指令都是不一样的,因此对应的汇编语言也就不一样。人类写完汇编语言后,把它转换成二进制码,就可以被机器执行了。转换的动作由编译器完成。

Go语言的编译器和汇编器都带了一个-S参数,可以查看生成的最终目标代码。通过对比目标代码和原始的Go语言或Go汇编语言代码的差异可以加深对底层实现的理解。

Go汇编语言实际上来源于plan9汇编语言,而plan9汇编语言最初来源于Go语言作者之一的Ken Thompson为plan9系统所写的C语言编译器输出的汇编伪代码。这里强烈推荐一下春晖大神的新书《Go语言高级编程》,即将上市,电子版的点击阅读原文可以看到地址,书中有一整个章节讲Go的汇编语言,非常精彩!

理解Go的汇编语言,哪怕只是一点点,都能对Go的运行机制有更深入的理解。比如我们以前讲的defer,如果从Go源码编译后的汇编代码来看,就能深刻地掌握它的底层原理。再比如,很多文章都会分析Go的函数参数传递都是值传递,如果把汇编代码秀出来,很容易就能得出结论。

汇编角度看函数调用及返回过程

假设我们有一个这样年幼无知的例子,求两个int的和,Go源码如下:

package main

func main() {
    _ = add(3,5)
}

func add(a, b int) int {
    return a+b
}

使用如下命令得到汇编代码:

go tool compile -S main.go

go tool compile命令用于调用Go语言提供的底层命令工具,其中-S参数表示输出汇编格式。

我们现在只关心add函数的汇编代码:

"".add STEXT nosplit size=19 args=0x18 locals=0x0
        0x0000 00000 (main.go:7)        TEXT    "".add(SB), NOSPLIT, $0-24
        0x0000 00000 (main.go:7)        FUNCDATA        $0, gclocals·54241e171da8af6ae173d69da0236748(SB)
        0x0000 00000 (main.go:7)        FUNCDATA        $1, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB)
        0x0000 00000 (main.go:7)        MOVQ    "".b+16(SP), AX
        0x0005 00005 (main.go:7)        MOVQ    "".a+8(SP), CX
        0x000a 00010 (main.go:8)        ADDQ    CX, AX
        0x000d 00013 (main.go:8)        MOVQ    AX, "".~r2+24(SP)
        0x0012 00018 (main.go:8)        RET

看不懂没关系,我目前也不是全部都懂,但是对于理解一个函数调用的整体过程而言,足够了。

0x0000 00000 (main.go:7)        TEXT    "".add(SB), NOSPLIT, $0-24

这一行表示定义add这个函数,最后的数字$0-24,其中0表示函数栈帧大小为0;24表示参数及返回值的大小:参数是2个int型变量,返回值是1个int型变量,共24字节。

再看中间这四行:

0x0000 00000 (main.go:7)        MOVQ    "".b+16(SP), AX
        0x0005 00005 (main.go:7)        MOVQ    "".a+8(SP), CX
        0x000a 00010 (main.go:8)        ADDQ    CX, AX
        0x000d 00013 (main.go:8)        MOVQ    AX, "".~r2+24(SP)

代码片段中的第1行,将第2个参数b搬到AX寄存器;第2行将1个参数a搬到寄存器CX;第3行将ab相加,相加的结果搬到AX;最后一行,将结果搬到返回参数的地址,这段汇编代码非常简单,来看一下函数调用者和被调者的栈帧图:

(SP)指栈顶,b+16(SP)表示裸骑1的位置,从SP往上增加16个字节,注意,前面的b仅表示一个标号;同样,a+8(SP)表示实参0;~r2+24(SP)则表示返回值的位置。


有疑问加站长微信联系(非本文作者)

280

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK