Linux内核强制抢占测试疑问:自愿抢占配置下的调度行为
内核抢占行为的推测验证
你的推测完全准确:当内核仅开启CONFIG_PREEMPT_VOLUNTARY而未开启CONFIG_PREEMPT时,内核态代码只有在显式调用schedule()、进入阻塞状态(比如msleep这类休眠操作),或者到达自愿抢占点(如cond_resched()调用)时,才会触发调度器切换,允许高优先级进程抢占。
你测试中的场景逻辑清晰:
- 前5秒的
mdelay属于忙等待,此时内核处于非抢占状态,高优先级RT线程无法抢占; - 调用
schedule()时,调度器才会切换到高优先级线程,但此时已经过去了低优先级线程的2秒启动延迟+5秒忙等待,总计7秒,这就是你看到高优先级线程7秒后才唤醒的核心原因; - 最初使用
msleep(10000)时,内核直接进入阻塞状态,调度器立刻切换到高优先级线程,结果符合预期。
不同调度策略的表现差异
1. SCHED_RR(实时轮转调度)
和SCHED_FIFO表现完全一致。二者同属实时调度策略,优先级均高于普通进程,在CONFIG_PREEMPT_VOLUNTARY内核中,同样只能在内核代码显式调用schedule()或阻塞时完成抢占。
2. SCHED_NORMAL/SCHED_BATCH/SCHED_IDLE(普通非实时调度)
这类非实时调度策略的进程优先级远低于RT进程,但即使如此,在内核态忙等待阶段(比如mdelay执行期间),它们同样无法抢占当前运行的内核态低优先级RT进程。只有当内核代码触发自愿抢占或阻塞时,调度器才会考虑调度优先级更高的进程——哪怕是普通进程中的高优先级实例,也必须等内核主动让出CPU。
3. SCHED_DEADLINE(截止时间调度)
作为Linux的硬实时调度策略,它的优先级高于SCHED_FIFO/SCHED_RR,但在CONFIG_PREEMPT_VOLUNTARY内核中依然受限于抢占规则:如果当前内核态进程未显式调用schedule()或进入阻塞,SCHED_DEADLINE进程也无法强制抢占,必须等待内核自愿让出CPU。只有开启CONFIG_PREEMPT(通用抢占)或CONFIG_PREEMPT_RT(完全实时抢占补丁),才能实现内核态的即时抢占。
总结
所有调度策略在CONFIG_PREEMPT_VOLUNTARY内核下的表现逻辑一致:内核态代码默认不会被抢占,只有主动触发调度或阻塞时,高优先级进程才能获得CPU时间。若要实现内核态的即时抢占,需开启CONFIG_PREEMPT或CONFIG_PREEMPT_RT补丁。
内容的提问来源于stack exchange,提问作者Dražen Grašovec

