让我们来看下协程的源代码,在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
本文深入探讨了Go语言中的协程(Goroutine)实现,详细介绍了`g`结构体,包括栈空间记录、调度信息`sched`、协程状态`atomicstatus`以及协程ID`goid`等关键元素。通过这些内容,我们可以理解Go语言如何高效地管理和调度协程。
1265

被折叠的 条评论
为什么被折叠?



