Lunarain_079's Inn

Back

1. 什么是协程(Coroutines)?#

协程是一种协作式多任务的编程模式,其主要特点包括:

  • 对称性:协程之间是平等的,没有严格的调用层级关系(不像函数调用中的 caller/callee)。
  • 主动让出控制权:协程通过显式的 yieldswitch 操作将 CPU 控制权转移给另一个协程。
  • 保留执行状态:切换时会保存上下文(如寄存器、栈指针),下次恢复时继续执行。

对比其他概念:#

概念调度方式控制权转移方式状态保存方式
线程抢占式操作系统调度操作系统自动保存
函数调用同步顺序执行call/ret栈自动保存
协程协作式yield/switch 显式切换用户手动保存上下文

2. 为什么 sched()scheduler() 是协程?#

在 xv6 的调度机制中,这两个函数通过上下文切换互相切换控制权,具有协程的本质特征。

sched()#

  • 作用:由进程主动调用(例如在 yield()sleep() 中)
  • 行为
    • 保存当前进程的上下文到 p->context
    • 通过 swtch() 切换到 cpu->context(即调度器的上下文)

scheduler()#

  • 作用:每个 CPU 核心运行一个独立的调度器线程
  • 行为
    • 选择一个就绪进程(如 p->state == RUNNABLE
    • 通过 swtch() 切换到该进程的 p->context

关键协程行为#

对称切换#

Process A → sched() → scheduler() → Process B → sched() → scheduler() → ...
text
  • sched() 切换到 scheduler()scheduler() 又切换到某个进程的 sched(),形成闭环。
  • 两者通过 swtch() 互相转移控制权,没有严格的调用关系。

上下文保留#

  • 每次切换时,swtch() 会保存和恢复寄存器状态(如 ra, sp),保证协程能正确恢复执行。

3. 协程在 xv6 中的实际意义#

协作式调度#

  • xv6 的进程切换是协作式的(非抢占式),依赖进程主动调用 sched() 让出 CPU。
  • :现代操作系统通常使用抢占式调度,通过时钟中断强制切换。

无栈协程(Stackless)#

  • swtch() 仅保存寄存器,不复制完整的栈(与用户态协程库如 Go 的 goroutine 不同),因此更轻量。

调度器的特殊角色#

  • scheduler() 本身是一个“无限循环”的协程
  • 永远以 cpu->context 为切换目标,是进程切换的中枢

4. 对比其他系统中的协程#

场景xv6 的 sched/scheduler用户态协程(如 Go)
切换方式通过 swtch() 保存/恢复寄存器通常复制或切换整个栈
调度触发进程主动调用 sched()可能由运行时系统或异步事件触发
是否抢占协作式(非抢占)可协作式或抢占式(如 Go 的抢占)
上下文存储位置p->contextcpu->context堆分配的结构体

5. 总结(xv6 部分)#

  • 核心观点sched()scheduler() 通过 swtch() 互相切换,符合协程“对称协作”的定义。
  • 设计意义:这种模式简化了 xv6 的调度器实现,避免了复杂的抢占逻辑(但依赖进程自觉让出 CPU)。
  • 扩展思考:现代操作系统(如 Linux)的调度更复杂,但某些底层机制(如上下文切换)仍与协程概念相通。
xv6 的协程式调度模型
https://www.lunarain.top/blog/%E8%B0%83%E5%BA%A6%E6%A8%A1%E5%9E%8B
Author Lunarain_079
Published at April 8, 2025
Comment seems to stuck. Try to refresh?✨