SimpleKernel 1.17.0
Loading...
Searching...
No Matches
cfs_scheduler_test.cpp File Reference
#include "cfs_scheduler.hpp"
#include <gtest/gtest.h>
#include "kstd_vector"
#include "task_control_block.hpp"
Include dependency graph for cfs_scheduler_test.cpp:

Go to the source code of this file.

Functions

 TEST (CfsSchedulerTest, BasicEnqueueDequeue)
 
 TEST (CfsSchedulerTest, VruntimeOrdering)
 
 TEST (CfsSchedulerTest, NewTaskVruntimeInitialization)
 
 TEST (CfsSchedulerTest, WeightImpactOnVruntime)
 
 TEST (CfsSchedulerTest, OnTickPreemption)
 
 TEST (CfsSchedulerTest, OnTickNoPreemption)
 
 TEST (CfsSchedulerTest, DequeueSpecificTask)
 
 TEST (CfsSchedulerTest, NullPointerHandling)
 
 TEST (CfsSchedulerTest, DefaultWeightAssignment)
 
 TEST (CfsSchedulerTest, Statistics)
 
 TEST (CfsSchedulerTest, MinVruntimeUpdate)
 
 TEST (CfsSchedulerTest, MultipleTicksVruntimeAccumulation)
 
 TEST (CfsSchedulerTest, FairnessWithDifferentWeights)
 
 TEST (CfsSchedulerTest, ExtremeWeightValues)
 
 TEST (CfsSchedulerTest, QueueSizeConsistency)
 
 TEST (CfsSchedulerTest, PreemptionStatistics)
 

Function Documentation

◆ TEST() [1/16]

TEST ( CfsSchedulerTest  ,
BasicEnqueueDequeue   
)

Definition at line 13 of file cfs_scheduler_test.cpp.

13 {
14 CfsScheduler scheduler;
15
16 // 创建测试任务
17 TaskControlBlock task1("Task1", 1, nullptr, nullptr);
18 task1.status = TaskStatus::kReady;
19 task1.sched_data.cfs.weight = CfsScheduler::kDefaultWeight;
20 task1.sched_data.cfs.vruntime = 0;
21
22 TaskControlBlock task2("Task2", 2, nullptr, nullptr);
23 task2.status = TaskStatus::kReady;
24 task2.sched_data.cfs.weight = CfsScheduler::kDefaultWeight;
25 task2.sched_data.cfs.vruntime = 0;
26
27 // 测试空队列
28 EXPECT_TRUE(scheduler.IsEmpty());
29 EXPECT_EQ(scheduler.GetQueueSize(), 0);
30 EXPECT_EQ(scheduler.PickNext(), nullptr);
31
32 // 加入任务
33 scheduler.Enqueue(&task1);
34 EXPECT_FALSE(scheduler.IsEmpty());
35 EXPECT_EQ(scheduler.GetQueueSize(), 1);
36
37 scheduler.Enqueue(&task2);
38 EXPECT_EQ(scheduler.GetQueueSize(), 2);
39
40 // 选择任务(vruntime 相同时,按入队顺序)
41 auto* next1 = scheduler.PickNext();
42 EXPECT_NE(next1, nullptr);
43 EXPECT_EQ(scheduler.GetQueueSize(), 1);
44
45 auto* next2 = scheduler.PickNext();
46 EXPECT_NE(next2, nullptr);
47 EXPECT_EQ(scheduler.GetQueueSize(), 0);
48
49 EXPECT_EQ(scheduler.PickNext(), nullptr);
50 EXPECT_TRUE(scheduler.IsEmpty());
51}
CFS (Completely Fair Scheduler) 调度器
auto PickNext() -> TaskControlBlock *override
选择下一个要运行的任务
auto Enqueue(TaskControlBlock *task) -> void override
将任务加入就绪队列
auto IsEmpty() const -> bool override
判断队列是否为空
static constexpr uint32_t kDefaultWeight
默认权重 (对应 nice 值为 0)
auto GetQueueSize() const -> size_t override
获取就绪队列大小
constexpr etl::fsm_state_id_t kReady
Definition task_fsm.hpp:15
任务控制块,管理进程/线程的核心数据结构
#define EXPECT_TRUE(cond, msg)
#define EXPECT_NE(val1, val2, msg)
#define EXPECT_FALSE(cond, msg)
#define EXPECT_EQ(val1, val2, msg)
Here is the call graph for this function:

