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

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

基本概念

  • 传统并发使用的是:多线程共享内存,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的话说明发生了循环调用。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,033评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,725评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,473评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,846评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,848评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,691评论 1 282
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,053评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,700评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,856评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,676评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,787评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,430评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,034评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,990评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,218评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,174评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,526评论 2 343

推荐阅读更多精彩内容