golang调度器
调度器
golang语言强大的并发编程能力,得益于语言在原生层面对并发的支持。接下来介绍go语言运行时调度器的设计和实现,以及运行调度相关的数据结构。
进程与线程 process/thread
多个线程可以同属于一个进程,并共享内存空间。线程之间的通信就是通过基于共享的内存进行的,与重量的进程相比,线程更轻。但是每个线程仍会占用1M以上的内存空间,进行线程间切换时,不仅需要多申请内存,同时恢复寄存器的内容是也会向操作系统额外申请资源。每个线程上下文的切换大概需要消耗1us的时间,而goroutine切换大概只需要0.2us,同时每个goroutine只占用2k内存。
调度器的设计原理
GMP模型 G-Goroutine M-线程 P-中间层
P的数据结构
|
|
抢占式调度器
基于工作窃取的多线程调度器将每一个线程都绑定在独立的CPU上,这些线程会被不同处理器管理,不同的处理器通过工作窃取对任务进行再分配实现任务的平衡,也能提升调度器和Go语言程序的整体性能。
从基于协作的抢占式调度器发展到基于信号的抢占式调度器。
- 基于协作的抢占式调度器工作原理
- 编译器会在调用函数前插入
runtime.morestack
- Go语言在运行时会在垃圾回收暂停时,系统监控发现Goroutine运行超过10ms时发出抢占请求StackPreempt。系统为 Goroutine 引入
stackguard0
字段,该字段被设置成StackPreempt
意味着当前 Goroutine 发出了抢占请求 - 当发生系统调用时,会执行编译器插入的runtime.morestack ,它调用的
runtime.newstack
会检查goroutine的stackguard0字段是否为StackPreempt。 - 如果为StackPreempt,则会触发抢占,让出线程。
- 编译器会在调用函数前插入
- 基于信号的抢占式调度器
- 是一种非协作的抢占
- 程序启动时,在
runtime.sighandler
中注册SIGURG
信号的处理函数runtime.doSigPreempt
; - 在触发垃圾回收的栈扫描时会调用
runtime.suspendG
挂起 Goroutine,该函数会执行下面的逻辑:- 将
_Grunning
状态的 Goroutine 标记成可以被抢占,即将preemptStop
设置成true
; - 调用
runtime.preemptM
触发抢占;
- 将
runtime.preemptM
会调用runtime.signalM
向线程发送信号SIGURG
;- 操作系统会中断正在运行的线程并执行预先注册的信号处理函数
runtime.doSigPreempt
; runtime.doSigPreempt
函数会处理抢占信号,获取当前的 SP 和 PC 寄存器并调用runtime.sigctxt.pushCall
;runtime.sigctxt.pushCall
会修改寄存器并在程序回到用户态时执行runtime.asyncPreempt
;- 汇编指令
runtime.asyncPreempt
会调用运行时函数runtime.asyncPreempt2
; runtime.asyncPreempt2
会调用runtime.preemptPark
;runtime.preemptPark
会修改当前 Goroutine 的状态到_Gpreempted
并调用runtime.schedule
让当前函数陷入休眠并让出线程,调度器会选择其它的 Goroutine 继续执行;
数据结构
G-Goroutine,指一个待执行的任务
M-操作系统线程
P-运行在线程上的本地调度器
G
goroutine是Go语言调度器中待执行的任务,它在运行时调度器中的地位同线程在操作系统中差不多,但是占用更少的内存,降低上下文切换的开销。goroutine是由runtime管理的,是go语言在用户态的线程。
runtime.g
表示goroutine的结构体
M
M是操作系统线程,最多只能有GOMAXPROCS个活跃线程能正常运行。
|
|
g0是持有调度栈的goroutine,curg是在当前线程上运行的用户goroutine,操作系统只关心这两个goroutine。
g0是一个运行中比较特殊的goroutine,深度参与运行时的调度过程,包括goroutine的创建,大内存分配和CGO的执行。
P
P是线程和goroutine的中间层,能提供线程所需要的上下文环境,负责调度线程上的等待队列,通过P的调度,那个内核线程都能够执行多个goroutine,能在goroutine进行I/O操作时及时让出计算资源,提高线程利用率。
Reference
https://draveness.me/golang/docs/part3-runtime/ch06-concurrency/golang-goroutine/
- 原文作者:nepp
- 原文链接:https://nepp-an.github.io/post/golang%E8%B0%83%E5%BA%A6%E5%99%A8/
- 版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议进行许可,非商业转载请注明出处(作者,原文链接),商业转载请联系作者获得授权。