返回介绍

上卷 程序设计

中卷 标准库

下卷 运行时

源码剖析

附录

4.3.1 G

发布于 2024-10-12 19:16:05 字数 3977 浏览 0 评论 0 收藏 0

编译器将 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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文