◆ TEST() [2/16]

TEST ( CfsSchedulerTest  ,
DefaultWeightAssignment   
)

Definition at line 236 of file cfs_scheduler_test.cpp.

236 {
237 CfsScheduler scheduler;
238
239 TaskControlBlock task("Task", 1, nullptr, nullptr);
240 task.sched_data.cfs.weight = 0; // 权重未设置
241 task.sched_data.cfs.vruntime = 0;
242
243 scheduler.Enqueue(&task);
244
245 // 权重应该被设置为默认值
246 EXPECT_EQ(task.sched_data.cfs.weight, CfsScheduler::kDefaultWeight);
247}
Here is the call graph for this function:

◆ TEST() [3/16]

TEST ( CfsSchedulerTest  ,
DequeueSpecificTask   
)

Definition at line 183 of file cfs_scheduler_test.cpp.

183 {
184 CfsScheduler scheduler;
185
186 TaskControlBlock task1("Task1", 1, nullptr, nullptr);
187 task1.sched_data.cfs.weight = CfsScheduler::kDefaultWeight;
188 task1.sched_data.cfs.vruntime = 100;
189
190 TaskControlBlock task2("Task2", 2, nullptr, nullptr);
191 task2.sched_data.cfs.weight = CfsScheduler::kDefaultWeight;
192 task2.sched_data.cfs.vruntime = 200;
193
194 TaskControlBlock task3("Task3", 3, nullptr, nullptr);
195 task3.sched_data.cfs.weight = CfsScheduler::kDefaultWeight;
196 task3.sched_data.cfs.vruntime = 300;
197
198 scheduler.Enqueue(&task1);
199 scheduler.Enqueue(&task2);
200 scheduler.Enqueue(&task3);
201
202 EXPECT_EQ(scheduler.GetQueueSize(), 3);
203
204 // 移除中间的任务
205 scheduler.Dequeue(&task2);
206 EXPECT_EQ(scheduler.GetQueueSize(), 2);
207
208 // 验证只剩下 task1 和 task3
209 EXPECT_EQ(scheduler.PickNext(), &task1);
210 EXPECT_EQ(scheduler.PickNext(), &task3);
211 EXPECT_EQ(scheduler.PickNext(), nullptr);
212}
auto Dequeue(TaskControlBlock *task) -> void override
从就绪队列中移除指定任务
Here is the call graph for this function:

◆ TEST() [4/16]

TEST ( CfsSchedulerTest  ,
ExtremeWeightValues   
)

Definition at line 385 of file cfs_scheduler_test.cpp.

385 {
386 CfsScheduler scheduler;
387
388 TaskControlBlock task("Task", 1, nullptr, nullptr);
389 task.sched_data.cfs.vruntime = 0;
390
391 // 测试极小权重 (避免除零)
392 task.sched_data.cfs.weight = 1;
393 scheduler.OnTick(&task);
394 EXPECT_GT(task.sched_data.cfs.vruntime, 0);
395
396 // 测试极大权重
397 task.sched_data.cfs.vruntime = 0;
398 task.sched_data.cfs.weight = CfsScheduler::kDefaultWeight * 1000;
399 scheduler.OnTick(&task);
400 EXPECT_GT(task.sched_data.cfs.vruntime, 0);
401 EXPECT_LT(task.sched_data.cfs.vruntime, 10); // 应该很小
402}
auto OnTick(TaskControlBlock *current) -> bool override
每个 tick 更新任务的 vruntime
#define EXPECT_LT(val1, val2, msg)
#define EXPECT_GT(val1, val2, msg)
Here is the call graph for this function:

◆ TEST() [5/16]

TEST ( CfsSchedulerTest  ,
FairnessWithDifferentWeights   
)

Definition at line 355 of file cfs_scheduler_test.cpp.

