关于OS调度器与Go调度器上下文切换的两类技术问询
关于线程调度与Go协程调度的疑问解答
问题1:OS调度器能否在映射到内核级线程的用户级线程之间执行上下文切换?
这个问题要结合用户级线程(ULT)和内核级线程(KLT)的映射模型来看:
- 一对一模型:每个用户级线程对应一个独立的内核级线程。此时OS调度器只能感知到内核级线程,它的上下文切换操作针对的是内核级线程,本质上是间接完成了用户级线程的切换,但OS并不会直接识别或操作用户级线程本身。
- 多对一模型:多个用户级线程绑定到同一个内核级线程。OS完全看不到用户级线程,只能调度这个唯一的内核级线程;用户级线程之间的上下文切换完全由用户态的线程调度器负责,OS无法介入。
- 多对多模型:多个用户级线程映射到一组内核级线程。OS依然只负责内核级线程的调度,用户级线程到内核级线程的映射、以及用户级线程之间的切换,由用户态调度器控制,OS不会直接在用户级线程之间执行切换。
简单来说:OS调度器无法直接在用户级线程之间执行上下文切换,它的调度对象始终是内核级线程;用户级线程的切换要么由用户态调度器完成,要么通过OS切换内核级线程间接实现。
问题2:当goroutine发起系统阻塞调用时,谁会率先执行上下文切换:OS还是Go调度器?
Go采用的是M:N调度模型(M=内核线程,G=goroutine,P=逻辑处理器),当goroutine A发起系统阻塞调用时,Go调度器会率先执行上下文切换操作:
- 当Go调度器检测到goroutine A即将进入系统调用阻塞时,会立即将绑定的逻辑处理器P与当前内核线程M解绑;
- 随后把这个空闲的P分配给其他处于空闲状态的内核线程M',让M'可以调度其他等待执行的goroutine;
- 当系统调用真正导致内核线程M阻塞后,OS调度器才会介入,将M从CPU上切换出去,调度其他就绪的内核线程运行。
也就是说,Go调度器会先完成goroutine调度层面的上下文切换(P的转移),避免CPU资源浪费,之后OS才会因为内核线程阻塞执行内核层面的切换。
内容的提问来源于stack exchange,提问作者Tarun
相关产品推荐
相关产品推荐

