rt_sigpending系统调用及示例

我们来深入学习 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 的信号被处理。

rt_sigpending系统调用及示例-CSDN博客

此条目发表在linux文章分类目录。将固定链接加入收藏夹。

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注