DragonOS/user/apps/c_unitest/test_waitid.c

461 lines
12 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* test_waitid.c - 测试waitid系统调用的功能实现
*
* 这个测试程序验证waitid系统调用的各种功能包括
* 1. 等待子进程退出 (WEXITED)
* 2. 等待子进程停止 (WSTOPPED)
* 3. 等待子进程继续 (WCONTINUED)
* 4. 非阻塞模式 (WNOHANG)
* 5. 只观测不回收模式 (WNOWAIT)
* 6. 不同进程选择器 (P_ALL, P_PID, P_PGID)
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#include <signal.h>
#include <string.h>
#include <errno.h>
#include <assert.h>
#define TEST_PASSED 0
#define TEST_FAILED 1
// 打印siginfo_t结构的内容
static void print_siginfo(const siginfo_t *info) {
printf(" siginfo_t: signo=%d, errno=%d, code=%d, pid=%d, uid=%d, status=%d\n",
info->si_signo, info->si_errno, info->si_code,
info->si_pid, info->si_uid, info->si_status);
}
// 测试基本退出功能
static int test_basic_exit(void) {
printf("测试1: 基本退出功能 (WEXITED)\n");
pid_t pid = fork();
if (pid < 0) {
perror("fork");
return TEST_FAILED;
}
if (pid == 0) {
// 子进程
sleep(1);
exit(42);
}
// 父进程
siginfo_t info;
memset(&info, 0, sizeof(info));
int ret = waitid(P_PID, (id_t)pid, &info, WEXITED);
if (ret != 0) {
perror("waitid");
return TEST_FAILED;
}
printf(" 成功等待子进程退出\n");
print_siginfo(&info);
if (info.si_signo != SIGCHLD) {
printf(" 错误: si_signo应为SIGCHLD(%d),实际为%d\n", SIGCHLD, info.si_signo);
return TEST_FAILED;
}
if (info.si_code != CLD_EXITED) {
printf(" 错误: si_code应为CLD_EXITED(%d),实际为%d\n", CLD_EXITED, info.si_code);
return TEST_FAILED;
}
if (info.si_status != 42) {
printf(" 错误: si_status应为42实际为%d\n", info.si_status);
return TEST_FAILED;
}
printf(" 测试1通过\n\n");
return TEST_PASSED;
}
// 测试非阻塞模式
static int test_nonblocking(void) {
printf("测试2: 非阻塞模式 (WNOHANG)\n");
pid_t pid = fork();
if (pid < 0) {
perror("fork");
return TEST_FAILED;
}
if (pid == 0) {
// 子进程,稍后退出
sleep(2);
exit(0);
}
// 父进程立即检查(非阻塞)
siginfo_t info;
memset(&info, 0, sizeof(info));
int ret = waitid(P_PID, (id_t)pid, &info, WEXITED | WNOHANG);
if (ret != 0) {
perror("waitid");
return TEST_FAILED;
}
if (info.si_signo != 0) {
printf(" 错误: 非阻塞模式下si_signo应为0实际为%d\n", info.si_signo);
return TEST_FAILED;
}
printf(" 非阻塞检查:无事件(正确)\n");
// 现在阻塞等待
ret = waitid(P_PID, (id_t)pid, &info, WEXITED);
if (ret != 0) {
perror("waitid");
return TEST_FAILED;
}
if (info.si_signo != SIGCHLD || info.si_code != CLD_EXITED) {
printf(" 错误: 阻塞等待失败\n");
return TEST_FAILED;
}
printf(" 阻塞等待:成功\n");
printf(" 测试2通过\n\n");
return TEST_PASSED;
}
// 测试停止和继续功能
static int test_stop_continue(void) {
printf("测试3: 停止和继续功能 (WSTOPPED, WCONTINUED)\n");
pid_t pid = fork();
if (pid < 0) {
perror("fork");
return TEST_FAILED;
}
if (pid == 0) {
// 子进程
while (1) {
printf(" 子进程运行中...\n");
sleep(1);
}
exit(0);
}
// 父进程
sleep(1); // 让子进程运行一会儿
// 停止子进程
printf(" 发送SIGSTOP停止子进程\n");
if (kill(pid, SIGSTOP) != 0) {
perror("kill SIGSTOP");
return TEST_FAILED;
}
siginfo_t info;
memset(&info, 0, sizeof(info));
// 等待停止事件
int ret = waitid(P_PID, (id_t)pid, &info, WSTOPPED);
if (ret != 0) {
perror("waitid WSTOPPED");
return TEST_FAILED;
}
printf(" 成功检测到停止事件\n");
print_siginfo(&info);
if (info.si_signo != SIGCHLD || info.si_code != CLD_STOPPED) {
printf(" 错误: 停止事件信息不正确\n");
return TEST_FAILED;
}
// 继续子进程
printf(" 发送SIGCONT继续子进程\n");
if (kill(pid, SIGCONT) != 0) {
perror("kill SIGCONT");
return TEST_FAILED;
}
memset(&info, 0, sizeof(info));
// 等待继续事件
ret = waitid(P_PID, (id_t)pid, &info, WCONTINUED);
if (ret != 0) {
perror("waitid WCONTINUED");
return TEST_FAILED;
}
printf(" 成功检测到继续事件\n");
print_siginfo(&info);
if (info.si_signo != SIGCHLD || info.si_code != CLD_CONTINUED) {
printf(" 错误: 继续事件信息不正确\n");
return TEST_FAILED;
}
// 终止子进程
kill(pid, SIGTERM);
waitid(P_PID, (id_t)pid, &info, WEXITED);
printf(" 测试3通过\n\n");
return TEST_PASSED;
}
// 测试只观测不回收模式
static int test_nowait(void) {
printf("测试4: 只观测不回收模式 (WNOWAIT)\n");
pid_t pid = fork();
if (pid < 0) {
perror("fork");
return TEST_FAILED;
}
if (pid == 0) {
// 子进程
sleep(1);
exit(99);
}
// 父进程
siginfo_t info;
// 第一次观测(不回收)
memset(&info, 0, sizeof(info));
int ret = waitid(P_PID, (id_t)pid, &info, WEXITED | WNOWAIT);
if (ret != 0) {
perror("waitid WNOWAIT 1");
return TEST_FAILED;
}
printf(" 第一次观测(不回收):\n");
print_siginfo(&info);
if (info.si_signo != SIGCHLD || info.si_code != CLD_EXITED || info.si_status != 99) {
printf(" 错误: 第一次观测信息不正确\n");
return TEST_FAILED;
}
// 第二次观测(应该还能看到相同信息)
memset(&info, 0, sizeof(info));
ret = waitid(P_PID, (id_t)pid, &info, WEXITED | WNOWAIT);
if (ret != 0) {
perror("waitid WNOWAIT 2");
return TEST_FAILED;
}
printf(" 第二次观测(不回收):\n");
print_siginfo(&info);
if (info.si_signo != SIGCHLD || info.si_code != CLD_EXITED || info.si_status != 99) {
printf(" 错误: 第二次观测信息不正确\n");
return TEST_FAILED;
}
// 最终回收 - 使用waitpid而不是waitid
int status;
pid_t reaped = waitpid(pid, &status, 0);
if (reaped != pid) {
perror("waitpid final");
return TEST_FAILED;
}
printf(" 最终回收 (waitpid): pid=%d, status=%d\n", reaped, WEXITSTATUS(status));
// 再次检查应该没有事件了
memset(&info, 0, sizeof(info));
ret = waitid(P_PID, (id_t)pid, &info, WEXITED | WNOHANG);
printf(" 回收后检查 - waitid返回: %d, errno=%d\n", ret, errno);
printf(" 回收后检查: ");
print_siginfo(&info);
if (ret != 0) {
// 在某些系统上回收后waitid可能返回错误而不是无事件
printf(" 注意: 回收后waitid返回错误 (errno=%d),这是可接受的行为\n", errno);
} else {
if (info.si_signo != 0) {
printf(" 错误: 回收后仍有事件\n");
return TEST_FAILED;
}
}
printf(" 测试4通过\n\n");
return TEST_PASSED;
}
// 测试进程组功能
static int test_process_group(void) {
printf("测试5: 进程组功能 (P_PGID)\n");
// 创建新的进程组
pid_t pgid = getpid();
if (setpgid(0, 0) != 0) {
perror("setpgid");
return TEST_FAILED;
}
pid_t pid1 = fork();
if (pid1 < 0) {
perror("fork 1");
return TEST_FAILED;
}
if (pid1 == 0) {
// 子进程1
setpgid(0, pgid);
sleep(1);
exit(1);
}
pid_t pid2 = fork();
if (pid2 < 0) {
perror("fork 2");
return TEST_FAILED;
}
if (pid2 == 0) {
// 子进程2
setpgid(0, pgid);
sleep(2);
exit(2);
}
// 父进程等待进程组中的子进程
siginfo_t info;
int count = 0;
while (count < 2) {
memset(&info, 0, sizeof(info));
int ret = waitid(P_PGID, (id_t)pgid, &info, WEXITED);
if (ret != 0) {
perror("waitid P_PGID");
return TEST_FAILED;
}
if (info.si_signo == SIGCHLD && info.si_code == CLD_EXITED) {
printf(" 等待到进程组中的子进程: pid=%d, status=%d\n",
info.si_pid, info.si_status);
count++;
}
}
printf(" 成功等待到进程组中的所有子进程\n");
printf(" 测试5通过\n\n");
return TEST_PASSED;
}
// 测试等待所有子进程
static int test_wait_all(void) {
printf("测试6: 等待所有子进程 (P_ALL)\n");
pid_t pids[3];
for (int i = 0; i < 3; i++) {
pids[i] = fork();
if (pids[i] < 0) {
perror("fork");
return TEST_FAILED;
}
if (pids[i] == 0) {
// 子进程
sleep(i + 1);
exit(100 + i);
}
}
// 父进程等待所有子进程
siginfo_t info;
int count = 0;
while (count < 3) {
memset(&info, 0, sizeof(info));
int ret = waitid(P_ALL, 0, &info, WEXITED);
if (ret != 0) {
perror("waitid P_ALL");
return TEST_FAILED;
}
if (info.si_signo == SIGCHLD && info.si_code == CLD_EXITED) {
printf(" 等待到子进程: pid=%d, status=%d\n",
info.si_pid, info.si_status);
count++;
}
}
printf(" 成功等待到所有子进程\n");
printf(" 测试6通过\n\n");
return TEST_PASSED;
}
// 测试错误参数
static int test_error_cases(void) {
printf("测试7: 错误参数测试\n");
siginfo_t info;
// 测试无效的which参数
int ret = waitid((idtype_t)999, 0, &info, WEXITED);
if (ret == 0) {
printf(" 错误: 无效的which参数应该失败\n");
return TEST_FAILED;
}
printf(" 无效which参数正确返回错误 (errno=%d)\n", errno);
// 测试无效的options不包含任何事件位
ret = waitid(P_ALL, 0, &info, WNOHANG);
if (ret == 0) {
printf(" 错误: 无效的options参数应该失败\n");
return TEST_FAILED;
}
printf(" 无效options参数正确返回错误 (errno=%d)\n", errno);
// 测试不存在的进程
ret = waitid(P_PID, 99999, &info, WEXITED | WNOHANG);
if (ret != 0) {
// 在某些系统上,不存在的进程可能返回错误而不是无事件
printf(" 注意: 不存在的进程返回错误 (errno=%d),这是可接受的行为\n", errno);
} else {
if (info.si_signo != 0) {
printf(" 错误: 不存在的进程si_signo应该为0\n");
return TEST_FAILED;
}
printf(" 不存在的进程正确返回无事件\n");
}
printf(" 测试7通过\n\n");
return TEST_PASSED;
}
int main(void) {
printf("开始测试waitid系统调用功能\n");
printf("================================\n\n");
int passed = 0;
int total = 7;
// 运行所有测试
if (test_basic_exit() == TEST_PASSED) passed++;
if (test_nonblocking() == TEST_PASSED) passed++;
if (test_stop_continue() == TEST_PASSED) passed++;
if (test_nowait() == TEST_PASSED) passed++;
if (test_process_group() == TEST_PASSED) passed++;
if (test_wait_all() == TEST_PASSED) passed++;
if (test_error_cases() == TEST_PASSED) passed++;
printf("================================\n");
printf("测试完成: %d/%d 通过\n", passed, total);
if (passed == total) {
printf("所有测试通过waitid系统调用功能正常。\n");
return 0;
} else {
printf("部分测试失败,请检查实现。\n");
return 1;
}
}