上卷 程序设计
中卷 标准库
- 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
下卷 运行时
源码剖析
- 1. 初始化
- 2. 内存分配
- 3. 垃圾回收
- 4. 并发调度
- 5. 通道
- 6. 延迟调用
- 7. 终结器
- 8. 其他
附录
文章来源于网络收集而来,版权归原创者所有,如有侵权请及时联系!
4.6.5 扩容
编译器在函数头尾插入相关指令,用欲栈内存扩展。
package main func main() { println("hello, world!") }
$ go build -gcflags "-S" # test "".main STEXT size=86 args=0x0 locals=0x18 0x0000 00000 TEXT "".main(SB), ABIInternal, $24-0 0x0000 00000 MOVQ (TLS), CX // 将当前 G 地址载入 CX。 0x0009 00009 CMPQ SP, 16(CX) // 16(CX) 对应 G.stackguard0,与 SP 比较。 0x000d 00013 JLS 79 // 如果 SP 小于等于 stackguard0,则跳转。(JLS: JBE) 0x000f 00015 SUBQ $24, SP 0x0013 00019 MOVQ BP, 16(SP) 0x0018 00024 LEAQ 16(SP), BP 0x001d 00029 ... 0x0045 00069 MOVQ 16(SP), BP 0x004a 00074 ADDQ $24, SP 0x004e 00078 RET 0x004f 00079 CALL runtime.morestack_noctxt(SB) // 执行栈内存扩张。 0x0054 00084 JMP 0
// runtime2.go type g struct { stack stack stackguard0 uintptr stackguard1 uintptr } type stack struct { lo uintptr hi uintptr }
TLS
头部 MOV (TLS), CX
指令对应 get_tls
宏函数。
// go_tls.h #define get_tls(r) MOVQ TLS, r #define g(r) 0(r)(TLS*1)
// golang.org/doc/asm.html Within the runtime, the get_tls macro loads its argument register with a pointer to the g pointer, and the g struct contains the m pointer. The sequence to load g and m using CX looks like this: get_tls(CX) // Move g into CX. MOVL g(CX), AX // Move g into AX. MOVL g_m(AX), BX // Move g.m into BX.
至于最后 TLS 被翻译成什么样,须看不同平台定义。
// cmd/link/internal/ld/sym.go // computeTLSOffset records the thread-local storage offset. func (ctxt *Link) computeTLSOffset() { /* * ELF uses TLS offset negative from FS. * Translate 0(FS) and 8(FS) into -16(FS) and -8(FS). */ /* * OS X system constants - offset from 0(GS) to our TLS. * For x86, Apple has reserved a slot in the TLS for Go. * That slot is at offset 0x30 on amd64, and 0x18 on 386. */ }
$ GOOS=darwin GOARCH=amd64 go build $ go tool objdump -s "main\.main" test TEXT main.main(SB) MOVQ GS:0x30, CX CMPQ 0x10(CX), SP JBE 0x104df5a
$ GOOS=linux GOARCH=amd64 go build $ go tool objdump -s "main\.main" test TEXT main.main(SB) MOVQ FS:0xfffffff8, CX CMPQ 0x10(CX), SP BE 0x44ea1a
morestack
由汇编实现的 morestack 函数,最终调用 newstack 完成具体操作。
// asm_amd64.s TEXT runtime·morestack_noctxt(SB),NOSPLIT,$0 MOVL $0, DX JMP runtime·morestack(SB)
TEXT runtime·morestack(SB),NOSPLIT,$0-0 // Call newstack on m->g0's stack. MOVQ m_g0(BX), BX MOVQ BX, g(CX) MOVQ (g_sched+gobuf_sp)(BX), SP CALL runtime·newstack(SB) CALL runtime·abort(SB) // crash if newstack returns RET
// stack.go // Called from runtime·morestack when more stack is needed. // Allocate larger stack and relocate to new stack. // Stack growth is multiplicative, for constant amortized cost. func newstack() { thisg := getg() // g0 gp := thisg.m.curg // G // 抢占调度 ... // 分配 2 倍新栈,拷贝数据到新栈。 oldsize := gp.stack.hi - gp.stack.lo newsize := oldsize * 2 casgstatus(gp, _Grunning, _Gcopystack) copystack(gp, newsize) // 恢复 G 运行。 casgstatus(gp, _Gcopystack, _Grunning) gogo(&gp.sched) }
// stack.go // Copies gp's stack to a new stack of a different size. func copystack(gp *g, newsize uintptr) { old := gp.stack used := old.hi - gp.sched.sp // 创建新栈。 new := stackalloc(uint32(newsize)) // 计算新旧栈地址偏移(delta),用于调整指针。 var adjinfo adjustinfo adjinfo.old = old adjinfo.delta = new.hi - old.hi // 拷贝数据到新栈。 ncopy := used memmove(unsafe.Pointer(new.hi-ncopy), unsafe.Pointer(old.hi-ncopy), ncopy) // 调整相关指针。 adjustctxt(gp, &adjinfo) adjustdefers(gp, &adjinfo) adjustpanics(gp, &adjinfo) // 切换到新栈。 gp.stack = new gp.stackguard0 = new.lo + _StackGuard gp.sched.sp = new.hi - used gp.stktopsp += adjinfo.delta // 释放旧栈。 stackfree(old) }
拷贝栈需要调整指针指向。
计算新旧栈基址差(newbase - oldbase),然后修改指针内容(+= delta)。
无需扫描栈内容,因为它们基于栈寄存器(SP、G.sched.sp)寻址。
// stack.go type adjustinfo struct { old stack delta uintptr // ptr distance from old to new stack (newbase - oldbase) cache pcvalueCache // sghi is the highest sudog.elem on the stack. sghi uintptr }
// Adjustpointer checks whether *vpp is in the old stack described by adjinfo. // If so, it rewrites *vpp to point into the new stack. func adjustpointer(adjinfo *adjustinfo, vpp unsafe.Pointer) { pp := (*uintptr)(vpp) p := *pp if adjinfo.old.lo <= p && p < adjinfo.old.hi { *pp = p + adjinfo.delta } }
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论