golang 启动流程
来源:互联网 发布:网络高清摄像头报价 编辑:程序博客网 时间:2024/06/05 20:20
找到启动函数
在linux使用objdump反汇编可以看到golang编译的exe的启动代码
首先使用objdump -f exe 可以看到
start address 0x0808c760
然后使用objdump -d exe > t.asm
打开t.asm文件查找上面的start address
可以看到入口函数是_rt0_386_linux
入口函数分析
接下来就是找到这个入口函数了,这里因为我现在使用的是windows,所以接下的代码就是看的
_rt0_amd64_windows函数了(rt0_windows_amd64.s),linux主要是objdump反汇编比较方便
_rt0_amd64_windows
_rt0_amd64_windows这个函数中将argc argv保存到DI SI 寄存器,然后调用main
main直接调用runtime·rt0_go(asm_amd64.s)
runtime.rt0_go
(asm_amd64.s)
保存AX,BX到栈里面
设置栈pos顶的位置,用于栈的扩大,初始化g0的栈空间。这是整个golang的第一个g
MOVQ $runtime·g0(SB), DILEAQ (-64*1024+104)(SP), BXMOVQ BX, g_stackguard0(DI)MOVQ BX, g_stackguard1(DI)MOVQ BX, (g_stack+stack_lo)(DI)MOVQ SP, (g_stack+stack_hi)(DI)
查询cpu信息
如果有cgo,初始化cgo; 调用setg_gcc(g0),然后更新stackguard。
设置tls windows是设置到GS寄存器里面去了(sys_windows_and64.s runtime·settls(SB))
保存g0到TLS, g0->m = m0 m0->g0 = g0
get_tls(BX)LEAQ runtime·g0(SB), CXMOVQ CX, g(BX)LEAQ runtime·m0(SB), AX // save m->g0 = g0MOVQ CX, m_g0(AX) // save m0 to g0->mMOVQ AX, g_m(CX)
- 调用rumtime.check()检查进行必要的运行时间检查,针对变量长度等。。。(runtime1.go check())
- 重新设置argc和argv到栈顶和栈第二个位置,然后调用
runtime.args(c int32, v **byte)
runtime1.go 保存argc和argv到全局变量
runtime.osinit()
osinit() os_windows.go
runtime.schedinit()
schedinit() proc.go
runtime.newproc(0, runtime.mainPC )
runtime.mstart()
schedinit()
这里主要初始化stack 内存 args env gc 等等
if procresize(procs) != nil { throw("unknown runnable goroutine during bootstrap")}
创建P数组,MAXPROC个,同时设置当前的M的p。并且将多余的p设置为pidle状态并增加sched.npidle(通过pidleput函数实现)
runtime.newproc(0, runtime.mainPC )
func newproc(siz int32, fn *funcval)
创建一个新的运行函数为fn的g,且fn的参数长度为siz。并且获取caller’s pc和argp,然后调用systemstack在系统栈上执行newproc1函数。
func newproc1(fn *funcval, argp *uint8, narg int32, nret int32, callerpc uintptr) *g
创建一个fn为函数,argp为参数 narg为参数个数 nret为返回值,callerpc是发起这次创建的地址(如果是在go语言中创建,就是go语句的位置
siz := narg + nretsiz = (siz + 7) &^ 7// We could allocate a larger initial stack if necessary.// Not worth it: this is almost always an error.// 4*sizeof(uintreg): extra space added below// sizeof(uintreg): caller's LR (arm) or return address (x86, in gostartcall).if siz >= _StackMin-4*sys.RegSize-sys.RegSize { throw("newproc: function arguments too large for new goroutine")}
创建goroutinue的时候只分配初始大小的栈,如果参数argp的大小大于这个初始大小,则会报错。
totalSize := 4*sys.RegSize + uintptr(siz) + sys.MinFrameSize // extra space in case of reads slightly beyond frame //设置栈指针 totalSize += -totalSize & (sys.SpAlign - 1) // align to spAlign sp := newg.stack.hi - totalSize spArg := sp if usesLR { // caller's LR *(*uintptr)(unsafe.Pointer(sp)) = 0 prepGoExitFrame(sp) spArg += sys.MinFrameSize } if narg > 0 { //拷贝参数到goroutinue栈 memmove(unsafe.Pointer(spArg), unsafe.Pointer(argp), uintptr(narg)) // This is a stack-to-stack copy. If write barriers // are enabled and the source stack is grey (the // destination is always black), then perform a // barrier copy. We do this *after* the memmove // because the destination stack may have garbage on // it. if writeBarrier.needed && !_g_.m.curg.gcscandone { f := findfunc(fn.fn) stkmap := (*stackmap)(funcdata(f, _FUNCDATA_ArgsPointerMaps)) // We're in the prologue, so it's always stack map index 0. bv := stackmapdata(stkmap, 0) bulkBarrierBitmap(spArg, spArg, uintptr(narg), 0, bv.bytedata) } } memclrNoHeapPointers(unsafe.Pointer(&newg.sched), unsafe.Sizeof(newg.sched)) newg.sched.sp = sp newg.stktopsp = sp newg.sched.pc = funcPC(goexit) + sys.PCQuantum // +PCQuantum so that previous instruction is in same function newg.sched.g = guintptr(unsafe.Pointer(newg)) gostartcallfn(&newg.sched, fn)
gostartcallfun
函数设置caller's
PC到LR或者是SP,然后设置gobuf.pc = fn; gobuf.sp = sp,这样就可以假装是从goexit调用过来的,以便结束的时候回到goexit进行最后的清理工作,同时当goroutinue被换入的时候,pc回复fn,SP也会恢复
newg.gopc = callerpcnewg.startpc = fn.fn
最后将newg放入就绪队列。
if atomic.Load(&sched.npidle) != 0 && atomic.Load(&sched.nmspinning) == 0 && runtimeInitTime != 0 { wakep()}
这个wakep的功能是如果有pidle状态的P,则新建一个M来执行P
这里因为还没有调用runtime.main()函数出初始化runtimeInitTime,所以本次调用并不会触发wakep,所以这个goroutinue会继续在最初的线程执行。
其中runtimeInitTime的初始化在runtime.main的这一句完成。runtimeInitTime = nanotime()
如果是系统启动之后调用newproc,且设置的maxproc大于1,则会有调用wakep来创建新的M了
runtime.mstart()
初始化g0的栈大小 然后调用mstart1 保存g0的栈 然后schedule
这个时候因为前面设置了一个就绪的goroutinue,所以就会执行那个goroutinue,并执行mainPC函数
schedule里面会设置m的curg位即将要执行的g,并调用gogo切换pc和sp等
runtime.mainPC
由asm_amd64.s中可以看到mainPC其实就是runtime.main
DATA runtime·mainPC+0(SB)/8,$runtime·main(SB)GLOBL runtime·mainPC(SB),RODATA,$8
在这里启动一个sysmon进程
进行初始化 runtime_init()
使能gc gcenable()
如果cgo,还要初始化cgo的运行时环境
调用main_init
调用main_main
如果有正在pancing的状态,还要调用然后调用gopark()
这个函数的作用是发起一次schedule,可以让panic的goroutinue有机会打印完panic信息。 (这种情况要从panic那里直接程序就exit了?)
gopark
gopark
的作用是让当前让出m,别进行一次调度
设置m的wait状态,然后调用mcall(park_m)
mcall是在g0栈上调用函数
park_m
把gp的状态转为waiting(gp是调用gopark的goroutinue)
同时将m.curg和m分离
然后schedule,执行别的goroutinue
- golang 启动流程
- golang web bbs 项目启动
- 启动流程
- 启动流程
- golang之web编程执行流程
- golang基础-字符串、日期、指针、流程
- golang学习之web服务流程分析
- Uboot启动流程和Kernel启动流程
- Android启动:Linux启动流程
- Android启动:Linux启动流程
- Android启动:Linux启动流程
- Eclipse的启动流程
- 流程启动(Jbpm)
- pmon启动流程
- i386 Redboot启动流程
- Linux启动流程
- linux启动流程
- uboot启动流程
- Echarts折线图平滑问题
- jenkins 无法启动tomcat
- Unity手机上查看日志的插件LogViewer
- 【MyBatis学习18】使用注解配置Mapper
- Caused by: kafka.common.OffsetOutOfRangeException
- golang 启动流程
- [牛客]字符串移位练习题
- 必须使用初始化列表的情况
- C++之虚函数与纯虚函数
- Uboot详解——板子刚上电时都干了些什么
- 修改系统默认语言发现buchengg
- 二叉树:二叉链表
- Mac 下制作加密的zip压缩包
- 初学CSS笔记