SimpleKernel 1.17.0
Loading...
Searching...
No Matches
exit_system_test.cpp
Go to the documentation of this file.
1
5#include <cpu_io.h>
6
7#include <atomic>
8#include <cstddef>
9#include <cstdint>
10
11#include "arch.h"
12#include "basic_info.hpp"
13#include "kernel.h"
14#include "kstd_cstdio"
15#include "kstd_cstring"
16#include "kstd_libcxx.h"
17#include "kstd_memory"
18#include "sk_stdlib.h"
19#include "syscall.hpp"
20#include "system_test.h"
22#include "task_manager.hpp"
23#include "task_messages.hpp"
24
25namespace {
26
27std::atomic<int> g_exit_test_counter{0};
28std::atomic<int> g_tests_completed{0};
29std::atomic<int> g_tests_failed{0};
30
31// ---------------------------------------------------------------------------
32// test_exit_normal
33// 测试: 创建带工作函数的任务,让其运行完毕后,验证 TCB 状态字段的语义正确性。
34// 具体检查: 初始状态不是 kExited/kZombie,退出码字段可正确写入并读出。
35// ---------------------------------------------------------------------------
36
37void normal_work(void* arg) {
38 auto* flag = reinterpret_cast<std::atomic<int>*>(arg);
39 klog::Debug("normal_work: running");
40 (void)sys_sleep(30);
41 *flag = 1;
42 klog::Debug("normal_work: done, calling sys_exit(0)");
43 sys_exit(0);
44}
45
46void test_exit_normal(void* /*arg*/) {
47 klog::Info("=== Exit Normal Test ===");
48
49 bool passed = true;
50
51 // 1. 创建 TCB 并检查初始状态不是终止态
52 auto* task = new TaskControlBlock("ExitNormal", 10, nullptr, nullptr);
53 task->pid = 5000;
54 task->aux->tgid = 5000;
55 task->aux->parent_pid = 1;
56
57 if (task->GetStatus() == TaskStatus::kExited ||
58 task->GetStatus() == TaskStatus::kZombie) {
59 klog::Err("test_exit_normal: FAIL — fresh TCB already in terminal state");
60 passed = false;
61 }
62
63 // 2. exit_code 默认应为 0
64 if (task->aux->exit_code != 0) {
65 klog::Err("test_exit_normal: FAIL — default exit_code != 0 (got {})",
66 task->aux->exit_code);
67 passed = false;
68 }
69
70 // 3. 创建有实际工作函数的任务,等待其完成后通过 flag 验证执行路径
71 std::atomic<int> work_flag{0};
72 auto worker = kstd::make_unique<TaskControlBlock>(
73 "ExitNormalWorker", 10, normal_work, reinterpret_cast<void*>(&work_flag));
74 TaskManagerSingleton::instance().AddTask(std::move(worker));
75
76 // 等待 worker 运行完毕(最多 500ms)
77 int timeout = 10;
78 while (timeout > 0 && work_flag.load() == 0) {
79 (void)sys_sleep(50);
80 timeout--;
81 }
82
83 if (work_flag.load() != 1) {
84 klog::Err("test_exit_normal: FAIL — worker did not complete");
85 passed = false;
86 } else {
87 klog::Info("test_exit_normal: worker completed successfully");
88 }
89
90 // 4. 向 task 写入退出信息并验证读回一致
91 task->aux->exit_code = 0;
92 task->fsm.Receive(MsgSchedule{}); // kUnInit -> kReady
93 task->fsm.Receive(MsgSchedule{}); // kReady -> kRunning
94 task->fsm.Receive(MsgExit{0, true}); // kRunning -> kZombie
95 if (task->aux->exit_code != 0 || task->GetStatus() != TaskStatus::kZombie) {
96 klog::Err("test_exit_normal: FAIL — TCB field write-back mismatch");
97 passed = false;
98 }
99
100 if (passed) {
101 klog::Info("Exit Normal Test: PASSED");
102 } else {
103 klog::Err("Exit Normal Test: FAILED");
104 g_tests_failed++;
105 }
106
107 delete task;
108 g_tests_completed++;
109 sys_exit(passed ? 0 : 1);
110}
111
112// ---------------------------------------------------------------------------
113// test_exit_with_error
114// 测试: 验证非零退出码可被正确存储和读回;字段语义与正常退出对称。
115// ---------------------------------------------------------------------------
116
117void error_work(void* arg) {
118 auto* flag = reinterpret_cast<std::atomic<int>*>(arg);
119 klog::Debug("error_work: running");
120 (void)sys_sleep(30);
121 *flag = 42;
122 klog::Debug("error_work: done, calling sys_exit(42)");
123 sys_exit(42);
124}
125
126void test_exit_with_error(void* /*arg*/) {
127 klog::Info("=== Exit With Error Test ===");
128
129 bool passed = true;
130
131 // 1. 创建 TCB,确认 exit_code 默认为 0
132 auto* task = new TaskControlBlock("ExitError", 10, nullptr, nullptr);
133 task->pid = 5001;
134 task->aux->tgid = 5001;
135 task->aux->parent_pid = 1;
136
137 if (task->aux->exit_code != 0) {
138 klog::Err("test_exit_with_error: FAIL — default exit_code != 0 (got {})",
139 task->aux->exit_code);
140 passed = false;
141 }
142
143 // 2. 创建带实际工作的任务,以错误码退出
144 std::atomic<int> work_flag{0};
145 auto worker = kstd::make_unique<TaskControlBlock>(
146 "ExitErrorWorker", 10, error_work, reinterpret_cast<void*>(&work_flag));
147 TaskManagerSingleton::instance().AddTask(std::move(worker));
148
149 int timeout = 10;
150 while (timeout > 0 && work_flag.load() == 0) {
151 (void)sys_sleep(50);
152 timeout--;
153 }
154
155 if (work_flag.load() != 42) {
156 klog::Err("test_exit_with_error: FAIL — worker did not set error flag");
157 passed = false;
158 } else {
159 klog::Info("test_exit_with_error: worker set flag to {}", work_flag.load());
160 }
161
162 // 3. 验证 TCB 中的退出码字段可以正确存储非零值
163 task->aux->exit_code = 42;
164 task->fsm.Receive(MsgSchedule{}); // kUnInit -> kReady
165 task->fsm.Receive(MsgSchedule{}); // kReady -> kRunning
166 task->fsm.Receive(MsgExit{42, true}); // kRunning -> kZombie
167 if (task->aux->exit_code != 42) {
168 klog::Err(
169 "test_exit_with_error: FAIL — exit_code write-back mismatch "
170 "(expected 42, got {})",
171 task->aux->exit_code);
172 passed = false;
173 }
174 if (task->GetStatus() != TaskStatus::kZombie) {
175 klog::Err("test_exit_with_error: FAIL — status write-back mismatch");
176 passed = false;
177 }
178
179 if (passed) {
180 klog::Info("Exit With Error Test: PASSED");
181 } else {
182 klog::Err("Exit With Error Test: FAILED");
183 g_tests_failed++;
184 }
185
186 delete task;
187 g_tests_completed++;
188 sys_exit(passed ? 0 : 1);
189}
190
191// ---------------------------------------------------------------------------
192// test_thread_exit (ALREADY GOOD — kept with counter increment added)
193// ---------------------------------------------------------------------------
194
195void child_thread_exit_work(void* arg) {
196 uint64_t thread_id = reinterpret_cast<uint64_t>(arg);
197
198 klog::Info("Thread {}: starting", thread_id);
199
200 for (int i = 0; i < 3; ++i) {
201 g_exit_test_counter++;
202 klog::Debug("Thread {}: working, iter={}", thread_id, i);
203 (void)sys_sleep(30);
204 }
205
206 klog::Info("Thread {}: exiting", thread_id);
207 sys_exit(static_cast<int>(thread_id));
208}
209
210void test_thread_exit(void* /*arg*/) {
211 klog::Info("=== Thread Exit Test ===");
212
213 g_exit_test_counter = 0;
214
215 // 创建线程组主线程
216 auto leader_uptr =
217 kstd::make_unique<TaskControlBlock>("ThreadLeader", 10, nullptr, nullptr);
218 leader_uptr->pid = 5100;
219 leader_uptr->aux->tgid = 5100;
220 leader_uptr->aux->parent_pid = 1;
221 auto* leader = leader_uptr.get();
222
223 TaskManagerSingleton::instance().AddTask(std::move(leader_uptr));
224
225 // 创建子线程
226 auto thread1 = kstd::make_unique<TaskControlBlock>(
227 "Thread1", 10, child_thread_exit_work, reinterpret_cast<void*>(1));
228 thread1->pid = 5101;
229 thread1->aux->tgid = 5100;
230 thread1->JoinThreadGroup(leader);
231
232 TaskManagerSingleton::instance().AddTask(std::move(thread1));
233
234 auto thread2 = kstd::make_unique<TaskControlBlock>(
235 "Thread2", 10, child_thread_exit_work, reinterpret_cast<void*>(2));
236 thread2->pid = 5102;
237 thread2->aux->tgid = 5100;
238 thread2->JoinThreadGroup(leader);
239
240 TaskManagerSingleton::instance().AddTask(std::move(thread2));
241
242 klog::Info("Created thread group with leader (pid={}) and 2 threads",
243 leader->pid);
244
245 // 等待线程运行并退出
246 (void)sys_sleep(200);
247
248 klog::Info("Exit test counter: {} (expected >= 6)",
249 g_exit_test_counter.load());
250
251 bool passed = (g_exit_test_counter.load() >= 6);
252
253 if (passed) {
254 klog::Info("Thread Exit Test: PASSED");
255 } else {
256 klog::Err("Thread Exit Test: FAILED (counter={}, expected >= 6)",
257 g_exit_test_counter.load());
258 g_tests_failed++;
259 }
260
261 g_tests_completed++;
262 sys_exit(passed ? 0 : 1);
263}
264
265// ---------------------------------------------------------------------------
266// test_orphan_exit
267// 测试: 孤儿进程 (parent_pid == 0) 的 TCB 字段语义:
268// - parent_pid 正确存储 0
269// - 无父进程时退出应进入 kExited 而非 kZombie
270// ---------------------------------------------------------------------------
271
272void orphan_work(void* arg) {
273 auto* flag = reinterpret_cast<std::atomic<int>*>(arg);
274 klog::Debug("orphan_work: running");
275 (void)sys_sleep(30);
276 *flag = 1;
277 klog::Debug("orphan_work: done");
278 sys_exit(0);
279}
280
281void test_orphan_exit(void* /*arg*/) {
282 klog::Info("=== Orphan Exit Test ===");
283
284 bool passed = true;
285
286 // 1. 创建孤儿 TCB,验证 parent_pid == 0 被正确存储
287 auto* orphan = new TaskControlBlock("Orphan", 10, nullptr, nullptr);
288 orphan->pid = 5200;
289 orphan->aux->tgid = 5200;
290 orphan->aux->parent_pid = 0; // 孤儿进程
291
292 if (orphan->aux->parent_pid != 0) {
293 klog::Err("test_orphan_exit: FAIL — parent_pid not stored as 0 (got {})",
294 orphan->aux->parent_pid);
295 passed = false;
296 }
297
298 // 2. 孤儿进程退出时预期进入 kExited 而非 kZombie(无父进程等待回收)
299 orphan->aux->exit_code = 0;
300 orphan->fsm.Receive(MsgSchedule{}); // kUnInit -> kReady
301 orphan->fsm.Receive(MsgSchedule{}); // kReady -> kRunning
302 orphan->fsm.Receive(MsgExit{0, false}); // kRunning -> kExited (no parent)
303 if (orphan->GetStatus() != TaskStatus::kExited) {
304 klog::Err(
305 "test_orphan_exit: FAIL — orphan status should be kExited "
306 "(got {})",
307 static_cast<int>(orphan->GetStatus()));
308 passed = false;
309 }
310 if (orphan->aux->parent_pid != 0) {
311 klog::Err("test_orphan_exit: FAIL — parent_pid changed unexpectedly");
312 passed = false;
313 }
314
315 // 3. 用实际工作函数验证孤儿任务能正常执行和退出
316 std::atomic<int> work_flag{0};
317 auto orphan_worker = kstd::make_unique<TaskControlBlock>(
318 "OrphanWorker", 10, orphan_work, reinterpret_cast<void*>(&work_flag));
319 orphan_worker->aux->parent_pid = 0; // 无父进程
320 TaskManagerSingleton::instance().AddTask(std::move(orphan_worker));
321
322 int timeout = 10;
323 while (timeout > 0 && work_flag.load() == 0) {
324 (void)sys_sleep(50);
325 timeout--;
326 }
327
328 if (work_flag.load() != 1) {
329 klog::Err("test_orphan_exit: FAIL — orphan worker did not complete");
330 passed = false;
331 } else {
332 klog::Info("test_orphan_exit: orphan worker completed");
333 }
334
335 if (passed) {
336 klog::Info("Orphan Exit Test: PASSED");
337 } else {
338 klog::Err("Orphan Exit Test: FAILED");
339 g_tests_failed++;
340 }
341
342 delete orphan;
343 g_tests_completed++;
344 sys_exit(passed ? 0 : 1);
345}
346
347// ---------------------------------------------------------------------------
348// test_zombie_process
349// 测试: 子进程退出后变为僵尸状态——TCB 语义契约:
350// - parent_pid 指向父进程
351// - 退出后应是 kZombie(等父进程回收)而非 kExited
352// ---------------------------------------------------------------------------
353
354void child_work(void* arg) {
355 auto* flag = reinterpret_cast<std::atomic<int>*>(arg);
356 klog::Debug("child_work: running");
357 (void)sys_sleep(30);
358 *flag = 1;
359 klog::Debug("child_work: done");
360 sys_exit(0);
361}
362
363void test_zombie_process(void* /*arg*/) {
364 klog::Info("=== Zombie Process Test ===");
365
366 bool passed = true;
367
368 // 1. 创建父子 TCB,验证 parent_pid 字段正确关联
369 auto parent_uptr =
370 kstd::make_unique<TaskControlBlock>("Parent", 10, nullptr, nullptr);
371 parent_uptr->pid = 5300;
372 parent_uptr->aux->tgid = 5300;
373 parent_uptr->aux->parent_pid = 1;
374 auto* parent = parent_uptr.get();
375
376 TaskManagerSingleton::instance().AddTask(std::move(parent_uptr));
377
378 auto* local_child =
379 new TaskControlBlock("ZombieFsmTest", 10, nullptr, nullptr);
380 local_child->pid = 5301;
381 local_child->aux->tgid = 5301;
382 local_child->aux->parent_pid = parent->pid;
383
384 if (local_child->aux->parent_pid != parent->pid) {
385 klog::Err(
386 "test_zombie_process: FAIL — child parent_pid mismatch "
387 "(expected {}, got {})",
388 parent->pid, local_child->aux->parent_pid);
389 passed = false;
390 }
391
392 local_child->aux->exit_code = 0;
393 local_child->fsm.Receive(MsgSchedule{}); // kUnInit -> kReady
394 local_child->fsm.Receive(MsgSchedule{}); // kReady -> kRunning
395 local_child->fsm.Receive(
396 MsgExit{0, true}); // kRunning -> kZombie (has parent)
397 if (local_child->GetStatus() != TaskStatus::kZombie) {
398 klog::Err(
399 "test_zombie_process: FAIL — child with living parent should be "
400 "kZombie (got {})",
401 static_cast<int>(local_child->GetStatus()));
402 passed = false;
403 }
404 if (local_child->aux->parent_pid != parent->pid) {
405 klog::Err(
406 "test_zombie_process: FAIL — child parent_pid changed after "
407 "status update");
408 passed = false;
409 }
410
411 klog::Info("Child process (pid={}) became zombie, waiting for parent to reap",
412 local_child->pid);
413
414 delete local_child;
415
416 // 3. 用真实工作函数验证有父进程的子任务能正常执行
417 std::atomic<int> work_flag{0};
418 auto real_child = kstd::make_unique<TaskControlBlock>(
419 "RealChild", 10, child_work, reinterpret_cast<void*>(&work_flag));
420 real_child->aux->parent_pid = 5300; // 指向 parent
421 TaskManagerSingleton::instance().AddTask(std::move(real_child));
422
423 int timeout = 10;
424 while (timeout > 0 && work_flag.load() == 0) {
425 (void)sys_sleep(50);
426 timeout--;
427 }
428
429 if (work_flag.load() != 1) {
430 klog::Err("test_zombie_process: FAIL — child worker did not complete");
431 passed = false;
432 } else {
433 klog::Info("test_zombie_process: child worker completed");
434 }
435
436 if (passed) {
437 klog::Info("Zombie Process Test: PASSED");
438 } else {
439 klog::Err("Zombie Process Test: FAILED");
440 g_tests_failed++;
441 }
442
443 g_tests_completed++;
444 sys_exit(passed ? 0 : 1);
445}
446
447} // namespace
448
452auto exit_system_test() -> bool {
453 klog::Info("===== Exit System Test Start =====");
454
455 // 重置全局计数器
456 g_tests_completed = 0;
457 g_tests_failed = 0;
458 g_exit_test_counter = 0;
459
460 auto& task_mgr = TaskManagerSingleton::instance();
461
462 // 测试 1: Normal exit
463 auto test1 = kstd::make_unique<TaskControlBlock>("TestExitNormal", 10,
464 test_exit_normal, nullptr);
465 task_mgr.AddTask(std::move(test1));
466
467 // 测试 2: Exit with error
468 auto test2 = kstd::make_unique<TaskControlBlock>(
469 "TestExitWithError", 10, test_exit_with_error, nullptr);
470 task_mgr.AddTask(std::move(test2));
471
472 // 测试 3: Thread exit
473 auto test3 = kstd::make_unique<TaskControlBlock>("TestThreadExit", 10,
474 test_thread_exit, nullptr);
475 task_mgr.AddTask(std::move(test3));
476
477 // 测试 4: Orphan exit
478 auto test4 = kstd::make_unique<TaskControlBlock>("TestOrphanExit", 10,
479 test_orphan_exit, nullptr);
480 task_mgr.AddTask(std::move(test4));
481
482 // 测试 5: Zombie process
483 auto test5 = kstd::make_unique<TaskControlBlock>(
484 "TestZombieProcess", 10, test_zombie_process, nullptr);
485 task_mgr.AddTask(std::move(test5));
486
487 klog::Info("Waiting for all 5 sub-tests to complete...");
488
489 // 等待所有子测试完成(每个子测试在退出前会增加 g_tests_completed)
490 // 超时: 200 * 50ms = 10s
491 int timeout = 200;
492 while (timeout > 0) {
493 (void)sys_sleep(50);
494 if (g_tests_completed.load() >= 5) {
495 break;
496 }
497 timeout--;
498 }
499
500 klog::Info("Exit System Test: completed={}, failed={}",
501 g_tests_completed.load(), g_tests_failed.load());
502
503 EXPECT_EQ(g_tests_completed, 5, "All 5 sub-tests completed");
504 EXPECT_EQ(g_tests_failed, 0, "No sub-tests failed");
505
506 klog::Info("===== Exit System Test End =====");
507 return true;
508}
auto exit_system_test() -> bool
Exit 系统测试入口
constexpr etl::fsm_state_id_t kExited
Definition task_fsm.hpp:19
constexpr etl::fsm_state_id_t kZombie
Definition task_fsm.hpp:20
auto Err(etl::format_string< Args... > fmt, Args &&... args) -> void
以 ERROR 级别记录日志
auto Info(etl::format_string< Args... > fmt, Args &&... args) -> void
以 INFO 级别记录日志
auto Debug(etl::format_string< Args... > fmt, Args &&... args) -> void
以 DEBUG 级别记录日志(SIMPLEKERNEL_MIN_LOG_LEVEL > 0 时编译期消除)
退出消息,携带退出码与父任务标志
调度消息(无负载,用作事件)
任务控制块,管理进程/线程的核心数据结构
auto sys_sleep(uint64_t ms) -> int
休眠指定毫秒数
Definition syscall.cpp:90
auto sys_exit(int code) -> int
退出当前进程或线程
Definition syscall.cpp:75
#define EXPECT_EQ(val1, val2, msg)