15auto test_cfs_basic_functionality() ->
bool {
16 klog::Info(
"Running test_cfs_basic_functionality...");
24 task1.sched_data.cfs.vruntime = 0;
29 task2.sched_data.cfs.vruntime = 0;
34 task3.sched_data.cfs.vruntime = 0;
39 "Queue size should be 0 for empty queue");
41 "PickNext should return nullptr for empty queue");
46 "Queue size should be 1 after enqueue");
51 "Queue size should be 3 after 3 enqueues");
54 auto* picked1 = scheduler.
PickNext();
55 EXPECT_NE(picked1,
nullptr,
"Picked task should not be nullptr");
57 auto* picked2 = scheduler.
PickNext();
58 EXPECT_NE(picked2,
nullptr,
"Picked task should not be nullptr");
60 auto* picked3 = scheduler.
PickNext();
61 EXPECT_NE(picked3,
nullptr,
"Picked task should not be nullptr");
64 "PickNext should return nullptr after all tasks picked");
67 klog::Info(
"test_cfs_basic_functionality passed");
71auto test_cfs_vruntime_ordering() ->
bool {
72 klog::Info(
"Running test_cfs_vruntime_ordering...");
78 task1.sched_data.cfs.vruntime = 1000;
82 task2.sched_data.cfs.vruntime = 500;
86 task3.sched_data.cfs.vruntime = 750;
96 auto* picked1 = scheduler.
PickNext();
97 EXPECT_EQ(picked1, &task2,
"First pick should be task2 (vruntime=500)");
99 auto* picked2 = scheduler.
PickNext();
100 EXPECT_EQ(picked2, &task3,
"Second pick should be task3 (vruntime=750)");
102 auto* picked3 = scheduler.
PickNext();
103 EXPECT_EQ(picked3, &task1,
"Third pick should be task1 (vruntime=1000)");
107 klog::Info(
"test_cfs_vruntime_ordering passed");
111auto test_cfs_new_task_vruntime() ->
bool {
112 klog::Info(
"Running test_cfs_new_task_vruntime...");
118 task1.sched_data.cfs.vruntime = 1000;
122 auto* picked = scheduler.
PickNext();
123 EXPECT_EQ(picked, &task1,
"First pick should be task1");
128 task2.sched_data.cfs.vruntime = 0;
131 uint64_t before_vruntime = task2.sched_data.cfs.vruntime;
132 EXPECT_EQ(before_vruntime, 0,
"New task vruntime should be 0 initially");
137 EXPECT_NE(task2.sched_data.cfs.vruntime, 0,
138 "New task vruntime should be initialized to min_vruntime");
139 EXPECT_GE(task2.sched_data.cfs.vruntime, 1000,
140 "New task vruntime should be >= min_vruntime");
142 klog::Info(
"test_cfs_new_task_vruntime passed");
146auto test_cfs_weight_impact() ->
bool {
147 klog::Info(
"Running test_cfs_weight_impact...");
152 high_priority.sched_data.cfs.weight =
154 high_priority.sched_data.cfs.vruntime = 0;
158 low_priority.sched_data.cfs.vruntime = 0;
161 constexpr int kTickCount = 10;
163 for (
int i = 0; i < kTickCount; ++i) {
164 (void)scheduler.
OnTick(&high_priority);
165 (void)scheduler.
OnTick(&low_priority);
169 EXPECT_LT(high_priority.sched_data.cfs.vruntime,
170 low_priority.sched_data.cfs.vruntime,
171 "High priority task should have lower vruntime growth");
173 uint64_t expected = high_priority.sched_data.cfs.vruntime * 2;
174 uint64_t tolerance = expected / 10;
175 EXPECT_GE(low_priority.sched_data.cfs.vruntime, expected - tolerance,
176 "vruntime ratio lower bound (should be ~2x)");
177 EXPECT_LE(low_priority.sched_data.cfs.vruntime, expected + tolerance,
178 "vruntime ratio upper bound (should be ~2x)");
184auto test_cfs_preemption() ->
bool {
191 task1.sched_data.cfs.vruntime = 1000;
195 task2.sched_data.cfs.vruntime = 0;
200 bool should_preempt = scheduler.
OnTick(&task1);
201 EXPECT_TRUE(should_preempt,
"Task with higher vruntime should be preempted");
207auto test_cfs_no_preemption() ->
bool {
208 klog::Info(
"Running test_cfs_no_preemption...");
214 task1.sched_data.cfs.vruntime = 1000;
223 uint64_t task1_after_tick = task1.sched_data.cfs.vruntime + delta;
224 task2.sched_data.cfs.vruntime =
230 bool should_preempt = scheduler.
OnTick(&task1);
233 "Task should not be preempted when vruntime difference is small");
239auto test_cfs_dequeue() ->
bool {
246 task1.sched_data.cfs.vruntime = 100;
250 task2.sched_data.cfs.vruntime = 200;
254 task3.sched_data.cfs.vruntime = 300;
258 task4.sched_data.cfs.vruntime = 400;
270 "Queue size should be 3 after dequeue");
275 "Queue size should be 2 after dequeue");
278 auto* picked1 = scheduler.
PickNext();
279 EXPECT_EQ(picked1, &task3,
"First remaining task should be task3");
281 auto* picked2 = scheduler.
PickNext();
282 EXPECT_EQ(picked2, &task4,
"Second remaining task should be task4");
290auto test_cfs_statistics() ->
bool {
297 task1.sched_data.cfs.vruntime = 0;
301 task2.sched_data.cfs.vruntime = 0;
305 EXPECT_EQ(stats.total_enqueues, 0,
"Initial enqueues should be 0");
306 EXPECT_EQ(stats.total_dequeues, 0,
"Initial dequeues should be 0");
307 EXPECT_EQ(stats.total_picks, 0,
"Initial picks should be 0");
308 EXPECT_EQ(stats.total_preemptions, 0,
"Initial preemptions should be 0");
314 EXPECT_EQ(stats.total_enqueues, 2,
"Enqueues should be 2");
320 EXPECT_EQ(stats.total_picks, 2,
"Picks should be 2");
326 EXPECT_EQ(stats.total_dequeues, 1,
"Dequeues should be 1");
331 EXPECT_EQ(stats.total_enqueues, 0,
"Enqueues should be 0 after reset");
332 EXPECT_EQ(stats.total_dequeues, 0,
"Dequeues should be 0 after reset");
333 EXPECT_EQ(stats.total_picks, 0,
"Picks should be 0 after reset");
334 EXPECT_EQ(stats.total_preemptions, 0,
"Preemptions should be 0 after reset");
340auto test_cfs_min_vruntime_update() ->
bool {
341 klog::Info(
"Running test_cfs_min_vruntime_update...");
347 task1.sched_data.cfs.vruntime = 1000;
351 task2.sched_data.cfs.vruntime = 500;
355 task3.sched_data.cfs.vruntime = 750;
369 EXPECT_GE(min_vruntime, 500,
"min_vruntime should be updated");
371 klog::Info(
"test_cfs_min_vruntime_update passed");
375auto test_cfs_multiple_ticks() ->
bool {
376 klog::Info(
"Running test_cfs_multiple_ticks...");
382 task.sched_data.cfs.vruntime = 0;
384 uint64_t initial_vruntime = task.sched_data.cfs.vruntime;
387 constexpr int kTickCount = 10;
388 for (
int i = 0; i < kTickCount; ++i) {
389 (void)scheduler.
OnTick(&task);
393 EXPECT_GT(task.sched_data.cfs.vruntime, initial_vruntime,
394 "vruntime should accumulate over ticks");
397 uint64_t expected_delta =
399 uint64_t expected_vruntime = initial_vruntime + expected_delta * kTickCount;
400 EXPECT_EQ(task.sched_data.cfs.vruntime, expected_vruntime,
401 "vruntime should grow by expected amount");
407auto test_cfs_fairness() ->
bool {
413 constexpr size_t kTaskCount = 3;
416 for (
size_t i = 0; i < kTaskCount; ++i) {
424 constexpr int kRounds = 5;
425 for (
int round = 0; round < kRounds; ++round) {
427 for (
size_t i = 0; i < kTaskCount; ++i) {
429 EXPECT_NE(task,
nullptr,
"Should pick a task");
432 for (
int tick = 0; tick < 5; ++tick) {
433 (void)scheduler.
OnTick(task);
442 uint64_t max_vruntime = 0;
443 uint64_t min_vruntime = UINT64_MAX;
445 for (
size_t i = 0; i < kTaskCount; ++i) {
446 if (tasks[i]->
sched_data.cfs.vruntime > max_vruntime) {
449 if (tasks[i]->
sched_data.cfs.vruntime < min_vruntime) {
455 uint64_t difference = max_vruntime - min_vruntime;
457 "vruntime difference should be small (fairness)");
460 for (
size_t i = 0; i < kTaskCount; ++i) {
468auto test_cfs_mixed_operations() ->
bool {
469 klog::Info(
"Running test_cfs_mixed_operations...");
475 task1.sched_data.cfs.vruntime = 100;
479 task2.sched_data.cfs.vruntime = 200;
483 task3.sched_data.cfs.vruntime = 300;
487 task4.sched_data.cfs.vruntime = 0;
491 task5.sched_data.cfs.vruntime = 0;
498 auto* picked1 = scheduler.
PickNext();
499 EXPECT_EQ(picked1, &task1,
"First pick should be task1");
506 "Queue size should be 3 after operations");
509 auto* picked2 = scheduler.
PickNext();
510 EXPECT_NE(picked2,
nullptr,
"Second pick should not be nullptr");
512 klog::Info(
"test_cfs_mixed_operations passed");
516auto test_cfs_robustness() ->
bool {
523 task1.sched_data.cfs.vruntime = 0;
527 "PickNext on empty queue should return nullptr");
537 task1.sched_data.cfs.weight = 0;
539 EXPECT_NE(task1.sched_data.cfs.weight, 0,
540 "Weight should be set to default if 0");
555 klog::Info(
"\n=== CFS Scheduler System Tests ===\n");
557 if (!test_cfs_basic_functionality()) {
561 if (!test_cfs_vruntime_ordering()) {
565 if (!test_cfs_new_task_vruntime()) {
569 if (!test_cfs_weight_impact()) {
573 if (!test_cfs_preemption()) {
577 if (!test_cfs_no_preemption()) {
581 if (!test_cfs_dequeue()) {
585 if (!test_cfs_statistics()) {
589 if (!test_cfs_min_vruntime_update()) {
593 if (!test_cfs_multiple_ticks()) {
597 if (!test_cfs_fairness()) {
601 if (!test_cfs_mixed_operations()) {
605 if (!test_cfs_robustness()) {
609 klog::Info(
"=== All CFS Scheduler Tests Passed ===\n");
CFS (Completely Fair Scheduler) 调度器
static constexpr uint64_t kMinGranularity
vruntime 粒度 (用于计算抢占阈值)
auto Dequeue(TaskControlBlock *task) -> void override
从就绪队列中移除指定任务
auto OnTick(TaskControlBlock *current) -> bool override
每个 tick 更新任务的 vruntime
auto PickNext() -> TaskControlBlock *override
选择下一个要运行的任务
auto Enqueue(TaskControlBlock *task) -> void override
将任务加入就绪队列
auto OnPreempted(TaskControlBlock *task) -> void override
任务被抢占时调用
auto IsEmpty() const -> bool override
判断队列是否为空
static constexpr uint32_t kDefaultWeight
默认权重 (对应 nice 值为 0)
auto GetQueueSize() const -> size_t override
获取就绪队列大小
auto GetMinVruntime() const -> uint64_t
获取当前 min_vruntime
virtual auto GetStats() const -> const Stats &
获取调度器统计信息
virtual auto OnScheduled(TaskControlBlock *task) -> void
任务开始运行时调用 (从 Ready 变为 Running)
virtual auto ResetStats() -> void
重置统计信息
auto Info(etl::format_string< Args... > fmt, Args &&... args) -> void
以 INFO 级别记录日志
CpuSchedData * sched_data
调度数据 (RunQueue) 指针
union TaskControlBlock::SchedData sched_data
auto cfs_scheduler_test() -> bool
#define EXPECT_TRUE(cond, msg)
#define EXPECT_LE(val1, val2, msg)
#define EXPECT_NE(val1, val2, msg)
#define EXPECT_LT(val1, val2, msg)
#define EXPECT_FALSE(cond, msg)
#define EXPECT_GT(val1, val2, msg)
#define EXPECT_GE(val1, val2, msg)
#define EXPECT_EQ(val1, val2, msg)
uint32_t weight
任务权重 (1024 为默认)
struct TaskControlBlock::SchedData::@3 cfs
CFS 调度器数据