核心机制对比
1. 用户抢占 —— need_resched
need_resched
是一个标志位,当当前进程用完时间片或者被唤醒了更高优先级任务,该标志就由scheduler_tick()
或try_to_wake_up()
设定(litux.nl, pearsonhighered.com)。- 内核在准备返回用户态之前(如用户系统调用结束或中断返回),会检查此标志;如果为真,会调用
schedule()
执行调度(litux.nl)。 - 因此用户抢占的安全点是“返回用户空间”这类明确的上下文切换时刻。
2. 内核抢占 —— preempt_count
preempt_count
是一个计数器,用于表示当前内核上下文是否允许被抢占。每次调用preempt_disable()
(或持锁等)会令其增加;调用preempt_enable()
时减少(LWN.net, linuxjournal.com, topic.alibabacloud.com)。- 当
preempt_count == 0
,表示没有阻止抢占的条件,内核允许被抢占;否则抢占被延迟(litux.nl)。 - 在中断返回或
preempt_enable()
后,如果发现need_resched
已设且preempt_count == 0
,就会触发调度(litux.nl, linuxjournal.com)。
总结对比表
机制 | 用户抢占 (need_resched ) |
内核抢占 (preempt_count ) |
---|---|---|
触发方式 | 用户态结束时,由标志触发调度 | 内核态结束临界区、锁释放时触发调度 |
标志/机制 | need_resched 标志位 |
preempt_count 计数器 |
安全点 | 返回用户态后可检查 | 离开临界区(计数归零)后可检查 |
值得注意 | 与用户态切换密切相关 | 保护内核态临界区不被打断 |
“用户抢占最终不是以内核的为准吗?”
这句话理解得不错:真正是否进行调度,要同时满足两个条件:
need_resched
为真,表示有更高优先级任务等待。preempt_count == 0
,表示当前内核上下文可以安全被抢占。
也就是说,用户抢占表面看是 need_resched
发信号,但必须等到内核确认安全(preempt_count == 0
),才会真正进入调度。因此,“最终是否抢占”确实要以内核抢占机制(preempt_count
)为准。
具体流程示例
- 当前任务在用户态执行,用完时间片 →
need_resched
被设定。 - 发生系统调用或中断返回。
- 内核上下文结束后,检查
preempt_count
是否为 0。- 若为 0,则立刻调度(用户抢占生效)。
- 若不为 0,则延迟调度,直到所有
preempt_disable()
匹配的preempt_enable()
执行完毕后才检查并执行调度。
引用参考
- 用户抢占与
need_resched
说明(litux.nl, pearsonhighered.com) - 内核抢占与
preempt_count
说明(litux.nl, LWN.net, linuxjournal.com) - 两者如何协同触发调度(litux.nl, UMass Boston CS)