😥 整理不易,此资源只针对正式星主开放,
还请入驻星球后再来观看。

GOLANG ROADMAP

阅读模式

  • 沉浸
  • 自动
  • 日常
首页
Go学习
  • Go学院

    • Go小课
    • Go小考
    • Go实战
    • 精品课
  • Go宝典

    • 在线宝典
    • B站精选
    • 推荐图书
    • 精品博文
  • Go开源

    • Go仓库
    • Go月刊
  • Go下载

    • 视频资源
    • 文档资源
Go求职
  • 求职服务

    • 内推互助
    • 求职助力
  • 求职刷题

    • 企业题库
    • 面试宝典
    • 求职面经
Go友会
  • 城市
  • 校园
推广返利 🤑
实验区
  • Go周边
消息
更多
  • 用户中心

    • 我的信息
    • 推广返利
  • 玩转星球

    • 星球介绍
    • 角色体系
    • 星主权益
  • 支持与服务

    • 联系星主
    • 成长记录
    • 常见问题
    • 吐槽专区
  • 合作交流

    • 渠道合作
    • 课程入驻
    • 友情链接
author-avatar

GOLANG ROADMAP


首页
Go学习
  • Go学院

    • Go小课
    • Go小考
    • Go实战
    • 精品课
  • Go宝典

    • 在线宝典
    • B站精选
    • 推荐图书
    • 精品博文
  • Go开源

    • Go仓库
    • Go月刊
  • Go下载

    • 视频资源
    • 文档资源
Go求职
  • 求职服务

    • 内推互助
    • 求职助力
  • 求职刷题

    • 企业题库
    • 面试宝典
    • 求职面经
Go友会
  • 城市
  • 校园
推广返利 🤑
实验区
  • Go周边
消息
更多
  • 用户中心

    • 我的信息
    • 推广返利
  • 玩转星球

    • 星球介绍
    • 角色体系
    • 星主权益
  • 支持与服务

    • 联系星主
    • 成长记录
    • 常见问题
    • 吐槽专区
  • 合作交流

    • 渠道合作
    • 课程入驻
    • 友情链接
  • Go真实面试题汇总系列

    • Go基础篇
  • 宝典内容

    • 20. 2个协程交替打印字母和数字
    • 21. goroutine与线程的区别?
    • 27. 为什么不要大量使用goroutine
    • 38. 协程goroutine
    • 41. go 中用 for 遍历多次执行 goroutine会存在什么问题
    • 49. 问等待所有goroutine结束,怎么做?
    • 56. goroutine 为什么轻量
    • 86. 用Channel和两个协程实现数组相加
    • 98. go协程的实现方式
    • 106. 并行goroutine如何实现
    • 109. goroutine和线程的区别,为什么说goroutine轻量
    • 117. 父 goroutine 退出,如何使得子 goroutine 也退出?
    • 125. 主协程如何等待其余协程完再操作
    • 141. 为什么不要频繁创建和停止goroutine
    • 146. 如何拿到多个goroutine的返回值,如何区别他们
    • 151.golang goroutine的工作原理以及他们怎么进行数据交互的
    • 157. 一个线程打印奇数一个线程打印偶数 交替打印
    • 160. channel主要做什么事情
    • 167. golang 协程机制
    • 168. 协程的栈空间大小有限制吗?会主动扩展吗?
    • 169. 用go实现一个协程池,大概用什么实现
    • 170. go里面为什么需要多协程?
    • 171. goroutine为什么会存在,为什么不使用线程?
    • 173. go协程线程进程区别
    • 176. go协程相比其它协程库区别在哪?
    • 184. 编程go协程交叉顺序打印数组
    • 185. go协程通信
    • 203. 一个goroutine sleep了,操作系统是怎么唤醒的
    • 222. Go的协程可以不可以自己让出cpu
    • 223. Go的协程可以只挂在一个线程上面吗
    • 239. 用go撸一个生产者消费型,用channel通信,怎么友好的关闭chan?
    • 240. goroutine调度源码
    • 248. go实现协程池
    • 249. 两个协程交替打印1到20
    • 251. goroutine 和 kernel thread 之间是什么关系?
    • 254. 用过go,那么进程,协程,线程各自的优缺点
    • 264. Go的多线程
    • 268. 进程和协程
    • 269. 如何解决孤儿进程的出现
    • 274. goroutine为什么比线程开销小,实现原理
    • 277. 协程实现顺序打印123
    • 286. goroutine的调度是出现在什么情况下,调度时做了什么
    • 297. golang有什么提高性能的设计, 重点说说goroutine
    • 298. 进程、线程和协程和通信方式
    • 300. Goroutine 数量是越多越好吗?
    • 308. go协程的简单用法
    • 314. 为什么用户级别的线程 goroutine 比操作系统线程更轻量级?
    • 341. 协程怎么停顿?
    • 367. go 如何关闭goroutine
    • 373. Go 语言协程怎么跑的
    • 375. Go创建协程的过程
    • 376. 协程共享哪些资源?
    • 385. go中协程是如何实现的
    • 386. 协程中参数直接使用,和传参的区别是什么,为什么会造成这种结果
    • 389. 是否写过go语言多协程内容
    • 394. 开俩个协程,一个协程生产数据,另一个协程对数据进行处理,处理完后再把数据发回去,使用管道如何实现?
    • 406. goroutine泄露
    • 407. 如何停止一个goroutine
    • 410. 查看goroutine (579)
    • 420. 用go协程的时候也是要走IO的,go是如何处理的?
    • 444. 有没有了解过goroutine的底层数据结构, 为什么协程比线程轻量且快
    • 461. 如何限制 goroutine 并发数量 (channel 或 WaitGroup)
    • 465. Go里面一个协程能保证绑定在一个内核线程上面的。

