面试题答案
一键面试1. 系统调用时调度循环处理流程概述
当一个Goroutine(G)发起系统调用时,Go的调度器会介入,以保证其他可运行的Goroutine能继续执行,从而维持程序的并发性能。这一过程涉及到M(操作系统线程)、G(Goroutine)和P(处理器上下文)三者状态的变化及资源重新分配。
2. M、G、P状态变化及资源重新分配
- Goroutine(G)状态变化:
- 发起系统调用的G会从
running
状态转变为syscall
状态。这表明该G当前正在执行系统调用,无法继续在用户态运行。 - 例如,假设一个G正在进行文件读取的系统调用,它会被标记为
syscall
状态。
- 发起系统调用的G会从
- 操作系统线程(M)状态变化:
- 与该G关联的M在G进入
syscall
状态后,会尝试将P释放。如果M成功释放P,它自身可能进入睡眠状态(例如在Linux系统下,通过sleep
相关系统调用),等待G完成系统调用。 - 比如,一个M原本绑定一个P并运行着G,当G发起系统调用,M会将P解绑,自身进入睡眠等待G完成系统调用返回。
- 与该G关联的M在G进入
- 处理器上下文(P)状态变化及资源重新分配:
- P被M释放后,会被放入全局的P空闲队列。调度器会从全局可运行G队列或其他M本地的可运行G队列中选取一个G,将其状态设为
running
,并与该P重新绑定。这样,这个新的G就可以在该P上运行,充分利用CPU资源。 - 举例来说,P被释放后,调度器从全局可运行G队列中取出一个计算密集型的G,将其与该P绑定,让其在P上执行计算任务。
- P被M释放后,会被放入全局的P空闲队列。调度器会从全局可运行G队列或其他M本地的可运行G队列中选取一个G,将其状态设为
3. 系统调用完成后的恢复过程
- 当发起系统调用的G完成系统调用后,它会从
syscall
状态转变回runnable
状态,并被放入全局可运行G队列。 - 如果之前释放P并进入睡眠的M被唤醒(例如通过系统中断等机制),它会尝试从全局P空闲队列中获取一个P。若获取成功,M会从全局可运行G队列中取出之前发起系统调用现在已完成的G,将其设为
running
状态,继续执行。若M未能获取到P,该G会留在全局可运行G队列,等待其他M获取P后将其取出执行。