SimpleKernel 1.17.0
Loading...
Searching...
No Matches
clone_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
24namespace {
25
26std::atomic<int> g_tests_completed{0};
27std::atomic<int> g_tests_failed{0};
28
29std::atomic<Pid> g_child1_pid{0};
30std::atomic<Pid> g_child1_tgid{0};
31std::atomic<Pid> g_child1_parent_pid{0};
32std::atomic<Pid> g_child2_pid{0};
33std::atomic<Pid> g_child2_tgid{0};
34std::atomic<Pid> g_child2_parent_pid{0};
35std::atomic<int> g_process_done{0};
36
37std::atomic<Pid> g_leader_pid{0};
38std::atomic<Pid> g_thread1_pid{0};
39std::atomic<Pid> g_thread1_tgid{0};
40std::atomic<Pid> g_thread2_pid{0};
41std::atomic<Pid> g_thread2_tgid{0};
42std::atomic<int> g_thread_done{0};
43std::atomic<bool> g_threads_may_exit{false};
44
45std::atomic<uint64_t> g_flags_value{0};
46std::atomic<int> g_flags_done{0};
47
48void child_process_work(void* arg) {
49 auto* current = TaskManagerSingleton::instance().GetCurrentTask();
50 uint64_t child_id = reinterpret_cast<uint64_t>(arg);
51 if (child_id == 1) {
52 g_child1_pid.store(current->pid);
53 g_child1_tgid.store(current->aux->tgid);
54 g_child1_parent_pid.store(current->aux->parent_pid);
55 } else {
56 g_child2_pid.store(current->pid);
57 g_child2_tgid.store(current->aux->tgid);
58 g_child2_parent_pid.store(current->aux->parent_pid);
59 }
60 g_process_done++;
61 sys_exit(0);
62}
63
64void child_thread_work(void* arg) {
65 auto* current = TaskManagerSingleton::instance().GetCurrentTask();
66 uint64_t thread_id = reinterpret_cast<uint64_t>(arg);
67 if (thread_id == 1) {
68 g_thread1_pid.store(current->pid);
69 g_thread1_tgid.store(current->aux->tgid);
70 } else {
71 g_thread2_pid.store(current->pid);
72 g_thread2_tgid.store(current->aux->tgid);
73 }
74 g_thread_done++;
75 while (!g_threads_may_exit.load()) {
76 (void)sys_sleep(10);
77 }
78 sys_exit(0);
79}
80
81void leader_work(void* /*arg*/) {
82 auto* current = TaskManagerSingleton::instance().GetCurrentTask();
83 g_leader_pid.store(current->pid);
84 (void)sys_sleep(500);
85 sys_exit(0);
86}
87
88void flags_reporter_work(void* /*arg*/) {
89 auto* current = TaskManagerSingleton::instance().GetCurrentTask();
90 g_flags_value.store(current->aux->clone_flags.value());
91 g_flags_done++;
92 sys_exit(0);
93}
94
95void noop_work(void* /*arg*/) { sys_exit(0); }
96
97void test_clone_process(void* /*arg*/) {
98 klog::Info("=== Clone Process Test ===");
99
100 bool passed = true;
101 g_process_done = 0;
102 g_child1_pid = 0;
103 g_child1_tgid = 0;
104 g_child1_parent_pid = 0;
105 g_child2_pid = 0;
106 g_child2_tgid = 0;
107 g_child2_parent_pid = 0;
108
109 auto* self = TaskManagerSingleton::instance().GetCurrentTask();
110 Pid my_pid = self->pid;
111
112 auto child1 = kstd::make_unique<TaskControlBlock>(
113 "CloneChild1", 10, child_process_work, reinterpret_cast<void*>(1));
114 child1->aux->parent_pid = my_pid;
115 TaskManagerSingleton::instance().AddTask(std::move(child1));
116
117 auto child2 = kstd::make_unique<TaskControlBlock>(
118 "CloneChild2", 10, child_process_work, reinterpret_cast<void*>(2));
119 child2->aux->parent_pid = my_pid;
120 TaskManagerSingleton::instance().AddTask(std::move(child2));
121
122 (void)sys_sleep(200);
123
124 Pid c1_pid = g_child1_pid.load();
125 Pid c1_tgid = g_child1_tgid.load();
126 Pid c2_pid = g_child2_pid.load();
127 Pid c2_tgid = g_child2_tgid.load();
128
129 if (c1_pid == 0 || c2_pid == 0) {
130 klog::Err("Child processes did not start");
131 passed = false;
132 }
133
134 if (passed && (c1_tgid != c1_pid || c2_tgid != c2_pid)) {
135 klog::Err("Child tgid != pid: c1 tgid={} pid={}, c2 tgid={} pid={}",
136 c1_tgid, c1_pid, c2_tgid, c2_pid);
137 passed = false;
138 }
139
140 if (passed && (g_child1_parent_pid.load() != my_pid ||
141 g_child2_parent_pid.load() != my_pid)) {
142 klog::Err("Parent-child relationship incorrect");
143 passed = false;
144 }
145
146 if (passed && g_process_done.load() < 2) {
147 klog::Err("Not all child processes completed");
148 passed = false;
149 }
150
151 if (passed) {
152 klog::Info("Clone Process Test: PASSED");
153 } else {
154 klog::Err("Clone Process Test: FAILED");
155 g_tests_failed++;
156 }
157
158 g_tests_completed++;
159 sys_exit(passed ? 0 : 1);
160}
161
162void test_clone_thread(void* /*arg*/) {
163 klog::Info("=== Clone Thread Test ===");
164
165 bool passed = true;
166 g_thread_done = 0;
167 g_threads_may_exit = false;
168 g_leader_pid = 0;
169 g_thread1_pid = 0;
170 g_thread1_tgid = 0;
171 g_thread2_pid = 0;
172 g_thread2_tgid = 0;
173
174 auto leader_ptr = kstd::make_unique<TaskControlBlock>("CloneThreadLeader", 10,
175 leader_work, nullptr);
176 TaskManagerSingleton::instance().AddTask(std::move(leader_ptr));
177
178 for (int i = 0; i < 100 && g_leader_pid.load() == 0; ++i) {
179 (void)sys_sleep(10);
180 }
181
182 Pid leader_pid = g_leader_pid.load();
183 if (leader_pid == 0) {
184 klog::Err("Leader did not start");
185 g_tests_failed++;
186 g_tests_completed++;
187 sys_exit(1);
188 }
189
190 auto* leader = TaskManagerSingleton::instance().FindTask(leader_pid);
191 if (!leader) {
192 klog::Err("Leader not found in task table");
193 g_tests_failed++;
194 g_tests_completed++;
195 sys_exit(1);
196 }
197
200
201 auto thread1 = kstd::make_unique<TaskControlBlock>(
202 "CloneThread1", 10, child_thread_work, reinterpret_cast<void*>(1));
203 thread1->aux->parent_pid = leader_pid;
204 thread1->aux->tgid = leader_pid;
205 thread1->aux->clone_flags = static_cast<CloneFlags>(flags);
206 thread1->JoinThreadGroup(leader);
207 TaskManagerSingleton::instance().AddTask(std::move(thread1));
208
209 auto thread2 = kstd::make_unique<TaskControlBlock>(
210 "CloneThread2", 10, child_thread_work, reinterpret_cast<void*>(2));
211 thread2->aux->parent_pid = leader_pid;
212 thread2->aux->tgid = leader_pid;
213 thread2->aux->clone_flags = static_cast<CloneFlags>(flags);
214 thread2->JoinThreadGroup(leader);
215 TaskManagerSingleton::instance().AddTask(std::move(thread2));
216
217 for (int i = 0; i < 100 && g_thread_done.load() < 2; ++i) {
218 (void)sys_sleep(10);
219 }
220
221 Pid t1_tgid = g_thread1_tgid.load();
222 Pid t2_tgid = g_thread2_tgid.load();
223
224 if (t1_tgid != leader_pid || t2_tgid != leader_pid) {
225 klog::Err("Thread tgid mismatch: t1={} t2={} expected={}", t1_tgid, t2_tgid,
226 leader_pid);
227 passed = false;
228 }
229
230 if (g_thread1_pid.load() == g_thread2_pid.load()) {
231 klog::Err("Threads have identical PIDs");
232 passed = false;
233 }
234
235 size_t group_size = leader->GetThreadGroupSize();
236 if (group_size != 3) {
237 klog::Err("Thread group size is {}, expected 3", group_size);
238 passed = false;
239 }
240
241 g_threads_may_exit = true;
242
243 if (passed) {
244 klog::Info("Clone Thread Test: PASSED");
245 } else {
246 klog::Err("Clone Thread Test: FAILED");
247 g_tests_failed++;
248 }
249
250 g_tests_completed++;
251 sys_exit(passed ? 0 : 1);
252}
253
254void test_clone_parent_flag(void* /*arg*/) {
255 klog::Info("=== Clone Parent Flag Test ===");
256
257 bool passed = true;
258
259 {
260 auto task = kstd::make_unique<TaskControlBlock>("DefaultTask", 10,
261 noop_work, nullptr);
262 if (task->aux->parent_pid != 0) {
263 klog::Err("Default parent_pid is not 0");
264 passed = false;
265 }
266 if (task->aux->tgid != 0) {
267 klog::Err("Default tgid is not 0");
268 passed = false;
269 }
270 if (task->aux->clone_flags.value() != 0) {
271 klog::Err("Default clone_flags is not empty");
272 passed = false;
273 }
274
275 auto* raw = task.get();
276 TaskManagerSingleton::instance().AddTask(std::move(task));
277
278 if (raw->pid == 0) {
279 klog::Err("AddTask did not assign pid");
280 passed = false;
281 }
282 if (raw->aux->tgid != raw->pid) {
283 klog::Err("AddTask did not set tgid=pid: tgid={} pid={}", raw->aux->tgid,
284 raw->pid);
285 passed = false;
286 }
287 }
288
289 {
290 auto leader = kstd::make_unique<TaskControlBlock>("JoinLeader", 10,
291 noop_work, nullptr);
292 auto* leader_raw = leader.get();
293 TaskManagerSingleton::instance().AddTask(std::move(leader));
294
295 auto member = kstd::make_unique<TaskControlBlock>("JoinMember", 10,
296 noop_work, nullptr);
297 member->JoinThreadGroup(leader_raw);
298 auto* member_raw = member.get();
299 TaskManagerSingleton::instance().AddTask(std::move(member));
300
301 if (member_raw->aux->tgid != leader_raw->aux->tgid) {
302 klog::Err("JoinThreadGroup did not link tgid: member={} leader={}",
303 member_raw->aux->tgid, leader_raw->aux->tgid);
304 passed = false;
305 }
306 }
307
308 {
309 auto task = kstd::make_unique<TaskControlBlock>("FieldTest", 10, noop_work,
310 nullptr);
311 task->aux->parent_pid = 42;
312 task->aux->pgid = 99;
313 task->aux->sid = 7;
314 task->aux->clone_flags =
316
317 if (task->aux->parent_pid != 42) {
318 klog::Err("clone_flags assignment corrupted parent_pid");
319 passed = false;
320 }
321 if (task->aux->pgid != 99) {
322 klog::Err("clone_flags assignment corrupted pgid");
323 passed = false;
324 }
325 if (task->aux->sid != 7) {
326 klog::Err("clone_flags assignment corrupted sid");
327 passed = false;
328 }
329 if (!(task->aux->clone_flags & clone_flag::kThread)) {
330 klog::Err("kThread flag not set");
331 passed = false;
332 }
333 if (!(task->aux->clone_flags & clone_flag::kVm)) {
334 klog::Err("kVm flag not set");
335 passed = false;
336 }
337
338 TaskManagerSingleton::instance().AddTask(std::move(task));
339 }
340
341 if (passed) {
342 klog::Info("Clone Parent Flag Test: PASSED");
343 } else {
344 klog::Err("Clone Parent Flag Test: FAILED");
345 g_tests_failed++;
346 }
347
348 g_tests_completed++;
349 sys_exit(passed ? 0 : 1);
350}
351
352void test_clone_flags_auto_completion(void* /*arg*/) {
353 klog::Info("=== Clone Flags Auto Completion Test ===");
354
355 bool passed = true;
356 g_flags_done = 0;
357 g_flags_value = 0;
358
359 {
360 CloneFlags empty_flags{};
361 if (empty_flags.value() != 0) {
362 klog::Err("Default CloneFlags is not 0");
363 passed = false;
364 }
365
366 CloneFlags combined = static_cast<CloneFlags>(
368 if (!(combined & clone_flag::kThread)) {
369 klog::Err("kThread not set in combined flags");
370 passed = false;
371 }
372 if (!(combined & clone_flag::kVm)) {
373 klog::Err("kVm not set in combined flags");
374 passed = false;
375 }
376 if (!(combined & clone_flag::kFiles)) {
377 klog::Err("kFiles not set in combined flags");
378 passed = false;
379 }
380 if (combined & clone_flag::kSighand) {
381 klog::Err("kSighand unexpectedly set");
382 passed = false;
383 }
384 if (combined & clone_flag::kParent) {
385 klog::Err("kParent unexpectedly set");
386 passed = false;
387 }
388 }
389
390 {
391 uint64_t expected_flags =
393 auto task = kstd::make_unique<TaskControlBlock>(
394 "FlagsTask", 10, flags_reporter_work, nullptr);
395 task->aux->clone_flags = static_cast<CloneFlags>(expected_flags);
396 TaskManagerSingleton::instance().AddTask(std::move(task));
397
398 for (int i = 0; i < 100 && g_flags_done.load() == 0; ++i) {
399 (void)sys_sleep(10);
400 }
401
402 uint64_t reported = g_flags_value.load();
403 if (reported != expected_flags) {
404 klog::Err(
405 "Flags not preserved through AddTask: got 0x{:x} expected "
406 "0x{:x}",
407 reported, expected_flags);
408 passed = false;
409 }
410 }
411
412 if (passed) {
413 klog::Info("Clone Flags Auto Completion Test: PASSED");
414 } else {
415 klog::Err("Clone Flags Auto Completion Test: FAILED");
416 g_tests_failed++;
417 }
418
419 g_tests_completed++;
420 sys_exit(passed ? 0 : 1);
421}
422
423} // namespace
424
425auto clone_system_test() -> bool {
426 klog::Info("===== Clone System Test Start =====");
427
428 g_tests_completed = 0;
429 g_tests_failed = 0;
430
431 auto& task_mgr = TaskManagerSingleton::instance();
432
433 auto test1 = kstd::make_unique<TaskControlBlock>("TestCloneProcess", 10,
434 test_clone_process, nullptr);
435 task_mgr.AddTask(std::move(test1));
436
437 auto test2 = kstd::make_unique<TaskControlBlock>("TestCloneThread", 10,
438 test_clone_thread, nullptr);
439 task_mgr.AddTask(std::move(test2));
440
441 auto test3 = kstd::make_unique<TaskControlBlock>(
442 "TestCloneParentFlag", 10, test_clone_parent_flag, nullptr);
443 task_mgr.AddTask(std::move(test3));
444
445 auto test4 = kstd::make_unique<TaskControlBlock>(
446 "TestCloneFlagsAutoCompletion", 10, test_clone_flags_auto_completion,
447 nullptr);
448 task_mgr.AddTask(std::move(test4));
449
450 int timeout = 200;
451 while (timeout > 0) {
452 (void)sys_sleep(50);
453 if (g_tests_completed >= 4) {
454 break;
455 }
456 timeout--;
457 }
458
459 EXPECT_EQ(g_tests_completed, 4, "tests completed");
460 EXPECT_EQ(g_tests_failed, 0, "tests failed");
461
462 klog::Info("Clone System Test Suite: COMPLETED");
463 return true;
464}
auto clone_system_test() -> bool
constexpr uint64_t kFiles
共享文件描述符表
constexpr uint64_t kSighand
共享信号处理器
constexpr uint64_t kParent
保持相同父进程
constexpr uint64_t kThread
同一线程组
constexpr uint64_t kVm
共享地址空间
auto Err(etl::format_string< Args... > fmt, Args &&... args) -> void
以 ERROR 级别记录日志
auto Info(etl::format_string< Args... > fmt, Args &&... args) -> void
以 INFO 级别记录日志
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)
etl::flags< uint64_t, clone_flag::kAllMask > CloneFlags
克隆标志位
size_t Pid
进程 ID 类型