分享
  1. 首页
  2. 文章

golang 源码剖析(1): 运行初始化和包初始化

darcyaf · · 1432 次点击 · · 开始浏览
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

初始化中主要对命令行参数整理,环境变量设置,以及内存分配器,垃圾回收器,并发调度器的工作现场准备

基本概念

  • 传统并发使用的是:多线程共享内存,go 使用的是CSP(communicating sequential processes)并发模型,以通信的方式来共享内存.
    go 中使用GPM方式来实现CSP,每个M关联一个P,,goroutine关联哪个P是无法控制的,P中维护了一个goroutin的列表,并用循环的方式取出一个G来关联上P来执行程序.
    // The main concepts are:
    // G - goroutine.
    // M - worker thread, or machine.
    // P - processor, a resource that is required to execute Go code.
    // M must have an associated P to execute Go code, however it can be
    // blocked or in a syscall w/o an associated P.

入口

随便写个demo debug,调用栈如下

main.main at main.go:15
runtime.main at proc.go:203
runtime.goexit at asm_amd64.s:1357
 - Async stack trace
runtime.rt0_go at asm_amd64.s:220

即go进程先启动runtime.main,然后才执行main.main
然后就可以新建一个G和M 开始运行程序.
runtime/asm_amd64.s

// The new G calls runtime·main.
 call runtime·args(sb)
 call runtime·osinit(sb)
 call runtime·schedinit(sb)
 // create a new goroutine to start program
 MOVQ $runtime·mainPC(SB), AX // entry
 // start this M
 CALL runtime·mstart(SB)
在runtime

scheduinit 这里启动了一个M(最大10000个),ncpu个P,初始化了一系列东西

// The bootstrap sequence is:
//
// call osinit
// call schedinit
// make & queue new G
// call runtime·mstart
 tracebackinit()
 moduledataverify()
 stackinit()
 mallocinit()
 mcommoninit(_g_.m)
 cpuinit() // must run before alginit
 alginit() // maps must not be used before this call
 modulesinit() // provides activeModules
 typelinksinit() // uses maps, activeModules
 itabsinit() // uses activeModules
 msigsave(_g_.m)
 initSigmask = _g_.m.sigmask
 goargs()
 goenvs()
 parsedebugvars()
 gcinit()
 sched.lastpoll = uint64(nanotime())
 procs := ncpu
 if n, ok := atoi32(gogetenv("GOMAXPROCS")); ok && n > 0 {
 procs = n
 }
 if procresize(procs) != nil {
 throw("unknown runnable goroutine during bootstrap")
 }

包init流程

src/cmd/compile/internal/gc/noder.go这个路径下,这里会对先解析所有的文件
gc.go中lines := parseFiles(flag.Args())调用解析文件函数,最终会生成一个pkgMap:map[string]*Pkg
接着查找所有的init方法并改名为init.0,init.1累加的名字,屏蔽了init函数防止调用,生成一个.inittask方法方便调用
在gc/init.go中

 nf := initOrder(n) //检查初始化时的包循环引用等问题
 // Record user init functions. 为函数生成别名
 for i := 0; i < renameinitgen; i++ {
 s := lookupN("init.", i)
 fns = append(fns, s.Linksym())
 }
 // Make an .inittask structure.
 sym := lookup(".inittask")
 nn := newname(sym)

src/runtime/proc.go中:

 //go:linkname runtime_inittask runtime..inittask
 var runtime_inittask initTask
 //go:linkname main_inittask main..inittask
 var main_inittask initTask
 doInit(&runtime_inittask) // must be before defer
 doInit(&main_inittask)
 fn := main_main // make an indirect call, as the linker doesn't know the address of the main package when laying down the runtime
 fn()

由此调用init之后再进入main.main入口

包init顺序

go中建议对包的引用只做初始化,不涉及包的顺序
先初始化依赖包,然后对同一个包下多文件按字母顺序初始化,同一文件从上到下init
src/cmd/compile/internal/gc/initorder.go中

// Assignments are in one of three phases: NotStarted, Pending, or
// Done. For assignments in the Pending phase, we use Xoffset to
// record the number of unique variable dependencies whose
// initialization assignment is not yet Done. We also maintain a
// "blocking" map that maps assignments back to all of the assignments
// that depend on it.
//
// For example, for an initialization like:
//
// var x = f(a, b, b)
// var a, b = g()
//
// the "x = f(a, b, b)" assignment depends on two variables (a and b),
// so its Xoffset will be 2. Correspondingly, the "a, b = g()"
// assignment's "blocking" entry will have two entries back to x's
// assignment.

这里的例子中,如果x依赖于a和b,那么x.off+=2,然后将x 添加到blocking[a]和blocking[b]的列表中去。
当a,b初始化完了以后会将x的offset--1。
最后再判断offset,如果offset不为0的话说明发生了循环调用。


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

本文来自:简书

感谢作者:darcyaf

查看原文:golang 源码剖析(1): 运行初始化和包初始化

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

关注微信
1432 次点击
添加一条新回复 (您需要 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传

用户登录

没有账号?注册
(追記) (追記ここまで)

今日阅读排行

    加载中
(追記) (追記ここまで)

一周阅读排行

    加载中

关注我

  • 扫码关注领全套学习资料 关注微信公众号
  • 加入 QQ 群:
    • 192706294(已满)
    • 731990104(已满)
    • 798786647(已满)
    • 729884609(已满)
    • 977810755(已满)
    • 815126783(已满)
    • 812540095(已满)
    • 1006366459(已满)
    • 692541889

  • 关注微信公众号
  • 加入微信群:liuxiaoyan-s,备注入群
  • 也欢迎加入知识星球 Go粉丝们(免费)

给该专栏投稿 写篇新文章

每篇文章有总共有 5 次投稿机会

收入到我管理的专栏 新建专栏