【golang源码分析】程序初始化(Centos 7)

本文详细剖析了Go语言在CentOS7平台上的启动流程,从初始化代码到golang的main函数执行过程。通过gdb调试工具,跟踪了从入口点到运行时关键函数的调用路径,包括runtime.check、runtime.args、runtime.osinit等,揭示了Go语言内部运行机制。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1 调试和源码分析

不同的平台,会执行不同的初始化代码,然后跳到golang的main函数。本次验证过程是基于CentOS 7平台。

测试代码如下:

package main
func main() {
    println("hello, wenTao!")
}

执行编码,禁止优化

go build -gcflags "-N -l" main.go

找入口

[root@VM_0_14_centos opt]# gdb main
GNU gdb (GDB) Red Hat Enterprise Linux 7.6.1-80.el7
Copyright (C) 2013 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-redhat-linux-gnu".
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>...
Reading symbols from /opt/main...done.
Loading Go Runtime support.
(gdb) source /usr/lib/golang/src/runtime/runtime-gdb.py 
Loading Go Runtime support.
(gdb) info files
Symbols from "/opt/main".
Local exec file:
        `/opt/main', file type elf64-x86-64.
        Entry point: 0x44a4c0
        0x0000000000401000 - 0x000000000044ec6f is .text
        0x000000000044f000 - 0x0000000000478dd5 is .rodata
        0x0000000000478f80 - 0x0000000000479654 is .typelink
        0x0000000000479658 - 0x0000000000479660 is .itablink
        0x0000000000479660 - 0x0000000000479660 is .gosymtab
        0x0000000000479660 - 0x00000000004b6bfb is .gopclntab
        0x00000000004b7000 - 0x00000000004b7c08 is .noptrdata
        0x00000000004b7c20 - 0x00000000004b9950 is .data
        0x00000000004b9960 - 0x00000000004d5c70 is .bss
        0x00000000004d5c80 - 0x00000000004d8378 is .noptrbss
        0x0000000000400f9c - 0x0000000000401000 is .note.go.buildid
(gdb) b *0x44a4c0
Breakpoint 1 at 0x44a4c0: file /usr/lib/golang/src/runtime/rt0_linux_amd64.s, line 8.
(gdb) 

执行一段汇编代码之后会跳到ok标签(具体参考:Go源码分析1- 引导程序),在文件src/runtime/asm_amd64.s

 

ok:
	// set the per-goroutine and per-mach "registers"
	get_tls(BX)
	LEAQ	runtime·g0(SB), CX
	MOVQ	CX, g(BX)
	LEAQ	runtime·m0(SB), AX

	// save m->g0 = g0
	MOVQ	CX, m_g0(AX)
	// save m0 to g0->m
	MOVQ	AX, g_m(CX)

	CLD				// convention is D is always left cleared
	CALL	runtime·check(SB)

	MOVL	16(SP), AX		// copy argc
	MOVL	AX, 0(SP)
	MOVQ	24(SP), AX		// copy argv
	MOVQ	AX, 8(SP)
	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
	PUSHQ	AX
	PUSHQ	$0			// arg size
	CALL	runtime·newproc(SB)
	POPQ	AX
	POPQ	AX

	// start this M
	CALL	runtime·mstart(SB)

	CALL	runtime·abort(SB)	// mstart should never return
	RET

	// Prevent dead-code elimination of debugCallV1, which is
	// intended to be called by debuggers.
	MOVQ	$runtime·debugCallV1(SB), AX
	RET

执行runtime.check

(gdb) b runtime.check
Breakpoint 2 at 0x434c60: file /opt/go/src/runtime/runtime1.go, line 136.

执行runime.args

(gdb) b runtime.args
Breakpoint 5 at 0x430610: file /usr/lib/golang/src/runtime/runtime1.go, line 60.

执行runtime.osinit

(gdb) b runtime.osinit
Breakpoint 4 at 0x423170: file /opt/go/src/runtime/os_linux.go, line 289.

获取CPU核数和内存页大小

func osinit() {
	ncpu = getproccount()
	physHugePageSize = getHugePageSize()
}

执行runtime.schedinit

(gdb) b runtime.schedinit
Breakpoint 5 at 0x428dc0: file /opt/go/src/runtime/proc.go, line 532.

做了表多的前期工作,有时间慢慢分析,初步计划分析内存管理部分,和gc部分。

// The bootstrap sequence is:
//
//	call osinit
//	call schedinit
//	make & queue new G
//	call runtime·mstart
//
// The new G calls runtime·main.
func schedinit() {
	// 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

	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")
	}

	// 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 = ""
	}
}

 

执行runtime.newproc

(gdb) b runtime.newproc
Breakpoint 6 at 0x42f7a0: file /opt/go/src/runtime/proc.go, line 3371.

执行runtime.mstart

(gdb) b runtime.mstart
Breakpoint 7 at 0x42a3a0: file /opt/go/src/runtime/proc.go, line 1069.

 

参考:

Go源码分析1- 引导程序

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值