DragonOS/user/apps/c_unitest/test_rt_sigtimedwait.c

305 lines
10 KiB
C
Raw Normal View History

#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;
}