让我们来看下协程的源代码,在runtime2.go文件下,名字叫g。这边为了方便观看,把很多注释都删掉了,具体的可以自己去查看。
type g struct { stack stack // offset known to runtime/cgo 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 syscallsp uintptr // if status==Gsyscall, syscallsp = sched.sp to use during gc syscallpc uintptr // if status==Gsyscall, syscallpc = sched.pc to use during gc stktopsp uintptr // expected sp at top of stack, to check in traceback param unsafe.Pointer atomicstatus uint32 stackLock uint32 // sigprof/scang lock; TODO: fold in to atomicstatus goid int64 schedlink guintptr waitsince int64 // approx time when the g become blocked waitreason waitReason // if status==Gwaiting preempt bool // preemption signal, duplicates stackguard0 = stackpreempt preemptStop bool // transition to _Gpreempted on preemption; otherwise, just deschedule preemptShrink bool // shrink stack at synchronous safe point asyncSafePoint bool paniconfault bool // panic (instead of crash) on unexpected fault address gcscandone bool // g has scanned stack; protected by _Gscan bit in status throwsplit bool // must not split stac activeStackChans bool parkingOnChan uint8 raceignore int8 // ignore race detection events sysblocktraced bool // StartTrace has emitted EvGoInSyscall about this goroutine tracking bool // whether we're tracking this G for sched latency statistics trackingSeq uint8 // used to decide whether to track this G runnableStamp int64 // timestamp of when the G last became runnable, only used when tracking runnableTime int64 // the amount of time spent runnable, cleared when running, only used when tracking sysexitticks int64 // cputicks when syscall has returned (for tracing) traceseq uint64 // trace event sequencer tracelastp puintptr // last P emitted an event for this goroutine lockedm muintptr sig uint32 writebuf []byte sigcode0 uintptr sigcode1 uintptr sigpc uintptr gopc uintptr // pc of go statement that created this goroutine ancestors *[]ancestorInfo startpc uintptr // pc of goroutine function racectx uintptr waiting *sudog cgoCtxt []uintptr // cgo traceback context labels unsafe.Pointer // profiler labels timer *timer // cached timer for time.Sleep selectDone uint32 goroutineProfiled goroutineProfileStateHolder gcAssistBytes int64 }
首先先看看stack的源代码:
type stack struct { lo uintptr hi uintptr }
这个是记录了栈空间的下限和上限。之后我们来看看sched,他是个gobuf类型,
type gobuf struct { sp uintptr pc uintptr g guintptr ctxt unsafe.Pointer ret uintptr lr uintptr bp uintptr // for framepointer-enabled architectures }
这个sp和pc学过机组的应该都不陌生 ,sp就是栈指针,他的含义就是我们的协程用到了栈的哪个地方,pc就是程序计数器,记录了我们运行到了哪个代码。之后我们再回来看下g的atomicstatus,记录了协程的状态,goid就是协程的id号。
所以,协程的底层结构可以总结如下:
1:runtime中,协程的本质是一个g结构体。
2:g中的stack记录了堆栈的地址。
3:sched记录了目前程序的运行现场。
4:atomicstatus记录了协程的状态。
5:goid记录了协程的id