book: 改进 ch06sched 中 gfget/gfput 的描述
luckyxiaoqiang opened this issue · comments
luckyxiaoqiang commented
实际描述
- 文件路径:book/zh-cn/part2runtime/ch06sched/mpg.md
- 原文段落:
(关于是否需要新分配栈的描述部分)
// 从 gfree 链表中获取 g
// 如果 P 本地 gfree 链表为空,从调度器的全局 gfree 链表中取
func gfget(_p_ *p) *g {
retry:
if _p_.gFree.empty() && (!sched.gFree.stack.empty() || !sched.gFree.noStack.empty()) {
lock(&sched.gFree.lock)
// 将一批空闲的 G 移动到 P
for _p_.gFree.n < 32 {
// 倾向于有栈的 G
gp := sched.gFree.stack.pop()
if gp == nil {
gp = sched.gFree.noStack.pop()
if gp == nil {
break
}
}
sched.gFree.n--
_p_.gFree.push(gp)
_p_.gFree.n++
}
unlock(&sched.gFree.lock)
goto retry
}
gp := _p_.gFree.pop()
if gp == nil {
return nil
}
// 拿到一个 g
_p_.gFree.n--
// 查看是否需要分配运行栈
if gp.stack.lo == 0 {
// 栈可能从全局 gfree 链表中取得,栈已被 gfput 给释放,所以需要分配一个新的栈。
// 栈分配发生在系统栈上
systemstack(func() {
gp.stack = stackalloc(_FixedStack)
})
// 计算栈边界
gp.stackguard0 = gp.stack.lo + _StackGuard
}
(...)
return gp
}
总结一下整个过程,gFree 用来表示已经执行完毕那些 g 对象,在 P 和调度器中均有保存,目的很明显是复用:
- 首先从 P 的 gFree 链表中取;
- 如果从 P 的 gFree 链表中取不到,再看从调度器的 gfree 链表取;
- 首先倾向于获取已经有执行栈的 g,因为省去了执行栈的获取
- 否则才去取没有执行栈的队列
- 如果都找不到则确实找不到可以复用的 g 了;
- 无论如何,如果找到了,则从 gfree 链表中取一个 g,这时 g 可能是从调度器的 gfree 中取出的没有执行栈的 g,因此按需创建
预期描述
// 从 gfree 链表中获取 g
// 如果 P 本地 gfree 链表为空,从调度器的全局 gfree 链表中取
func gfget(_p_ *p) *g {
retry:
if _p_.gFree.empty() && (!sched.gFree.stack.empty() || !sched.gFree.noStack.empty()) {
lock(&sched.gFree.lock)
// 将一批空闲的 G 移动到 P
for _p_.gFree.n < 32 {
// 倾向于有栈的 G
gp := sched.gFree.stack.pop()
if gp == nil {
gp = sched.gFree.noStack.pop()
if gp == nil {
break
}
}
sched.gFree.n--
_p_.gFree.push(gp)
_p_.gFree.n++
}
unlock(&sched.gFree.lock)
goto retry
}
gp := _p_.gFree.pop()
if gp == nil {
return nil
}
// 拿到一个 g
_p_.gFree.n--
// 查看是否需要分配运行栈
if gp.stack.lo == 0 {
// 栈可能是非固定大小,已被 gfput 给释放,所以需要分配一个新的栈。
// 栈分配发生在系统栈上
systemstack(func() {
gp.stack = stackalloc(_FixedStack)
})
// 计算栈边界
gp.stackguard0 = gp.stack.lo + _StackGuard
}
(...)
return gp
}
总结一下整个过程,gFree 用来表示已经执行完毕那些 g 对象,在 P 和调度器中均有保存,目的很明显是复用:
- 首先从 P 的 gFree 链表中取;
- 如果从 P 的 gFree 链表中取不到,再看从调度器的 gfree 链表取;
- 首先倾向于获取已经有执行栈的 g,因为省去了执行栈的获取
- 否则才去取没有执行栈的队列
- 如果都找不到则确实找不到可以复用的 g 了;
- 无论如何,如果找到了,则从 gfree 链表中取一个 g,取到的 g 的栈可能由于此前为非固定大小,已被 gfput 给释放,所以需要分配新的栈。