305 lines
10 KiB
C
305 lines
10 KiB
C
#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, "恢复原有信号屏蔽集");
|
||
}
|
||
|
||
// 非法timespec:tv_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;
|
||
}
|