前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >go源码剖析1 初始化

go源码剖析1 初始化

原创
作者头像
历久尝新
修改2020-05-15 14:38:03
4110
修改2020-05-15 14:38:03
举报
文章被收录于专栏:学而时习之

前言

实际上, 编译好的二进制文件的执行入口并非我们所写的main.main函数, 因为编译器会插入一段引导代码,用来完成准备操作,eg命令行参数 运行时初始化等

命令行 go build -gcflags "-N -l" -o xxx xxx.go 编译后使用gdb查看发现在创建main goroutine之前会调用初始化函数

代码语言:javascript
复制
runtime.args()
runtime.osinit()
runtime.schedinit()

初始化

1 先康康runtime.args()

代码语言:javascript
复制
func args(c int32, v **byte) {
   argc = c
   argv = v
   sysargs(c, v)
}

整理命令行参数, 没什么好看的

2 康康runtime.osinit()

代码语言:javascript
复制
func osinit() {
   ncpu = getproccount()
   physHugePageSize = getHugePageSize()
   osArchInit()
}

确定了 cpu core的数量, 也没什么好看

3. 康康最主要的runtime.schedinit()

为啥说主要看他,因为几乎我们所要关注的所有运行时环境初始化构造都是在这里被调用的

所以说他做了啥事? 1.线程的最大数量限制; 2.初始化栈 内存分配器 调度器; 3. 处理命令行参数和环境变量; 4. 垃圾回收器初始化; 5.通过cpu core和gomaxprocs环境变量确定p的数量; 6. 调整p的数量

procl.go

代码语言:javascript
复制
func schedinit() {
   // 设置了线程的最大数量限制
   sched.maxmcount = 10000
   // 栈初始化
   tracebackinit()
   stackinit()
   // 内存分配器初始化
   mallocinit()
   // 调度器初始化
   mcommoninit(_g_.m)
   // 处理命令行参数和环境变量
   goargs()
   goenvs()
   // 处理GODEBUG调试相关环境变量
   parsedebugvars()
   // 垃圾回收初始化
   gcinit()
   ...

   sched.lastpoll = uint64(nanotime())
   // 通过cpu core 和 gomaxprocs 环境变量确定p数量
   procs := ncpu
   if n, ok := atoi32(gogetenv("GOMAXPROCS")); ok && n > 0 {
      procs = n
   }
   // 调整p的数量
   if procresize(procs) != nil {
      throw("unknown runnable goroutine during bootstrap")
   }
   ...
}

关于内存, 垃圾回收, 并发调度器等后续在学习, 至此需要执行runtime.main 并非用户自己的main.main

4.关注一下包初始化函数init的执行

proc.go

代码语言:javascript
复制
func main() {
   ...
   // 执行栈的最大限制, 1gb on 64bit; 250m on 32bit
   if sys.PtrSize == 8 {
      maxstacksize = 1000000000
   } else {
      maxstacksize = 250000000
   }
   ...
   
   //  启动系统后台监控,(定期垃圾回收,以及并发任务调度相关信息)
   if GOARCH != "wasm" { // no threads on wasm yet, so no sysmon
      systemstack(func() {
         newm(sysmon, nil)
      })
   }
   ...
   
   // 执行runtime包内所有初始化函数
   runtime_init()
   ...
   
   // 启动垃圾回收器后台操作
   gcenable()
   ...
   
   // 执行所有用户包(包括标准库)初始化函数init
   main_init()
   ...
   
   // 执行用户逻辑入口
   main_mian()
   ...
   
   // 执行结束返回退出状态码
   exit(0)
}

与之相关的就是runtime_init 与 main_init 两个函数, 他们都是有编译器动态生成的

实际上通过反汇编工具可以看到, runtime内相关多个init函数被赋予唯一符号名, 然后再由runtime.init进行同一调用

至于main.init 情况基本一直, 区别在于他负责调用非runtime包的初始化函数,调用的是自定义包和标准库中的

总结

1. 所有init都在同一个goroutine内执行

2. 所有init函数结束后才会执行main.main函数

3. 强烈建议init只做该做的事情: 局部初始化

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • 初始化
    • 1 先康康runtime.args()
      • 2 康康runtime.osinit()
        • 3. 康康最主要的runtime.schedinit()
          • 4.关注一下包初始化函数init的执行
          • 总结
          领券
          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档