Golang探索之旅-GMP(二)重要的数据结构

474 阅读2分钟

结构体

g

// 代码位置:$GOROOT/src/runtime/runtime2.go
type g struct {
        // 记录该goroutine使用的栈
	stack       stack   // offset known to runtime/cgo
        // 下面两个成员用于栈溢出检查,实现栈的自动伸缩,抢占调度也会用到stackguard0
	stackguard0 uintptr // offset known to liblink
	stackguard1 uintptr // offset known to liblink
	
        ...
	_panic         *_panic // innermost panic - offset known to liblink
	_defer         *_defer // innermost defer
	m              *m      // current m; offset known to arm liblink
	sched          gobuf   //goroutine切换时,利用sched域来保存上下文
        lockedm        muintptr //G被锁定只能在这个m上运行
	goid           int64
        ...        
}

m

// 代码位置:$GOROOT/src/runtime/runtime2.go
type m struct {
        // g0主要用来记录工作线程使用的栈信息,在执行调度代码时需要使用这个栈
        // 执行用户goroutine代码时,使用用户goroutine自己的栈,调度时会发生栈的切换
	g0      *g     // goroutine with scheduling stack
      
	...
        // 通过TLS实现m结构体对象与工作线程之间的绑定
        tls           [6]uintptr   // thread-local storage (for x86 extern register)
        mstartfn      func()
        // 指向工作线程正在运行的goroutine的g结构体对象
	curg          *g       // current running goroutine
        // 记录与当前工作线程绑定的p结构体对象
	p             puintptr // attached p for executing go code (nil if not executing go code)
	nextp         puintptr
	oldp          puintptr // the p that was attached before executing a syscall

        // 记录所有工作线程的一个链表
	alllink       *m // on allm
	...
	mcache        *mcache
        // 自旋状态标识
        spinning      bool // m is out of work and is actively looking for work
	...
        // 没有goroutine需要运行时,工作线程睡眠在这个park成员上,
        // 其它线程通过这个park唤醒该工作线程
        park          note
        ...
}

p

// 代码位置:$GOROOT/src/runtime/runtime2.go
type p struct {
	id          int32
	status      uint32 // one of pidle/prunning/...
	m           muintptr   // back-link to associated m (nil if idle)
	mcache      *mcache

	...
	// Queue of runnable goroutines. Accessed without lock.
        //本地goroutine运行队列
	runqhead uint32
	runqtail uint32
	runq     [256]guintptr
	runnext guintptr
        
	// Available G's (status == Gdead)
        // 干净/可复用的G列表
	gFree struct {
	   gList
	   n int32
	}
	...       
}

gobuf

// 代码位置:$GOROOT/src/runtime/runtime2.go
type gobuf struct {
    sp   uintptr  // 保存CPU的rsp寄存器的值
    pc   uintptr  // 保存CPU的rip寄存器的值
    g    guintptr // 记录当前这个gobuf对象属于哪个goroutine
    ctxt unsafe.Pointer
 
    // 保存系统调用的返回值,因为从系统调用返回之后如果p被其它工作线程抢占,
    // 则这个goroutine会被放入全局运行队列被其它工作线程调度,其它线程需要知道系统调用的返回值。
    ret  sys.Uintreg  
    lr   uintptr
 
    // 保存CPU的rip寄存器的值
    bp   uintptr // for GOEXPERIMENT=framepointer
}

schedt

// schedt结构体用来保存调度器的状态信息和goroutine的全局运行队列
// 代码位置:$GOROOT/src/runtime/runtime2.go
type schedt struct {
    // 由空闲的工作线程组成链表
    midle        muintptr // idle m's waiting for work
    // 空闲的工作线程的数量
    nmidle       int32    // number of idle m's waiting for work
    nmidlelocked int32    // number of locked m's waiting for work
    mnext        int64    // number of m's that have been created and next M ID
    // 最多只能创建maxmcount个工作线程
    maxmcount    int32    // maximum number of m's allowed (or die)
    ...

    // 由空闲的p结构体对象组成的链表
    pidle      puintptr // idle p's
    // 空闲的p结构体对象的数量
    npidle     uint32
    nmspinning uint32 // See "Worker thread parking/unparking" comment in proc.go.

    // Global runnable queue.
    // goroutine全局运行队列
    runq     gQueue
    runqsize int32

    ...
    // Global cache of dead G's.
    // gFree是所有已经退出的goroutine对应的g结构体对象组成的链表
    // 用于缓存g结构体对象,避免每次创建goroutine时都重新分配内存
    gFree struct {
        lock          mutex
        stack        gList // Gs with stacks
        noStack   gList // Gs without stacks
        n              int32
    }
    ...
}

_defer

// 代码位置:$GOROOT/src/runtime/runtime2.go
type _defer struct {
   siz     int32 // includes both arguments and results
   started bool
   heap    bool
   sp      uintptr // sp at time of defer
   pc      uintptr
   fn      *funcval
   _panic  *_panic // panic that is running defer
   link    *_defer
}

全局变量

// 代码位置:$GOROOT/src/runtime/runtime2.go
var (
        allm      *m       // 所有的m构成的一个链表,包括下面的m0
        allp      []*p     // 保存所有的p,len(allp) == gomaxprocs

        ncpu       int32   // 系统中cpu核的数量,程序启动时由runtime代码初始化
        gomaxprocs int32   // p的最大值,默认等于ncpu,但可以通过GOMAXPROCS修改

        sched      schedt     // 调度器结构体对象,记录了调度器的工作状态
        ...
)

// 代码位置:$GOROOT/src/runtime/proc.go
var (
        m0   m       // 代表进程的主线程
        g0   g       // m0的g0,也就是m0.g0 = &g0
)