355 {
356 CfsScheduler scheduler;
357
358 TaskControlBlock high_priority("HighPriority", 1, nullptr, nullptr);
359 high_priority.sched_data.cfs.weight =
360 CfsScheduler::kDefaultWeight * 2; // 2倍权重
361 high_priority.sched_data.cfs.vruntime = 0;
362
363 TaskControlBlock low_priority("LowPriority", 2, nullptr, nullptr);
364 low_priority.sched_data.cfs.weight = CfsScheduler::kDefaultWeight; // 1倍权重
365 low_priority.sched_data.cfs.vruntime = 0;
366
367 // 模拟相同次数的 tick
368 constexpr int kTickCount = 10;
369
370 for (int i = 0; i < kTickCount; ++i) {
371 scheduler.OnTick(&high_priority);
372 scheduler.OnTick(&low_priority);
373 }
374
375 // 高优先级任务的 vruntime 增长应该是低优先级的一半
376 // high: delta = 1024 * 1000 / 2048 = 500 per tick
377 // low: delta = 1024 * 1000 / 1024 = 1000 per tick
378 EXPECT_EQ(high_priority.sched_data.cfs.vruntime, 500 * kTickCount);
379 EXPECT_EQ(low_priority.sched_data.cfs.vruntime, 1000 * kTickCount);
380 EXPECT_EQ(low_priority.sched_data.cfs.vruntime,
381 high_priority.sched_data.cfs.vruntime * 2);
382}
Here is the call graph for this function:

◆ TEST() [6/16]

TEST ( CfsSchedulerTest  ,
MinVruntimeUpdate   
)

Definition at line 293 of file cfs_scheduler_test.cpp.

293 {
294 CfsScheduler scheduler;
295
296 TaskControlBlock task1("Task1", 1, nullptr, nullptr);
297 task1.sched_data.cfs.weight = CfsScheduler::kDefaultWeight;
298 task1.sched_data.cfs.vruntime = 1000;
299
300 TaskControlBlock task2("Task2", 2, nullptr, nullptr);
301 task2.sched_data.cfs.weight = CfsScheduler::kDefaultWeight;
302 task2.sched_data.cfs.vruntime = 500;
303
304 TaskControlBlock task3("Task3", 3, nullptr, nullptr);
305 task3.sched_data.cfs.weight = CfsScheduler::kDefaultWeight;
306 task3.sched_data.cfs.vruntime = 750;
307
308 scheduler.Enqueue(&task1);
309 scheduler.Enqueue(&task2);
310 scheduler.Enqueue(&task3);
311
312 // 初始 min_vruntime 应该是 0
313 EXPECT_EQ(scheduler.GetMinVruntime(), 0);
314
315 // 选择 task2 (vruntime = 500)
316 scheduler.PickNext();
317
318 // min_vruntime 应该更新为队列中最小的 (750)
319 EXPECT_EQ(scheduler.GetMinVruntime(), 750);
320
321 // 选择 task3 (vruntime = 750)
322 scheduler.PickNext();
323
324 // min_vruntime 应该更新为 1000
325 EXPECT_EQ(scheduler.GetMinVruntime(), 1000);
326
327 // 选择 task1 (vruntime = 1000)
328 scheduler.PickNext();
329
330 // 队列为空,min_vruntime 保持为 1000
331 EXPECT_EQ(scheduler.GetMinVruntime(), 1000);
332}
auto GetMinVruntime() const -> uint64_t
获取当前 min_vruntime
Here is the call graph for this function:

◆ TEST() [7/16]

TEST ( CfsSchedulerTest  ,
MultipleTicksVruntimeAccumulation   
)

Definition at line 335 of file cfs_scheduler_test.cpp.

335 {
336 CfsScheduler scheduler;
337
338 TaskControlBlock task("Task", 1, nullptr, nullptr);
339 task.sched_data.cfs.weight = CfsScheduler::kDefaultWeight;
340 task.sched_data.cfs.vruntime = 0;
341
342 // 模拟多次 tick
343 constexpr int kTickCount = 10;
344 uint64_t expected_delta = (CfsScheduler::kDefaultWeight * 1000) /
345 task.sched_data.cfs.weight; // = 1000
346
347 for (int i = 0; i < kTickCount; ++i) {
348 scheduler.OnTick(&task);
349 }
350
351 EXPECT_EQ(task.sched_data.cfs.vruntime, expected_delta * kTickCount);
352}
Here is the call graph for this function:

◆ TEST() [8/16]