😥 整理不易,此资源只针对正式星主开放,
还请入驻星球后再来观看。

240. goroutine调度源码


企业题库解析小组

题目序号:(2807) 题目来源: 陌陌 频次: 1

# 答案: 小强

G、P、M 是 Go 调度器的三个核心组件,各司其职。在它们精密地配合下,Go 调度器得以高效运转,这也是Go天然支持高并发的内在动力。今天这篇文章我们来深入理解 GPM 模型。

先看 G,取 goroutine 的首字母,主要保存 goroutine 的一些状态信息以及 CPU 的一些寄存器的值,例如 IP 寄存器,以便在轮到本 goroutine 执行时,CPU 知道要从哪一条指令处开始执行。 runtime.g 的源码如下:

type g struct {

	// goroutine 使用的栈
	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

	// 当前与 g 绑定的 m
	m *m
	// current m; offset known to arm liblink
	// goroutine 的运行现场
	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

	// wakeup 时传入的参数
	param unsafe.Pointer

	// passed parameter on wakeup

	atomicstatus uint32

	stackLock uint32
	// sigprof/scang lock;
	goid int64

	// g 被阻塞之后的近似时间
	waitsince int64
	// approx time when the g become blocked

	// g 被阻塞的原因
	waitreason string

	// 指向全局队列里下一个 g
	schedlink guintptr

	// 抢占调度标志。这个为 true 时,stackguard0 等于 stackpreempt
	preempt bool

	// preemption signal, duplicates stackguard0 = stackpreempt
	paniconfault bool

	// panic (instead of crash) on unexpected fault address
	preemptscan bool

	// preempted g does scan for gc
	gcscandone bool

	// g has scanned stack; protected by _Gscan bit in status
	gcscanvalid bool
	// false at start of gc cycle, true if G has not run since last scan;
	throwsplit bool

	// must not split stack
	raceignore int8
	// ignore race detection events
	sysblocktraced bool

	// StartTrace has emitted EvGoInSyscall about this goroutine

	// syscall 返回之后的 cputicks,用来做 tracing

	sysexitticks int64
	// cputicks when syscall has returned (for tracing)

	traceseq uint64
	// trace event sequencer
	tracelastp puintptr
	// last P emitted an event for this goroutine
	// 如果调用了 LockOsThread,那么这个 g 会绑定到某个 m 上
	lockedm *m
	sig uint32
	writebuf []byte

	sigcode0 uintptr

	sigcode1 uintptr

	sigpc uintptr
	// 创建该 goroutine 的语句的指令地址

	gopc uintptr
	// pc of go statement that created this goroutine
	// goroutine 函数的指令地址

	startpc uintptr
	// pc of goroutine function

	racectx uintptr

	waiting *sudog
	// sudog structures this g is waiting on (that have a valid elem ptr); in lock order

	cgoCtxt []uintptr
	// cgo traceback context

	labels unsafe.Pointer
	// profiler labels

	// time.Sleep 缓存的定时器
	timer *timer
	// cached timer for time.Sleep

	gcAssistBytes int64
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120

再来看 M,取 machine 的首字母,它代表一个工作线程,或者说系统线程。G 需要调度到 M 上才能运行,M 是真正工作的人。结构体 m 就是我们常说的 M,它保存了 M 自身使用的栈信息、当前正在 M 上执行的 G 信息、与之绑定的 P 信息……

当 M 没有工作可做的时候,在它休眠前,会“自旋”地来找工作:检查全局队列,查看 network poller,试图执行 gc 任务,或者“偷”工作。

结构体 m 的源码如下:

type m struct {
	// 记录工作线程(也就是内核线程)使用的栈信息。在执行调度代码时需要使用
	// 执行用户 goroutine 代码时,使用用户 goroutine 自己的栈,因此调度时会发生栈的切换
	g0 *g
	// goroutine with scheduling stack/
	morebuf gobuf
	// gobuf arg to morestack

	divmod uint32
	// div/mod denominator for arm - known to liblink
	// Fields not known to debuggers.

	procid uint64
	// for debuggers, but offset not hard-coded

	gsignal *g
	// signal-handling g

	sigmask sigset
	// storage for saved signal mask
	// 通过 tls 结构体实现 m 与工作线程的绑定
	// 这里是线程本地存储
	tls [6]uintptr
	// thread-local storage (for x86 extern register)
	mstartfn func()

	// 指向正在运行的 gorutine 对象
	curg *g
	// current running goroutine
	caughtsig guintptr
	// goroutine running during fatal signal

	// 当前工作线程绑定的 p
	p puintptr
	// attached p for executing go code (nil if not executing go code)
	nextp puintptr

	id int32

	mallocing int32

	throwing int32

	// 该字段不等于空字符串的话,要保持 curg 始终在这个 m 上运行
	preemptoff string
	// if != "", keep curg running on this m
	locks int32

	softfloat int32

	dying int32

	profilehz int32

	helpgc int32

	// 为 true 时表示当前 m 处于自旋状态,正在从其他线程偷工作
	spinning bool

	// m is out of work and is actively looking for work
	// m 正阻塞在 note 上
	blocked bool

	// m is blocked on a note
	// m 正在执行 write barrier
	inwb bool

	// m is executing a write barrier
	newSigstack bool

	// minit on C thread called sigaltstack
	printlock int8

	// 正在执行 cgo 调用
	incgo bool

	// m is executing a cgo call
	fastrand uint32

	// cgo 调用总计数
	ncgocall uint64
	// number of cgo calls in total

	ncgo int32
	// number of cgo calls currently in progress

	cgoCallersUse uint32
	// if non-zero, cgoCallers in use temporarily

	cgoCallers *cgoCallers
	// cgo traceback if crashing in cgo call

	// 没有 goroutine 需要运行时,工作线程睡眠在这个 park 成员上,

	// 其它线程通过这个 park 唤醒该工作线程
	park note

	// 记录所有工作线程的链表
	alllink *m
	// on allm

	schedlink muintptr

	mcache *mcache

	lockedg *g

	createstack [32]uintptr
	// stack that created this thread.

	freglo [16]uint32
	// d[i] lsb and f[i]

	freghi [16]uint32
	// d[i] msb and f[i+16]

	fflag uint32
	// floating point compare flags

	locked uint32
	// tracking for lockosthread

	// 正在等待锁的下一个 m

	nextwaitm uintptr
	// next m waiting for lock

	needextram bool

	traceback uint8

	waitunlockf unsafe.Pointer
	// todo go func(*g, unsafe.pointer) bool

	waitlock unsafe.Pointer

	waittraceev byte

	waittraceskip int

	startingtrace bool

	syscalltick uint32

	// 工作线程 id
	thread uintptr
	// thread handle

	// these are here because they are too large to be on the stack

	// of low-level NOSPLIT functions.

	libcall libcall

	libcallpc uintptr
	// for cpu profiler

	libcallsp uintptr

	libcallg guintptr

	syscall libcall
	// stores syscall parameters on windows

	mOS
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166

再来看 P,取 processor 的首字母,为 M 的执行提供“上下文”,保存 M 执行 G 时的一些资源,例如本地可运行 G 队列,memeory cache 等。

一个 M 只有绑定 P 才能执行 goroutine,当 M 被阻塞时,整个 P 会被传递给其他 M ,或者说整个 P 被接管。

// p 保存 go 运行时所必须的资源

type p struct {
	lock mutex
	// 在 allp 中的索引
	id int32

	status uint32
	// one of pidle/prunning/...

	link puintptr

	// 每次调用 schedule 时会加一
	schedtick uint32

	// 每次系统调用时加一
	syscalltick uint32

	// 用于 sysmon 线程记录被监控 p 的系统调用时间和运行时间
	sysmontick sysmontick
	// last tick observed by sysmon

	// 指向绑定的 m,如果 p 是 idle 的话,那这个指针是 nil
	m muintptr
	// back-link to associated m (nil if idle)
	mcache  *mcache
	racectx uintptr

	deferpool [5][]*_defer
	// pool of available defer structs of different sizes (see panic.go)
	deferpoolbuf [5][32]*_defer

	// Cache of goroutine ids, amortizes accesses to runtime·sched.goidgen.
	goidcache    uint64
	goidcacheend uint64

	// Queue of runnable goroutines. Accessed without lock.
	// 本地可运行的队列,不用通过锁即可访问
	runqhead uint32
	// 队列头
	runqtail uint32
	// 队列尾
	// 使用数组实现的循环队列
	runq [256]guintptr

	// runnext 非空时,代表的是一个 runnable 状态的 G,
	// 这个 G 被 当前 G 修改为 ready 状态,相比 runq 中的 G 有更高的优先级。
	// 如果当前 G 还有剩余的可用时间,那么就应该运行这个 G
	// 运行之后,该 G 会继承当前 G 的剩余时间
	runnext guintptr

	// Available G's (status == Gdead)
	// 空闲的 g
	gfree    *g
	gfreecnt int32

	sudogcache []*sudog
	sudogbuf   [128]*sudog

	tracebuf                   traceBufPtr
	traceSwept, traceReclaimed uintptr
	palloc                     persistentAlloc
	// per-P to avoid mutex
	// Per-P GC state
	gcAssistTime int64
	// Nanoseconds in assistAlloc

	gcBgMarkWorker guintptr

	gcMarkWorkerMode gcMarkWorkerMode

	runSafePointFn uint32
	// if 1, run sched.safePointFn at next safe point
	pad [sys.CacheLineSize]byte
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
  • 答案: 小强