好的,我们来深入学习 rt_sigpending 系统调用,从 Linux 编程小白的角度出发。

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 的信号被处理。