go程序启动过程总结

in cn •  6 months ago 

Go程序启动流程

大体流程主要是:
编译 -> 加载准备 (初始化环境:内存分配器,调度器,垃圾回收器等模块)
-> 运行 (调用main函数) -> 程序退出(资源释放,关闭协程等)。
image.png

源代码调试

func main() {
    fmt.Println("hello world")
}
  1. 首先编译源代码GOFLAGS="-ldflags=-compressdwarf=false" go build -gcflags "-N -l" main.go
    Mac上的交叉编译,可以使用GOFLAGS="-ldflags=-compressdwarf=false" GOOS=linux GOARCH=arm64 go build -gcflags "-N -l" main.go。
  2. 安装调试工具gdb
  3. gdb ./main 调试可执行文件。

image.png

   172: // just before we're about to start letting user code run.
   173: // It kicks off the background sweeper goroutine, the background
   174: // scavenger goroutine, and enables GC.
   175: func gcenable() {
   176:         // Kick off sweeping and scavenging.
=> 177:         c := make(chan int, 2)
   178:         go bgsweep(c)
   179:         go bgscavenge(c)
   180:         <-c
   181:         <-c
   182:         memstats.enablegc = true // now that runtime is initialized, GC is okay
(dlv) goroutines
* Goroutine 1 - User: /Users/cotox/go1.20/src/runtime/mgc.go:177 runtime.gcenable (0x104076138) (thread 3776358)
  Goroutine 2 - User: /Users/cotox/go1.20/src/runtime/proc.go:382 runtime.gopark (0x1040944c4) [force gc (idle)]
[2 goroutines]


   175: func gcenable() {
   176:         // Kick off sweeping and scavenging.
   177:         c := make(chan int, 2)
   178:         go bgsweep(c)
=> 179:      go bgscavenge(c)
   180:         <-c
   181:         <-c
   182:         memstats.enablegc = true // now that runtime is initialized, GC is okay
   183: }
   184: 
(dlv) goroutines
* Goroutine 1 - User: /Users/cotox/go1.20/src/runtime/mgc.go:179 runtime.gcenable (0x10407618c) (thread 3776358)
  Goroutine 2 - User: /Users/cotox/go1.20/src/runtime/proc.go:382 runtime.gopark (0x1040944c4) [force gc (idle)]
  Goroutine 17 - User: /Users/cotox/go1.20/src/runtime/proc.go:382 runtime.gopark (0x1040944c4) [GC sweep wait]
[3 goroutines]


=> 182:         memstats.enablegc = true // now that runtime is initialized, GC is okay
   183: }
   184: 
   185: // Garbage collector phase.
   186: // Indicates to write barrier and synchronization task to perform.
   187: var gcphase uint32
(dlv) goroutines
* Goroutine 1 - User: /Users/cotox/go1.20/src/runtime/mgc.go:182 runtime.gcenable (0x1040761e4) (thread 3776358)
  Goroutine 2 - User: /Users/cotox/go1.20/src/runtime/proc.go:382 runtime.gopark (0x1040944c4) [force gc (idle)]
  Goroutine 17 - User: /Users/cotox/go1.20/src/runtime/proc.go:382 runtime.gopark (0x1040944c4) [GC sweep wait]
  Goroutine 18 - User: /Users/cotox/go1.20/src/runtime/proc.go:382 runtime.gopark (0x1040944c4) [GC scavenge wait]
[4 goroutines]

GC的启动,是在执行runtime.main函数时候,通过方法gcenable(),分别使用go bgsweep(c)和go bgscavenge(c)启动GC运行。

并且在runtime.main里调用用户的main函数入口执行。通过代码:

   249:         fn := main_main // make an indirect call, as the linker doesn't know the address of the main package when laying down the runtime
=> 250:         fn()

image.png


m0是什么,有什么作用

  • m0 是Go Runtime创建的第一个系统线程,也就是主线程
  • 声明方式和其他m线程一样( 普通的m线程是调度器P将可执行g调度给工作线程)
  • m0会调度执行g0

goroutine的类型

  1. 启动时执行调度任务的叫g0(系统线程)
  2. 执行用户任务的叫做g
  3. 执行runtime.main的main goroutine(第一个用户级协程)

在Go语言中,g0(也称为Goroutine 0)是一个特殊的Go协程(goroutine),它在程序启动时创建,并且在程序的整个生命周期内一直存在。

g0有一些独特的特性和作用:

  1. 启动和初始化:g0负责启动程序的执行,包括调用runtime.main函数,这是程序的入口点。
  2. 系统调度:g0不参与正常的Go协程调度。它主要用于执行系统级的调度任务,如垃圾回收(GC)。
  3. 垃圾回收:g0在执行垃圾回收时扮演重要角色。它不会执行用户代码,但在GC过程中,它会暂停其他协程的执行。
  4. 程序退出:g0还负责程序的退出流程。当程序结束时,g0会执行清理工作,包括关闭所有打开的资源和退出程序。

image.png

执行runtime.main函数的goroutine是程序启动时创建的第一个用户级别的协程
它是从g0中启动的,但它与g0有以下区别:

image.png

  1. 用户代码执行:执行runtime.main的goroutine负责执行用户的初始化代码和主函数。
  2. 调度:这个goroutine可以被调度器挂起和恢复,与普通用户协程一样。
  3. 生命周期:执行runtime.main的goroutine的生命周期与程序的执行周期相同,但它不是g0。当程序结束时,这个goroutine会结束执行。
  4. 并发:尽管runtime.main是程序的入口点,但用户可以在程序中创建其他协程来实现并发。

总结来说,g0和执行runtime.main的goroutine不是同一个。g0是一个特殊的系统协程,负责启动程序和执行系统任务,而执行runtime.main的goroutine是程序的第一个用户级别的协程,负责执行用户的初始化代码和主逻辑。两者在程序中扮演不同的角色,并有不同的生命周期和行为

g0和普通goroutine的区别?

  1. 栈分配区别: g0是系统栈上分配systemstack,linux默认8M,常规g runtime上栈默认2kb,可以扩缩容)
  2. 功能上区别: g0运行在操作系统线程上,主要执行协程调度;每个m只有一个g0,只绑定一个g0; 常规g执行用户任务,被等待调度。
  3. 执行流程上区别:g0固定的调度执行逻辑;g是根据任务代码逻辑执行不同。
Authors get paid when people like you upvote their post.
If you enjoyed what you read here, create your account today and start earning FREE STEEM!