Go-基础篇-图解启动过程

410 阅读2分钟

启动过程

环境: go1.8

流程图概览

Go语言启动流程 (1).png

步骤解释

  • 入口文件 runtime/asm_amd64.s
    • 根据不同的机器对应不同的文件,本文以mac电脑为例
  • 启动入口函数 - _rt0_amd64
    • 这是大部分在使用的64位操作系统的通用启动代码,
  • JMP runtime·rt0_go(SB) 跳到该函数
    • 复制入口参数进入栈中
    • 设置tls线程本地存储
    • 启动G0/启动M0建立关联关系 image.png
    • runtime.check - 运行时检查
    • args(argc/argv)参数处理
    • 调用runtime.args处理
    • 调用runtime.osinit
      • 获取CPU数量
      • 获取物理内存页的大小PageSize,为了将来mallocinit使用 image.png
    • 调用runtime.schedinit 调度器初始化 - 该处应为启动的核心逻辑(进行各种运行时组件初始化工作,这包括我们的调度器与内存分配器、回收器的初始化)
    // The bootstrap sequence is:
    //
    //	call osinit
    //	call schedinit
    //	make & queue new G
    //	call runtime·mstart
    //
    // The new G calls runtime·main.
    func schedinit() {
    	lockInit(&sched.lock, lockRankSched)
    	lockInit(&sched.sysmonlock, lockRankSysmon)
    	lockInit(&sched.deferlock, lockRankDefer)
    	lockInit(&sched.sudoglock, lockRankSudog)
    	lockInit(&deadlock, lockRankDeadlock)
    	lockInit(&paniclk, lockRankPanic)
    	lockInit(&allglock, lockRankAllg)
    	lockInit(&allpLock, lockRankAllp)
    	lockInit(&reflectOffs.lock, lockRankReflectOffs)
    	lockInit(&finlock, lockRankFin)
    	lockInit(&trace.bufLock, lockRankTraceBuf)
    	lockInit(&trace.stringsLock, lockRankTraceStrings)
    	lockInit(&trace.lock, lockRankTrace)
    	lockInit(&cpuprof.lock, lockRankCpuprof)
    	lockInit(&trace.stackTab.lock, lockRankTraceStackTab)
    	// Enforce that this lock is always a leaf lock.
    	// All of this lock's critical sections should be
    	// extremely short.
    	lockInit(&memstats.heapStats.noPLock, lockRankLeafRank)
    
    	// raceinit must be the first call to race detector.
    	// In particular, it must be done before mallocinit below calls racemapshadow.
    	_g_ := getg()
    	if raceenabled {
    		_g_.racectx, raceprocctx0 = raceinit()
    	}
    
    	sched.maxmcount = 10000 // 限制最大系统线程数量
    
    	// The world starts stopped.
    	worldStopped()
    
    	moduledataverify()
    	stackinit()  // 栈初始化
    	mallocinit() // 内存分配器初始化
    	cpuinit()      // must run before alginit
    	alginit()      // maps, hash, fastrand must not be used before this call
    	fastrandinit() // must run before mcommoninit
    	mcommoninit(_g_.m, -1)
    	modulesinit()   // provides activeModules
    	typelinksinit() // uses maps, activeModules
    	itabsinit()     // uses activeModules
    	stkobjinit()    // must run before GC starts
    
    	sigsave(&_g_.m.sigmask)
    	initSigmask = _g_.m.sigmask
    
    	if offset := unsafe.Offsetof(sched.timeToRun); offset%8 != 0 {
    		println(offset)
    		throw("sched.timeToRun not aligned to 8 bytes")
    	}
    
    	goargs()
    	goenvs()
    	parsedebugvars()
    	gcinit()    // 垃圾回收器初始化
    
    	lock(&sched.lock)
    	sched.lastpoll = uint64(nanotime())
    	
    	// 创建 P
        // 通过 CPU 核心数和 GOMAXPROCS 环境变量确定 P 的数量
    	procs := ncpu
    	if n, ok := atoi32(gogetenv("GOMAXPROCS")); ok && n > 0 {
    		procs = n
    	}
    	if procresize(procs) != nil {
    		throw("unknown runnable goroutine during bootstrap")
    	}
    	unlock(&sched.lock)
    
    	// World is effectively started now, as P's can run.
    	worldStarted()
    
    	// For cgocheck > 1, we turn on the write barrier at all times
    	// and check all pointer writes. We can't do this until after
    	// procresize because the write barrier needs a P.
    	if debug.cgocheck > 1 {
    		writeBarrier.cgo = true
    		writeBarrier.enabled = true
    		for _, p := range allp {
    			p.wbBuf.reset()
    		}
    	}
    
    	if buildVersion == "" {
    		// Condition should never trigger. This code just serves
    		// to ensure runtime·buildVersion is kept in the resulting binary.
    		buildVersion = "unknown"
    	}
    	if len(modinfo) == 1 {
    		// Condition should never trigger. This code just serves
    		// to ensure runtime·modinfo is kept in the resulting binary.
    		modinfo = ""
    	}
    }
    
    • newproc 负责根据主 goroutine (即main)入口地址创建可被运行时调度的执行单元
    • msstart 开始启动调度器的调度循环

参考