上卷 程序设计
中卷 标准库
- bufio 1.18
- bytes 1.18
- io 1.18
- container 1.18
- encoding 1.18
- crypto 1.18
- hash 1.18
- index 1.18
- sort 1.18
- context 1.18
- database 1.18
- connection
- query
- queryrow
- exec
- prepare
- transaction
- scan & null
- context
- tcp
- udp
- http
- server
- handler
- client
- h2、tls
- url
- rpc
- exec
- signal
- embed 1.18
- plugin 1.18
- reflect 1.18
- runtime 1.18
- KeepAlived
- ReadMemStats
- SetFinalizer
- Stack
- sync 1.18
- atomic
- mutex
- rwmutex
- waitgroup
- cond
- once
- map
- pool
- copycheck
- nocopy
- unsafe 1.18
- fmt 1.18
- log 1.18
- math 1.18
- time 1.18
- timer
下卷 运行时
源码剖析
附录
文章来源于网络收集而来,版权归原创者所有,如有侵权请及时联系!
4.3.1 G
编译器将 go func()
语句转换为 newproc 调用。
package main func test(a, b int) { println(a, b) } func main() { go test(11, 22) println() }
$ go build -gcflags "-S" "".test STEXT size=98 args=0x10 locals=0x10 ... "".main STEXT size=104 args=0x0 locals=0x28 MOVL $16, (SP) LEAQ "".test·f(SB), AX MOVQ AX, 8(SP) MOVQ $11, 16(SP) MOVQ $22, 24(SP) CALL runtime.newproc(SB) // func newproc(siz int32, fn *funcval) "".test·f SRODATA dupok size=8 0x0000 00 00 00 00 00 00 00 00 ........ rel 0+8 t=1 "".test+0
| newproc ... | +-------------+ | main IP/PC | callerpc SP +0 +-------------+ | siz = 16 | +8 +-------------+ +----------+ | fn | --------------------------> | funcval | +16 +-------------+ +----------+ | 11 | argp | test.f | +24 +-------------+ +----------+ | 22 | .rodata +32 +-------------+ | ... |
newproc
根据上面的示意图,计算出相关地址信息。
从 newproc 看,go 语句没有返回值。
基于调用规范各参数位置,获取 go 语句参数,以及调用者 IP/PC 地址。
getcallerpc 由编译器插入指令实现,返回 `go func` 语句下一指令位置。
// proc.go // Create a new g running fn with siz bytes of arguments. // Put it on the queue of g's waiting to run. // The compiler turns a go statement into a call to this. func newproc(siz int32, fn *funcval) { argp := add(unsafe.Pointer(&fn), sys.PtrSize) gp := getg() pc := getcallerpc() systemstack(func() { newg := newproc1(fn, argp, siz, gp, pc) // 将 G 放入 P 本地队列 _p_ := getg().m.p.ptr() runqput(_p_, newg, true) // 如果 runtime.main 已初始化(mainStarted = true), // 那么唤醒某个 MP 执行任务。 if mainStarted { wakep() } }) }
新建或复用 G,将目标函数调用参数复制到 G.stack 第一栈帧内。
另外,相关执行状态数据(PC、SP, fn)保存到 G.sched。
// runtime2.go type g struct { stack stack sched gobuf } type stack struct { lo uintptr hi uintptr } type gobuf struct { // The offsets of sp, pc, and g are known to (hard-coded in) libmach. sp uintptr pc uintptr g guintptr ctxt unsafe.Pointer ret sys.Uintreg lr uintptr // ARM bp uintptr // for GOEXPERIMENT=framepointer }
将 G 放入 P 本地队列后,检查相关环境尝试唤醒新的 MP 执行任务。
// proc.go // Create a new g in state _Grunnable, starting at fn, with narg bytes // of arguments starting at argp. callerpc is the address of the go // statement that created this. The caller is responsible for adding // the new g to the scheduler. func newproc1(fn *funcval, argp unsafe.Pointer, narg int32, callergp *g, callerpc uintptr) *g { // 参数长度按 8 字节对齐。 siz := narg siz = (siz + 7) &^ 7 // 从 P 缓存获取复用 dead G,或新建。 _p_ := _g_.m.p.ptr() newg := gfget(_p_) if newg == nil { newg = malg(_StackMin) casgstatus(newg, _Gidle, _Gdead) allgadd(newg) } // 计算栈帧(第一栈帧,存储调用参数)大小,确认 SP 地址。 totalSize := 4*sys.RegSize + uintptr(siz) + sys.MinFrameSize totalSize += -totalSize & (sys.SpAlign - 1) sp := newg.stack.hi - totalSize // 丛高向低 spArg := sp // 将参数复制到 G 栈。 if narg > 0 { memmove(unsafe.Pointer(spArg), argp, uintptr(narg)) } // 将调用信息存储到 sched。 newg.sched.sp = sp newg.sched.pc = funcPC(goexit) + sys.PCQuantum newg.sched.g = guintptr(unsafe.Pointer(newg)) gostartcallfn(&newg.sched, fn) newg.gopc = callerpc newg.startpc = fn.fn casgstatus(newg, _Gdead, _Grunnable) // 设置 G 编号。(参考编号) newg.goid = int64(_p_.goidcache) return newg }
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论