TEST ( CfsSchedulerTest  ,
NewTaskVruntimeInitialization   
)

Definition at line 84 of file cfs_scheduler_test.cpp.

84 {
85 CfsScheduler scheduler;
86
87 TaskControlBlock task1("Task1", 1, nullptr, nullptr);
88 task1.sched_data.cfs.weight = CfsScheduler::kDefaultWeight;
89 task1.sched_data.cfs.vruntime = 1000;
90
91 // 加入第一个任务
92 scheduler.Enqueue(&task1);
93 auto* picked1 = scheduler.PickNext();
94 EXPECT_EQ(picked1, &task1);
95
96 // 第二个任务 vruntime = 0 (新任务)
97 TaskControlBlock task2("Task2", 2, nullptr, nullptr);
98 task2.sched_data.cfs.weight = CfsScheduler::kDefaultWeight;
99 task2.sched_data.cfs.vruntime = 0;
100
101 scheduler.Enqueue(&task2);
102 // 新任务的 vruntime 应该被设置为 min_vruntime (1000)
103 EXPECT_EQ(task2.sched_data.cfs.vruntime, 1000);
104}
Here is the call graph for this function:

◆ TEST() [9/16]

TEST ( CfsSchedulerTest  ,
NullPointerHandling   
)

Definition at line 215 of file cfs_scheduler_test.cpp.

215 {
216 CfsScheduler scheduler;
217
218 // Enqueue 空指针应该不崩溃
219 scheduler.Enqueue(nullptr);
220 EXPECT_EQ(scheduler.PickNext(), nullptr);
221
222 // Dequeue 空指针应该不崩溃
223 scheduler.Dequeue(nullptr);
224
225 // OnTick 空指针应该不崩溃
226 EXPECT_FALSE(scheduler.OnTick(nullptr));
227
228 // OnPreempted 空指针应该不崩溃
229 scheduler.OnPreempted(nullptr);
230
231 // OnScheduled 空指针应该不崩溃
232 scheduler.OnScheduled(nullptr);
233}
auto OnPreempted(TaskControlBlock *task) -> void override
任务被抢占时调用
virtual auto OnScheduled(TaskControlBlock *task) -> void
任务开始运行时调用 (从 Ready 变为 Running)
Here is the call graph for this function:

◆ TEST() [10/16]

TEST ( CfsSchedulerTest  ,
OnTickNoPreemption   
)

Definition at line 161 of file cfs_scheduler_test.cpp.

161 {
162 CfsScheduler scheduler;
163
164 TaskControlBlock task1("Task1", 1, nullptr, nullptr);
165 task1.sched_data.cfs.weight = CfsScheduler::kDefaultWeight;
166 task1.sched_data.cfs.vruntime = 1000;
167
168 TaskControlBlock task2("Task2", 2, nullptr, nullptr);
169 task2.sched_data.cfs.weight = CfsScheduler::kDefaultWeight;
170 // OnTick 会让 task1 增加 1000,所以 task2 应该设置为 1000 + 1000 - 5 = 1995
171 // 这样 OnTick 后:task1 = 2000, task2 = 1995,差距 5 < 10
172 task2.sched_data.cfs.vruntime = 1995;
173
174 scheduler.Enqueue(&task2);
175
176 bool should_preempt = scheduler.OnTick(&task1);
177
178 // OnTick 后差距为 5,小于 kMinGranularity (10),不应该抢占
179 EXPECT_FALSE(should_preempt);
180}
Here is the call graph for this function:

◆ TEST() [11/16]

TEST ( CfsSchedulerTest  ,
OnTickPreemption   
)

Definition at line 139 of file cfs_scheduler_test.cpp.

139 {
140 CfsScheduler scheduler;
141
142 TaskControlBlock task1("Task1", 1, nullptr, nullptr);
143 task1.sched_data.cfs.weight = CfsScheduler::kDefaultWeight;
144 task1.sched_data.cfs.vruntime = 100;
145
146 TaskControlBlock task2("Task2", 2, nullptr, nullptr);
147 task2.sched_data.cfs.weight = CfsScheduler::kDefaultWeight;
148 task2.sched_data.cfs.vruntime = 0; // 更小的 vruntime
149
150 scheduler.Enqueue(&task2);
151
152 // task1 运行,vruntime 不断增长
153 task1.sched_data.cfs.vruntime = 100;
154 bool should_preempt = scheduler.OnTick(&task1);
155
156 // task2 的 vruntime (0) 比 task1 小很多,应该抢占
157 EXPECT_TRUE(should_preempt);
158}
Here is the call graph for this function:

◆ TEST() [12/16]

TEST ( CfsSchedulerTest  ,
PreemptionStatistics   
)

Definition at line 447 of file cfs_scheduler_test.cpp.

447 {
448 CfsScheduler scheduler;
449
450 TaskControlBlock task1("Task1", 1, nullptr, nullptr);
451 task1.sched_data.cfs.weight = CfsScheduler::kDefaultWeight;
452 task1.sched_data.cfs.vruntime = 1000;
453
454 TaskControlBlock task2("Task2", 2, nullptr, nullptr);
455 task2.sched_data.cfs.weight = CfsScheduler::kDefaultWeight;
456 task2.sched_data.cfs.vruntime = 0;
457
458 scheduler.Enqueue(&task2);
459
460 // 触发抢占
461 scheduler.OnTick(&task1);
462 auto stats = scheduler.GetStats();
463 EXPECT_EQ(stats.total_preemptions, 1);
464
465 // OnPreempted 也会增加抢占计数
466 scheduler.OnPreempted(&task1);
467 stats = scheduler.GetStats();
468 EXPECT_EQ(stats.total_preemptions, 2);
469}
virtual auto GetStats() const -> const Stats &
获取调度器统计信息
Here is the call graph for this function:

◆ TEST() [13/16]

TEST ( CfsSchedulerTest  ,
QueueSizeConsistency   
)

Definition at line 405 of file cfs_scheduler_test.cpp.

405 {
406 CfsScheduler scheduler;
407
408 kstd::vector<TaskControlBlock*> tasks;
409 for (int i = 0; i < 5; ++i) {
410 auto* task = new TaskControlBlock("Task", 10, nullptr, nullptr);
411 task->sched_data.cfs.weight = CfsScheduler::kDefaultWeight;
412 task->sched_data.cfs.vruntime = i * 100;
413 tasks.push_back(task);
414 }
415
416 // 加入 5 个任务
417 for (auto* task : tasks) {
418 scheduler.Enqueue(task);
419 }
420 EXPECT_EQ(scheduler.GetQueueSize(), 5);
421
422 // 移除 3 个任务
423 scheduler.PickNext();
424 scheduler.PickNext();
425 scheduler.PickNext();
426 EXPECT_EQ(scheduler.GetQueueSize(), 2);
427
428 // 加入 2 个任务
429 scheduler.Enqueue(tasks[0]);
430 scheduler.Enqueue(tasks[1]);
431 EXPECT_EQ(scheduler.GetQueueSize(), 4);
432
433 // 清空队列
434 while (!scheduler.IsEmpty()) {
435 scheduler.PickNext();
436 }
437 EXPECT_EQ(scheduler.GetQueueSize(), 0);
438 EXPECT_TRUE(scheduler.IsEmpty());
439
440 // 清理内存
441 for (auto* task : tasks) {
442 delete task;
443 }
444}
Here is the call graph for this function:

◆ TEST() [14/16]

TEST ( CfsSchedulerTest  ,
Statistics   
)

Definition at line 250 of file cfs_scheduler_test.cpp.

