1. sched_ext GA 之后,struct sched_ext_ops 的 select_cpu / enqueue / dispatch 在 idle CPU 选路与 rq lock 释放顺序如何避免 task starvation?
sched_ext 进入 GA 之后,其 struct sched_ext_ops 中的 select_cpu、enqueue、dispatch 回调在 idle CPU 选路与 rq 锁释放顺序上,是如何设计以避免任务饥饿(task starvation)的?
- sched_ext 三个核心回调各自承担的职责与调用时机
- rq lock 的持有/释放顺序如何保证公平性与可抢占性
- starvation 的避免机制(活锁、公平队列、自主调度)
sched_ext 的 select_cpu 在任务唤醒时被调用,用于为任务选择一个空闲或理想的 CPU,允许调度器在真正入队前完成负载均衡;enqueue 在任务进入某个 CPU 的调度队列时调用,把任务放入对应的调度队列(DSQ);dispatch 则在 CPU 需要运行下一个任务时从队列取任务。为避免饥饿,整套框架有意保持回调的"uninterruptible-free"语义:回调持有 rq lock 的时间必须极短,且不能调用会睡眠的阻塞操作,保证 CPU 上的调度不会因某个回调长时间占用而饿死其它任务。同时 select_cpu 返回的 CPU 只作为候选,真正的入队仍要遵循队列的公平性;enqueue/dispatch 通过 DSQ 的 FIFO 或显式优先级语义保证任务按序被选出,避免某个任务被反复推迟。配合 scx_bpf_dispatch 等 kfunc 的原子操作,整个调度决策在可抢占的锁区间内完成,从而在正确性上排除了忙等导致的活锁和饥饿。
饥饿的本质是某个任务永远得不到执行机会。sched_ext 通过三点保证:一是回调内禁止阻塞睡眠,避免锁被恶性长期持有;二是任务的调度状态由 BPF 程序显式管理,出队/入队均有明确语义;三是仍借用 CFS 的 rq 锁与调度域机制,select_cpu 只是提示而非强制,保证公平高层的兜底。这三者共同构成"不会有任务被永久饿死"的工程保证。
struct sched_ext_ops {
s32 (*select_cpu)(struct task_struct *p, s32 prev_cpu, u64 wake_flags);
void (*enqueue)(struct task_struct *p, u64 enq_flags);
void (*dispatch)(s32 cpu, struct task_struct *prev);
...
};