DragonOS/user/apps/c_unitest/test_rt_sigtimedwait.c

305 lines
10 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.

#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <sys/syscall.h>
#include <errno.h>
#include <string.h>
#include <time.h>
#include <pthread.h>
#include <sys/types.h>
// 兼容性处理:获取 SYS_rt_sigtimedwait 宏
#ifndef SYS_rt_sigtimedwait
# ifdef __NR_rt_sigtimedwait
# define SYS_rt_sigtimedwait __NR_rt_sigtimedwait
# else
# error "SYS_rt_sigtimedwait 未定义,无法直接进行系统调用测试"
# endif
#endif
// libc 包装:优先使用 sigtimedwait保证在不同libc/内核下参数兼容
static int rt_sigtimedwait_libc(const sigset_t *set, siginfo_t *info, const struct timespec *timeout) {
return sigtimedwait(set, info, timeout);
}
// 原始syscall用于特定场景如测试非法sigsetsize
static int sys_rt_sigtimedwait_raw(const sigset_t *set, siginfo_t *info, const struct timespec *timeout, size_t sigsetsize) {
return (int)syscall(SYS_rt_sigtimedwait, set, info, timeout, sigsetsize);
}
// 测试统计
static int total_tests = 0;
static int passed_tests = 0;
static int failed_tests = 0;
static char failed_test_names[200][256];
static int failed_test_count = 0;
#define TEST_ASSERT(condition, test_name) do { \
total_tests++; \
if (condition) { \
passed_tests++; \
printf("PASS - %s\n", test_name); \
} else { \
failed_tests++; \
if (failed_test_count < 200) { \
snprintf(failed_test_names[failed_test_count], sizeof(failed_test_names[failed_test_count]), "%s", test_name); \
failed_test_count++; \
} \
printf("FAIL - %s (errno=%d: %s)\n", test_name, errno, strerror(errno)); \
} \
} while(0)
// 工具函数:阻塞/解除阻塞 指定信号
static int block_signal(int sig, sigset_t *oldset) {
sigset_t set;
sigemptyset(&set);
sigaddset(&set, sig);
return pthread_sigmask(SIG_BLOCK, &set, oldset);
}
static int unblock_signal(int sig, const sigset_t *oldset) {
(void)sig; // 未使用,仅保持接口对称
return pthread_sigmask(SIG_SETMASK, oldset, NULL);
}
// 工具函数使用rt_sigtimedwait带0超时轮询一次并(如存在)清理信号
static int poll_and_consume_signal_once(int sig) {
sigset_t set;
siginfo_t info;
struct timespec ts = {0, 0};
sigemptyset(&set);
sigaddset(&set, sig);
int r = rt_sigtimedwait_libc(&set, &info, &ts);
return r; // =sig 表示读到并消费;-1 且 errno=EAGAIN 表示无;其它为异常
}
// 用于线程延迟发送信号
typedef struct {
pid_t pid;
int sig;
int delay_ms;
} sender_args_t;
static void *delayed_kill_sender(void *arg) {
sender_args_t *a = (sender_args_t *)arg;
// 确保该线程不接收要发送的信号:阻塞之,让信号成为进程待处理,供主线程 sigtimedwait 消费
sigset_t set, oldset;
sigemptyset(&set);
sigaddset(&set, a->sig);
pthread_sigmask(SIG_BLOCK, &set, &oldset);
struct timespec ts;
ts.tv_sec = a->delay_ms / 1000;
ts.tv_nsec = (long)(a->delay_ms % 1000) * 1000000L;
nanosleep(&ts, NULL);
printf("[TEST_DEBUG]:delayed_kill_sender to kill %d sig %d\n", a->pid, a->sig);
// 进程定向发送
kill(a->pid, a->sig);
printf("[TEST_DEBUG]:delayed_kill_sender killed %d sig %d\n", a->pid, a->sig);
// 还原该线程掩码
pthread_sigmask(SIG_SETMASK, &oldset, NULL);
printf("[TEST_DEBUG]:delayed_kill_sender unblocked %d sig %d\n", a->pid, a->sig);
return NULL;
}
// 基础功能阻塞SIGUSR1 -> 自发 -> rt_sigtimedwait应立即返回该信号
static void test_rt_sigtimedwait_basic_self_kill_SIGUSR1() {
printf("=== 测试: 基础 - 阻塞SIGUSR1后自发并等待 ===\n");
sigset_t oldset;
int rc = block_signal(SIGUSR1, &oldset);
TEST_ASSERT(rc == 0, "阻塞SIGUSR1");
// 确保没有遗留的待处理SIGUSR1
while (poll_and_consume_signal_once(SIGUSR1) == SIGUSR1) {}
// 发送到自身
pid_t me = getpid();
rc = kill(me, SIGUSR1);
TEST_ASSERT(rc == 0, "向自身发送SIGUSR1");
sigset_t waitset;
sigemptyset(&waitset);
sigaddset(&waitset, SIGUSR1);
siginfo_t info;
struct timespec timeout = {2, 0}; // 2秒超时正常应很快返回
int ret = rt_sigtimedwait_libc(&waitset, &info, &timeout);
TEST_ASSERT(ret == SIGUSR1, "rt_sigtimedwait 返回SIGUSR1");
if (ret == SIGUSR1) {
TEST_ASSERT(info.si_signo == SIGUSR1, "info.si_signo == SIGUSR1");
// kill() 产生的应为 SI_USER
TEST_ASSERT(info.si_code == SI_USER, "info.si_code == SI_USER");
TEST_ASSERT(info.si_pid == me, "info.si_pid 为当前进程");
TEST_ASSERT(info.si_uid == getuid(), "info.si_uid 为当前用户");
}
rc = unblock_signal(SIGUSR1, &oldset);
TEST_ASSERT(rc == 0, "恢复原有信号屏蔽集");
}
// 超时等待一个未发送的被阻塞信号短超时应返回EAGAIN
static void test_rt_sigtimedwait_timeout() {
printf("\n=== 测试: 超时 - 无信号到达时返回EAGAIN ===\n");
const int sig = SIGUSR2;
sigset_t oldset;
int rc = block_signal(sig, &oldset);
TEST_ASSERT(rc == 0, "阻塞SIGUSR2");
// 清理可能的遗留
while (poll_and_consume_signal_once(sig) == sig) {}
sigset_t waitset;
sigemptyset(&waitset);
sigaddset(&waitset, sig);
siginfo_t info;
struct timespec timeout = {0, 100 * 1000 * 1000}; // 100ms
int ret = rt_sigtimedwait_libc(&waitset, &info, &timeout);
TEST_ASSERT(ret == -1 && errno == EAGAIN, "无信号到达时超时返回EAGAIN");
rc = unblock_signal(sig, &oldset);
TEST_ASSERT(rc == 0, "恢复原有信号屏蔽集");
}
// 非法timespectv_nsec越界应返回EINVAL
static void test_rt_sigtimedwait_invalid_timespec() {
printf("\n=== 测试: 参数校验 - 非法timespec返回EINVAL ===\n");
const int sig = SIGUSR1;
sigset_t oldset;
int rc = block_signal(sig, &oldset);
TEST_ASSERT(rc == 0, "阻塞SIGUSR1");
sigset_t waitset;
sigemptyset(&waitset);
sigaddset(&waitset, sig);
siginfo_t info;
struct timespec bad = { .tv_sec = 0, .tv_nsec = 2000000000L }; // > 1e9-1
int ret = rt_sigtimedwait_libc(&waitset, &info, &bad);
TEST_ASSERT(ret == -1 && errno == EINVAL, "tv_nsec越界 -> EINVAL");
rc = unblock_signal(sig, &oldset);
TEST_ASSERT(rc == 0, "恢复原有信号屏蔽集");
}
// 延迟发送NULL超时指针(无限等待) + 后台线程延迟发送实时信号,调用应被唤醒并返回
static void test_rt_sigtimedwait_null_timeout_with_delayed_rt_signal() {
printf("\n=== 测试: NULL超时 + 延迟发送SIGRTMIN+1 ===\n");
int rtsig = SIGRTMIN + 1;
sigset_t oldset;
int rc = block_signal(rtsig, &oldset);
TEST_ASSERT(rc == 0, "阻塞SIGRTMIN+1");
// 线程延迟发送
pthread_t th;
sender_args_t args = { .pid = getpid(), .sig = rtsig, .delay_ms = 100 };
rc = pthread_create(&th, NULL, delayed_kill_sender, &args);
TEST_ASSERT(rc == 0, "创建延迟发送线程");
sigset_t waitset;
sigemptyset(&waitset);
sigaddset(&waitset, rtsig);
siginfo_t info;
printf("[TEST_DEBUG]: to call rt_sigtimedwait_libc\n");
// NULL超时按规范为无限等待但我们确保100ms内会到信号
int ret = rt_sigtimedwait_libc(&waitset, &info, NULL);
printf("[TEST_DEBUG]: rt_sigtimedwait_libc returned %d\n", ret);
TEST_ASSERT(ret == rtsig, "rt_sigtimedwait(NULL) 收到实时信号");
if (ret == rtsig) {
TEST_ASSERT(info.si_signo == rtsig, "info.si_signo == 发送的实时信号");
TEST_ASSERT((info.si_code == SI_USER) || (info.si_code == SI_TKILL) || (info.si_code == SI_QUEUE), "info.si_code 合理");
TEST_ASSERT(info.si_pid == getpid(), "info.si_pid 为当前进程");
}
printf("[TEST_DEBUG]: to join thread\n");
if (rc == 0) {
pthread_join(th, NULL);
}
printf("[TEST_DEBUG]: to unblock signal\n");
rc = unblock_signal(rtsig, &oldset);
TEST_ASSERT(rc == 0, "恢复原有信号屏蔽集");
}
// 零超时轮询ts={0,0} 未有信号立刻返回EAGAIN
static void test_rt_sigtimedwait_zero_timeout_poll() {
printf("\n=== 测试: 轮询模式 - 零超时无信号返回EAGAIN ===\n");
const int sig = SIGUSR1;
sigset_t oldset;
int rc = block_signal(sig, &oldset);
TEST_ASSERT(rc == 0, "阻塞SIGUSR1");
// 清空待处理
while (poll_and_consume_signal_once(sig) == sig) {}
sigset_t waitset;
sigemptyset(&waitset);
sigaddset(&waitset, sig);
siginfo_t info;
struct timespec ts = {0, 0};
int ret = rt_sigtimedwait_libc(&waitset, &info, &ts);
TEST_ASSERT(ret == -1 && errno == EAGAIN, "零超时无信号 -> EAGAIN");
rc = unblock_signal(sig, &oldset);
TEST_ASSERT(rc == 0, "恢复原有信号屏蔽集");
}
// 非法sigsetsize传0期望返回EINVAL具体行为依赖内核这里按Linux常见实现
static void test_rt_sigtimedwait_invalid_sigsetsize() {
printf("\n=== 测试: 参数校验 - 非法sigsetsize返回EINVAL ===\n");
const int sig = SIGUSR1;
sigset_t oldset;
int rc = block_signal(sig, &oldset);
TEST_ASSERT(rc == 0, "阻塞SIGUSR1");
sigset_t waitset;
sigemptyset(&waitset);
sigaddset(&waitset, sig);
siginfo_t info;
struct timespec ts = {0, 0};
int ret = sys_rt_sigtimedwait_raw(&waitset, &info, &ts, 0 /* 非法大小 */);
TEST_ASSERT(ret == -1 && errno == EINVAL, "sigsetsize=0 -> EINVAL");
rc = unblock_signal(sig, &oldset);
TEST_ASSERT(rc == 0, "恢复原有信号屏蔽集");
}
int main() {
printf("开始 rt_sigtimedwait 系统调用测试\n");
printf("当前进程 PID=%d\n", getpid());
test_rt_sigtimedwait_basic_self_kill_SIGUSR1();
test_rt_sigtimedwait_timeout();
test_rt_sigtimedwait_invalid_timespec();
test_rt_sigtimedwait_null_timeout_with_delayed_rt_signal();
test_rt_sigtimedwait_zero_timeout_poll();
test_rt_sigtimedwait_invalid_sigsetsize();
printf("\n=== rt_sigtimedwait 测试完成 ===\n");
printf("\n=== 测试结果总结 ===\n");
printf("总测试数: %d\n", total_tests);
printf("通过: %d\n", passed_tests);
printf("失败: %d\n", failed_tests);
printf("成功率: %.1f%%\n", total_tests > 0 ? (float)passed_tests / total_tests * 100 : 0);
if (failed_tests > 0) {
printf("\n失败的测试用例:\n");
for (int i = 0; i < failed_test_count; i++) {
printf(" - %s\n", failed_test_names[i]);
}
} else {
printf("\n所有测试用例都通过了!\n");
}
return failed_tests > 0 ? 1 : 0;
}