250 {
251 CfsScheduler scheduler;
252
253 TaskControlBlock task1("Task1", 1, nullptr, nullptr);
254 task1.sched_data.cfs.weight = CfsScheduler::kDefaultWeight;
255 task1.sched_data.cfs.vruntime = 0;
256
257 TaskControlBlock task2("Task2", 2, nullptr, nullptr);
258 task2.sched_data.cfs.weight = CfsScheduler::kDefaultWeight;
259 task2.sched_data.cfs.vruntime = 0;
260
261 // 测试 enqueue 计数
262 scheduler.Enqueue(&task1);
263 scheduler.Enqueue(&task2);
264 auto stats = scheduler.GetStats();
265 EXPECT_EQ(stats.total_enqueues, 2);
266
267 // 测试 pick 计数
268 scheduler.PickNext();
269 stats = scheduler.GetStats();
270 EXPECT_EQ(stats.total_picks, 1);
271
272 scheduler.PickNext();
273 stats = scheduler.GetStats();
274 EXPECT_EQ(stats.total_picks, 2);
275
276 // 测试 dequeue 计数
277 scheduler.Enqueue(&task1);
278 scheduler.Enqueue(&task2);
279 scheduler.Dequeue(&task1);
280 stats = scheduler.GetStats();
281 EXPECT_EQ(stats.total_dequeues, 1);
282
283 // 测试重置统计
284 scheduler.ResetStats();
285 stats = scheduler.GetStats();
286 EXPECT_EQ(stats.total_enqueues, 0);
287 EXPECT_EQ(stats.total_picks, 0);
288 EXPECT_EQ(stats.total_dequeues, 0);
289 EXPECT_EQ(stats.total_preemptions, 0);
290}
virtual auto ResetStats() -> void
重置统计信息
Here is the call graph for this function:

◆ TEST() [15/16]

TEST ( CfsSchedulerTest  ,
VruntimeOrdering   
)

Definition at line 54 of file cfs_scheduler_test.cpp.

54 {
55 CfsScheduler scheduler;
56
57 TaskControlBlock task1("Task1", 1, nullptr, nullptr);
58 task1.sched_data.cfs.weight = CfsScheduler::kDefaultWeight;
59 task1.sched_data.cfs.vruntime = 1000;
60
61 TaskControlBlock task2("Task2", 2, nullptr, nullptr);
62 task2.sched_data.cfs.weight = CfsScheduler::kDefaultWeight;
63 task2.sched_data.cfs.vruntime = 500; // 最小 vruntime
64
65 TaskControlBlock task3("Task3", 3, nullptr, nullptr);
66 task3.sched_data.cfs.weight = CfsScheduler::kDefaultWeight;
67 task3.sched_data.cfs.vruntime = 750;
68
69 // 按任意顺序加入任务
70 scheduler.Enqueue(&task1);
71 scheduler.Enqueue(&task2);
72 scheduler.Enqueue(&task3);
73
74 EXPECT_EQ(scheduler.GetQueueSize(), 3);
75
76 // 应该按 vruntime 从小到大选择
77 EXPECT_EQ(scheduler.PickNext(), &task2); // vruntime = 500
78 EXPECT_EQ(scheduler.PickNext(), &task3); // vruntime = 750
79 EXPECT_EQ(scheduler.PickNext(), &task1); // vruntime = 1000
80 EXPECT_EQ(scheduler.PickNext(), nullptr);
81}
Here is the call graph for this function:

◆ TEST() [16/16]

TEST ( CfsSchedulerTest  ,
WeightImpactOnVruntime   
)

Definition at line 107 of file cfs_scheduler_test.cpp.

107 {
108 CfsScheduler scheduler;
109
110 TaskControlBlock task1("HighPriorityTask", 1, nullptr, nullptr);
111 task1.sched_data.cfs.weight = CfsScheduler::kDefaultWeight * 2; // 高优先级
112 task1.sched_data.cfs.vruntime = 0;
113
114 TaskControlBlock task2("LowPriorityTask", 2, nullptr, nullptr);
115 task2.sched_data.cfs.weight = CfsScheduler::kDefaultWeight / 2; // 低优先级
116 task2.sched_data.cfs.vruntime = 0;
117
118 scheduler.Enqueue(&task1);
119 scheduler.Enqueue(&task2);
120
121 auto* first = scheduler.PickNext();
122 EXPECT_NE(first, nullptr);
123
124 // 模拟 OnTick 更新 vruntime
125 uint64_t initial_vruntime = first->sched_data.cfs.vruntime;
126 scheduler.OnTick(first);
127
128 // 权重大的任务 vruntime 增长慢
129 if (first == &task1) {
130 // task1 权重是 2048,delta = 1024 * 1000 / 2048 = 500
131 EXPECT_EQ(first->sched_data.cfs.vruntime, initial_vruntime + 500);
132 } else {
133 // task2 权重是 512,delta = 1024 * 1000 / 512 = 2000
134 EXPECT_EQ(first->sched_data.cfs.vruntime, initial_vruntime + 2000);
135 }
136}
Here is the call graph for this function: