Golang程序的入口点和启动代码详解

Golang程序的入口点和启动代码详解 我是一名C程序员,了解C程序是如何编译并链接成可执行文件的。例如,在Windows和MSVC中,有一个mainCRTStartup函数,它是可执行文件的入口点。这个函数位于C运行时库中,它负责准备argcargv,然后调用main函数。

对于Go语言,我不太清楚Go程序是如何编译并链接成可执行文件的。Go程序的入口点是什么?入口或启动函数做了什么?启动代码位于哪里?有人能给我一些启发或指出相关的资源吗?非常感谢!

5 回复

是的。前两个答案并不是我真正想要的。

更多关于Golang程序的入口点和启动代码详解的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


我认为程一平是在询问Windows下Go程序的实际加载过程。

或 func init(){}

func main() {
    fmt.Println("hello world")
}

当你运行 main 包时,默认会执行一个 main 函数,例如,在 main 包中包含你的 main() 函数的文件,例如 main.go

package main

func main() {
  // 一切从这里开始...
}

要编译并运行你的应用程序: #: go run main.go 要编译并将你的应用程序构建为可执行文件: #: go build main.go

如果你的 main.go 使用了同一目录下的其他 Go 文件,你需要使用“go run .”。作为入口点,只能有一个 main() 函数。

main() 函数是你的启动函数,它只执行你指定的操作,这意味着如果需要,你需要在此处自行读取和准备参数。还有一个可选的 init() 函数,它会在 main() 之前被调用。

在Go语言中,程序的入口点不是由运行时库提供的,而是由Go工具链直接生成的。Go程序的入口点位于Go运行时内部,具体是runtime包中的rt0_*.s文件(汇编文件),根据目标平台不同而有所差异。

入口点位置

Go程序的入口点通常命名为_rt0_<arch>(例如_rt0_amd64_linux),它位于Go运行时库的汇编启动文件中。这些文件在Go源码树的src/runtime目录下,例如:

  • Linux x86-64: src/runtime/rt0_linux_amd64.s
  • Windows x86-64: src/runtime/rt0_windows_amd64.s
  • macOS ARM64: src/runtime/rt0_darwin_arm64.s

启动流程

入口点会执行以下关键步骤:

  1. 初始化运行时:调用runtime·argsruntime·osinit设置命令行参数和操作系统相关初始化。
  2. 调度器初始化:调用runtime·schedinit初始化Go调度器、内存分配器等核心组件。
  3. 创建主goroutine:通过runtime·newproc创建执行main.main的goroutine。
  4. 启动调度器:调用runtime·mstart开始调度,最终执行main.main

代码示例

以下是一个简化的Linux x86-64入口点汇编代码(rt0_linux_amd64.s):

TEXT _rt0_amd64_linux(SB),NOSPLIT,$-8
    MOVQ    0(SP), DI  // argc
    LEAQ    8(SP), SI  // argv
    JMP     runtime·rt0_go(SB)

TEXT runtime·rt0_go(SB),NOSPLIT,$0
    // 保存argc和argv
    MOVQ    DI, AX        // argc
    MOVQ    SI, BX        // argv
    // 调用运行时初始化
    CALL    runtime·args(SB)
    CALL    runtime·osinit(SB)
    CALL    runtime·schedinit(SB)
    // 创建main goroutine
    MOVQ    $runtime·mainPC(SB), AX
    PUSHQ   AX
    CALL    runtime·newproc(SB)
    POPQ    AX
    // 启动调度器
    CALL    runtime·mstart(SB)
    RET

其中runtime·mainPC指向runtime.main函数,该函数会进一步调用用户定义的main.main

用户层视角

从用户代码角度看,程序从main包的main函数开始:

package main

func main() {
    // 用户代码
}

但实际执行前,Go运行时已经完成了上述初始化工作。

编译链接

Go工具链(如go build)会直接将运行时和用户代码静态链接为一个独立的可执行文件,无需外部C运行时。可以通过go tool nm查看可执行文件的符号,找到入口点:

go tool nm ./program | grep '_rt0_'

总结:Go程序的入口点由运行时汇编代码定义,负责初始化Go运行时环境,最终调用用户编写的main.main函数。这种设计使得Go程序不依赖外部运行时库,生成独立的可执行文件。

回到顶部