一、搭建环境
在线上环境,我们都是用的Unix系统,但是在平时的开发过程中,我们可能用的是Mac。为了保持环境的统一,我们首先需要使用docker构建一个centos的容器,并在该容器上进行实验。
Dockerfile
FROM centos
RUN cd /etc/yum.repos.d/
RUN sed -i 's/mirrorlist/#mirrorlist/g' /etc/yum.repos.d/CentOS-*
RUN sed -i 's|#baseurl=http://mirror.centos.org|baseurl=http://vault.centos.org|g' /etc/yum.repos.d/CentOS-*
RUN yum install golang -y \
&& yum install dlv -y \
&& yum install binutils -y \
&& yum install vim -y \
&& yum install gdb -y
构建镜像:docker build -t test .
运行容器:docker run -it --rm test bash
二、源码到可执行文件
本小节的目的:了解源码到可执行文件的整个过程。
首先,我们编译一个简单的hello.go
文件,具体内容如下:
go
package main
import "fmt"
func main() {
fmt.Println("hello world")
}
我们想想,编译器与链接器根据程序员写的代码,将其翻译成可执行文件。
- 首先,他两需要知道程序员写的源码是什么?有没有错误?是否能够被优化?
- 其次,将其编译成底层汇编语言(中间文件)。
- 最后,将多个中间文件链接为可执行文件。(软件系统也是如此啊。倡导组件化,像搭积木一样构建负责系统和这里出发点类似吧)

【注】作为应用开发人员,具体细节不用深究,需要的时候再详细了解即可
需要了解一下几个指令:
- 获取编译详情:
go build -x hello.go
- 输出汇编代码:
go tool compile -N -l -S hello.go
- 反编译
go tool objdump -S -s "main.main" hello
三、Dlv调试
本小节的目的:了解如何使用DLV以及利用DLV熟悉代码流程;
ReadELF获取程序入口地址

通过对可执行文件执行上述操作,可以获取可执行程序的入口地址;
DLV调试
下面所有的描述,都按照我们想做什么,然后执行什么指令的格式来描述;
- 我们想运行hello的可执行文件,所以执行
dlv exec ./hello
- 我们想在RealELF文件Entry point address位置打断电,所以执行
b *0x465760
- 我们想在调度函数
schedule
打断点,所以执行b schedule
- 我们想运行程序,执行
c
运行到第一个断点,再次运行c
到schedule
; - 我们想查看此时的函数栈,执行
stack
,此时会打印函数栈; - 我们想看看dlv还有哪些用法,执行
h
,查看介绍;

通过上述方法,我们知道Go函数的入口为runtime.rt0_go
,在这个函数中:
runtime.newproc
创建一个协程,并放在p.Next
;runtime.schedule
进行调用执行协程;
【注】创建协程放哪里,以及如何找可执行协程的后面再详细介绍
四:了解一下有用网站
- 语法分析:将Golang代码翻译为抽象语法树:astexplorer.net/;
- 代码生成汇编的整个过程:golang.design/gossa
- 代码编译成汇编代码(这个网站挺好时的 ):godbolt.org/
- Golang语法相关的问题来这里找:go.dev/ref/spec
参考
- 草春晖《高级Go工程师》
- Go语言底层原理剖析
- Go语言设计与实现