嵌入式Linux系统编程 — 6.6 信号掩码

目录

1 信号掩码介绍

2 sigprocmas函数

3 sigsuspend函数阻塞等待信号


1 信号掩码介绍

信号掩码(Signal Mask)是操作系统中用于控制进程接收信号的一种机制。每个进程都有一个或多个信号掩码,它们定义了哪些信号在特定时间被阻塞(即暂时忽略),哪些信号可以被进程接收。

向信号掩码中添加一个信号,通常有如下几种方式:

  • 当应用程序调用 signal()或 sigaction()函数为某一个信号设置处理方式时,进程会自动将该信号添加到信号掩码中, 这样保证了在处理一个给定的信号时,如果此信号再次发生,那么它将会被阻塞;对于 sigaction(),需要根据 sigaction()函数是否设置了 SA_NODEFER 标志决定该信号添加到信号掩码中;当信号处理函数结束返回后,会自动将该信号从信号掩码中移除。
  • 使用 sigaction()函数为信号设置处理方式时,可通过 sa_mask 参数进行设置该组信号是否自动添加到信号掩码中。
  • 除了以上两种方式之外,还可以使用 sigprocmask()系统调用,随时可以显式地向信号掩码中添加/移除信号。

2 sigprocmas函数

sigprocmask() 函数用于管理进程的信号屏蔽字(signal mask),即控制哪些信号可以被进程接收,哪些信号被暂时阻塞。函数原型如下所示:

#include <signal.h>

int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);
  • how:指定了如何修改信号屏蔽字,可以是以下宏之一:

    • SIG_BLOCK:将 set 指向的信号集中的信号添加到当前信号屏蔽字中。
    • SIG_UNBLOCK:将 set 指向的信号集中的信号从当前信号屏蔽字中移除。
    • SIG_SETMASK:将当前信号屏蔽字设置为 set 指向的信号集。
  • set:指向 sigset_t 结构的指针,该结构定义了一组信号,根据 how 参数的值,这些信号将被添加到、移除或设置为当前的信号屏蔽字。

  • oldset:如果非空,sigprocmask() 将当前的信号屏蔽字存储在 oldset 指向的 sigset_t 结构中。

  • 返回值:成功时返回 0,失败时返回 -1,并设置 errno 以指示错误。

以下是使用 sigprocmask() 的示例代码:

#include <stdio.h>
#include <signal.h>
#include <errno.h>

int main() 
{
    sigset_t set, oldset, pending;

    // 将SIGINT添加到信号集中
    sigemptyset(&set);
    sigaddset(&set, SIGINT);

    // 阻塞SIGINT信号
    if (sigprocmask(SIG_BLOCK, &set, &oldset) < 0) {
        perror("sigprocmask SIG_BLOCK");
        return 1;
    }

    // ... 执行其他任务,SIGINT信号被阻塞 ...

    // 检查SIGINT是否在待处理信号集中
    sigpending(&pending);
    if (sigismember(&pending, SIGINT)) {
        printf("SIGINT is pending.\n");
    }

    // 恢复原始信号掩码
    if (sigprocmask(SIG_SETMASK, &oldset, NULL) < 0) {
        perror("sigprocmask SIG_SETMASK");
        return 1;
    }

    printf("SIGINT is no longer blocked.\n");

    return 0;
}

程序首先创建并初始化了一个信号集 set,然后将 SIGINT 信号添加到这个信号集中。接着使用 sigprocmask() 函数调用,通过 SIG_BLOCK 选项来阻塞 SIGINT 信号,并保存旧的信号掩码到 oldset 中。之后,使用 sigpending() 函数来检查 SIGINT 是否在待处理信号集中。最后,我们使用 sigprocmask() 函数调用,通过 SIG_SETMASK 选项来恢复原始的信号掩码,从而解除对 SIGINT 信号的阻塞。运行结果如下:

3 sigsuspend函数阻塞等待信号

sigsuspend() 函数会替换当前进程的信号掩码为函数参数sigmask 指向的信号掩码,并立即挂起调用进程的执行。进程会一直挂起直到有信号到达,并且该信号不在当前的信号屏蔽字中,或者有信号被 sigpending() 函数标记为“待处理”。当信号被处理后,进程恢复执行,并恢复到之前的信号掩码。

可以用一句话进行理解,暂停当前进程直到有指定信号掩码之外的信号触发处理完成。函数原型如下所示:

#include <signal.h>

int sigsuspend(const sigset_t *mask);
  • mask: 参数 mask 指向一个信号集。
  • 返回值: sigsuspend()始终返回-1,并设置 errno 来指示错误(通常为 EINTR) ,表示被信号所中断,如果调用失败,将 errno 设置为 EFAULT。

调用 sigsuspend()函数相当于以不可中断的方式执行以下操作:

sigprocmask(SIG_SETMASK, &mask, &old_mask);
pause();
sigprocmask(SIG_SETMASK, &old_mask, NULL);

通过下面的的程序来理解函数,以下是使用 sigsuspend() 的示例代码:

#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>

// 静态信号处理函数定义
static void sig_handler(int sig) {
    printf("执行信号处理函数...\n");
}

int main(void) {
    struct sigaction sa; // 使用更简洁的变量名
    sigset_t new_mask, old_mask, wait_mask;

    /* 初始化信号集 */
    sigemptyset(&new_mask);
    sigaddset(&new_mask, SIGINT);
    sigemptyset(&wait_mask); // 这里可以不用再次初始化,因为sigsuspend不会修改wait_mask

    /* 注册信号处理函数 */
    sa.sa_handler = sig_handler;
    sa.sa_flags = 0;
    if (sigaction(SIGINT, &sa, NULL) == -1) {
        perror("sigaction");
        exit(EXIT_FAILURE);
    }

    /* 向信号掩码中添加信号 */
    if (sigprocmask(SIG_BLOCK, &new_mask, &old_mask) == -1) {
        perror("sigprocmask");
        exit(EXIT_FAILURE);
    }

    /* 执行保护代码段 */
    puts("执行保护代码段");

    /* 挂起、等待信号唤醒 */
    // sigsuspend的返回值检查可以省略,因为标准行为是永远不返回
    sigsuspend(&wait_mask);

    /* 恢复信号掩码 */
    if (sigprocmask(SIG_SETMASK, &old_mask, NULL) == -1) {
        perror("sigprocmask");
        exit(EXIT_FAILURE);
    }

    // 正常退出程序
    return 0;
}

程序首先声明一个静态的信号处理函数 sig_handler,该函数在接收到信号时被调用,并打印一条消息。在 main 函数中,程序初始化了三个信号集:

  • new_mask 用于定义需要被阻塞的信号,
  • old_mask 用于存储当前的信号掩码,
  • wait_mask 用作 sigsuspend 的参数。

使用 sigaction 函数注册了 sig_handler 函数来处理 SIGINT 信号。然后,程序通过 sigprocmask 函数设置 new_mask 为当前信号掩码,以阻塞 SIGINT 信号。接下来,程序执行一个保护代码段,之后调用 sigsuspend 挂起,直到接收到 SIGINT 信号。一旦信号到达,sig_handler 函数被执行,然后程序从 sigsuspend 返回并恢复原始的信号掩码,最后正常退出。 


 

  • 10
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

几度春风里

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值