我们来深入学习 rt_sigpending
系统调用,摘要:rt_sigpending
系统调用用于检查被阻塞但尚未处理的待处理信号集。通过sigpending
函数调用,可获取当前被屏蔽信号的”等待中”状态,类似查看”信号邮箱”中的未读邮件。使用时需配合sigprocmask
设置信号屏蔽字,并结合sigset_t
相关函数操作信号集。示例代码演示了如何阻塞SIGUSR1/SIGUSR2信号,在10秒内捕获待处理信号,最后解除阻塞使信号被处理。该机制适用于需要延迟处理特定信号的场景。(149字)
1. 函数介绍
在 Linux 中,你可以使用 sigprocmask
来阻塞(或屏蔽)某些信号,这意味着即使这些信号被发送到你的进程,它们也不会立即被处理,而是进入一种“等待中”(pending)的状态。
rt_sigpending
(通常通过用户空间的 sigpending
函数调用)的作用就是让你检查当前有哪些信号正处于这种“等待中”的状态。这在你需要知道在屏蔽信号期间发生了哪些信号时非常有用。
你可以把它想象成一个“信号邮箱”的查看器:当信号被阻塞时,它们就像邮件一样被“投递”到你的邮箱里(变成 pending),但你暂时不“阅读”它们。sigpending
就是让你打开邮箱看看里面有哪些“未读邮件”(待处理信号)。
2. 函数原型
#include <signal.h>
int sigpending(sigset_t *set);
3. 功能
获取当前进程中所有被阻塞且已产生但尚未递送(即待处理)的信号集合。
4. 参数
set
:sigset_t *
类型。- 一个指向
sigset_t
类型变量的指针。调用成功后,这个变量将被填充为当前所有待处理信号的集合。
5. 返回值
- 成功: 返回 0。
- 失败: 返回 -1,并设置全局变量
errno
来指示具体的错误原因(这种情况比较少见)。
6. 相似函数或关联函数
sigprocmask
: 用于设置或查询当前进程的信号屏蔽字(signal mask),即哪些信号当前被阻塞。sigset_t
: 用于存储信号集合的数据类型。sigemptyset
: 初始化一个sigset_t
集合为空。sigfillset
: 初始化一个sigset_t
集合,使其包含所有信号。sigaddset
: 向一个sigset_t
集合中添加一个特定的信号。sigdelset
: 从一个sigset_t
集合中删除一个特定的信号。sigismember
: 检查一个特定的信号是否属于某个sigset_t
集合。
7. 示例代码
下面是一个例子,演示如何阻塞信号,然后使用 sigpending
来检查哪些信号在等待。
#define _GNU_SOURCE // 启用 GNU 扩展
#include <stdio.h>
#include <stdlib.h> // 包含 exit
#include <unistd.h> // 包含 sleep
#include <signal.h> // 包含信号处理相关函数
#include <string.h> // 包含 memset
// 一个简单的信号处理函数
void signal_handler(int sig) {
printf("Caught signal %d\n", sig);
// 在实际应用中,信号处理函数应尽量简短,并只调用异步信号安全函数
}
int main() {
sigset_t block_set; // 用于设置要阻塞的信号
sigset_t pending_set; // 用于接收待处理的信号集
printf("My PID is: %d\n", getpid());
// 1. 设置 SIGUSR1 和 SIGUSR2 的处理函数
struct sigaction sa;
memset(&sa, 0, sizeof(sa));
sa.sa_handler = signal_handler; // 使用上面定义的处理函数
sigemptyset(&sa.sa_mask); // 处理函数执行时不额外阻塞信号
sa.sa_flags = 0; // 没有特殊标志
if (sigaction(SIGUSR1, &sa, NULL) == -1) { // 设置 SIGUSR1 的处理
perror("sigaction SIGUSR1");
exit(EXIT_FAILURE);
}
if (sigaction(SIGUSR2, &sa, NULL) == -1) { // 设置 SIGUSR2 的处理
perror("sigaction SIGUSR2");
exit(EXIT_FAILURE);
}
// 2. 创建一个信号集,并添加要阻塞的信号 (SIGUSR1 和 SIGUSR2)
sigemptyset(&block_set); // 初始化为空集
sigaddset(&block_set, SIGUSR1); // 添加 SIGUSR1
sigaddset(&block_set, SIGUSR2); // 添加 SIGUSR2
// 3. 使用 sigprocmask 阻塞 SIGUSR1 和 SIGUSR2
printf("Blocking SIGUSR1 and SIGUSR2...\n");
printf("Try sending them now:\n");
printf(" In another terminal, run: 'kill -USR1 %d'\n", getpid());
printf(" In another terminal, run: 'kill -USR2 %d'\n", getpid());
printf("Sleeping for 10 seconds...\n");
if (sigprocmask(SIG_BLOCK, &block_set, NULL) == -1) {
perror("sigprocmask BLOCK");
exit(EXIT_FAILURE);
}
// 4. 在阻塞期间睡眠,等待信号发送
sleep(10);
printf("10 seconds passed. Signals should be pending now.\n");
// 5. 调用 sigpending 检查哪些信号在等待
if (sigpending(&pending_set) == -1) {
perror("sigpending");
exit(EXIT_FAILURE);
}
// 6. 检查并打印待处理的信号
printf("Checking pending signals:\n");
if (sigismember(&pending_set, SIGUSR1)) {
printf(" SIGUSR1 is pending.\n");
} else {
printf(" SIGUSR1 is NOT pending.\n");
}
if (sigismember(&pending_set, SIGUSR2)) {
printf(" SIGUSR2 is pending.\n");
} else {
printf(" SIGUSR2 is NOT pending.\n");
}
// 7. 解除对 SIGUSR1 和 SIGUSR2 的阻塞
printf("Unblocking SIGUSR1 and SIGUSR2...\n");
if (sigprocmask(SIG_UNBLOCK, &block_set, NULL) == -1) {
perror("sigprocmask UNBLOCK");
exit(EXIT_FAILURE);
}
printf("Unblocked. Any pending signals should be delivered now.\n");
printf("Sleeping for 1 second to allow signal handlers to run...\n");
sleep(1);
printf("Program exiting.\n");
return 0;
}
编译和运行:
# 假设代码保存在 sigpending_example.c 中
gcc -o sigpending_example sigpending_example.c
# 终端 1: 运行程序
./sigpending_example
# 程序会输出 PID,例如 My PID is: 12345
# 然后提示你发送信号
# 终端 2: 发送信号 (在程序提示的 10 秒内执行)
kill -USR1 12345
kill -USR2 12345
# 观察终端 1 的输出,你会看到程序报告哪些信号是 pending 的,
# 以及在解除阻塞后信号被处理。
这个例子清晰地展示了信号阻塞和 sigpending
的工作流程:信号被阻塞 -> 信号发送 -> 信号变为 pending -> 使用 sigpending
查询 -> 解除阻塞 -> pending 的信号被处理。