信号集与信号阻塞集

论坛 期权论坛 编程之家     
选择匿名的用户   2021-6-2 20:57   2395   0

信号集

有时候一个进程需要对多个信号进行处理,如果一个一个信号去判断,那会很蛋疼。我们可以用信号集来很好地解决这个蛋疼的问题。顾名思义,信号集是一个信号集合。数据类型为 sigset_t . 对信号集的操作主要有一下几个函数:

#include <signal.h>  
int sigemptyset(sigset_t *set);  
int sigfillset(sigset_t *set);  
int sigismember(const sigset_t *set, int signum);  
int sigaddset(sigset_t *set, int signum);  
int sigdelset(sigset_t *set, int signum);

看函数名也就能猜到各自的功能。让我来试试。

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

int main()
{
    /* 定义一个信号集 */
    sigset_t SigSet;
    /* 清空这个信号集 */
    sigemptyset(&SigSet);
    /* 判断 SIGINT 是否在信号集中,在返回1,不在返回0 */
    if(sigismember(&SigSet, SIGINT))
    {
        printf("SIGINT 在该信号集中\n");
    }
    else
    {
        printf("SIGINT 不在该信号集中\n");
    }
    /* 添加信号 SIGINT 到信号集中 */
    sigaddset(&SigSet,SIGINT);
    if(sigismember(&SigSet, SIGINT))
    {
        printf("SIGINT 在该信号集中\n");
    }
    else
    {
        printf("SIGINT 不在该信号集中\n");
    }
    /* 判断 SIGQUIT 是否在信号集中,在返回1,不在返回0 */
    if(sigismember(&SigSet, SIGQUIT))
    {
        printf("SIGQUIT 在该信号集中\n");
    }
    else
    {
        printf("SIGQUIT 不在该信号集中\n");
    }
    /* 添加信号 SIGQUIT 到信号集中 */
    sigaddset(&SigSet,SIGQUIT);
    if(sigismember(&SigSet, SIGQUIT))
    {
        printf("SIGQUIT 在该信号集中\n");
    }
    else
    {
        printf("SIGQUIT 不在该信号集中\n");
    }
    /* 从 SigSet 信号集中删除 SIGINT 和 SIGQUIT 信号 */
    sigdelset(&SigSet,SIGINT);
    sigdelset(&SigSet,SIGQUIT);
    if((sigismember(&SigSet,SIGINT)) || (sigismember(&SigSet,SIGQUIT)))
    {
        printf("SIGINT 或 SIGQUIT 在该信号集中\n");
    }
    else
    {
        printf("SIGINT 和 SIGQUIT 都不在该信号集中\n");
    }
    return 0;
}

对照程序和执行结果,可以很好地理解这几个函数的功能和用法。

[lingyun@manjaro study]$ gcc study.cpp -o study
[lingyun@manjaro study]$ ./study 
SIGINT 不在该信号集中
SIGINT 在该信号集中
SIGQUIT 不在该信号集中
SIGQUIT 在该信号集中
SIGINT 和 SIGQUIT 都不在该信号集中

信号阻塞集

信号阻塞集也称信号屏蔽集、信号掩码。每个进程都有一个阻塞集,创建子进程时子进程将继承父进程的阻塞集。信号阻塞集用来描述哪些信号递送到该进程的时候被阻塞(在信号发生时记住它,直到进程准备好时再将信号通知进程)。所谓阻塞并不是禁止传送信号, 而是暂缓信号的传送。若将被阻塞的信号从信号阻塞集中删除,且对应的信号在被阻塞时发生了,进程将会收到相应的信号。我们可以通过 sigprocmask() 修改当前的信号掩码来改变信号的阻塞情况。

#include <signal.h>

/* 检查或修改信号阻塞集,根据 how 指定的方法对进程的阻塞集合进行修改,新的信号阻塞集由 set 指定,而原先的信号阻塞集合由 oldset 保存。*/
int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);

/* 参数:
how: 信号阻塞集合的修改方法,有 3 种情况:
    SIG_BLOCK:向信号阻塞集合中添加 set 信号集,新的信号掩码是set和旧信号掩码的并集。
    SIG_UNBLOCK:从信号阻塞集合中删除 set 信号集,从当前信号掩码中去除 set 中的信号。
    SIG_SETMASK:将信号阻塞集合设为 set 信号集,相当于原来信号阻塞集的内容清空,然后按照 set 中的信号重新设置信号阻塞集。

set: 要操作的信号集地址。若 set 为 NULL,则不改变信号阻塞集合,函数只把当前信号阻塞集合保存到 oldset 中。

oldset: 保存原先信号阻塞集地址。

/* 返回值:
    0:成功
    -1:失败,失败时错误代码只可能是 EINVAL,表示参数 how 不合法。
*/

Note: 不能阻塞 SIGKILL 和 SIGSTOP 等信号,但是当 set 参数包含这些信号时 sigprocmask() 不返回错误,只是忽略它们。另外,阻塞 SIGFPE 这样的信号可能导致不可挽回的结果,因为这些信号是由程序错误产生的,忽略它们只能导致程序无法执行而被终止。

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

/* 回调信号处理函数 */
void SignalHandle(int signum)
{
    if(signum == SIGINT)
    {
        printf("接收到 SIGINT 信号\n");
    }
    else if(signum == SIGQUIT)
    {
        printf("接收到 SIGQUIT 信号\n");
    }
}
 
int main(int argc, char *argv[])
{
 sigset_t set;
 int i = 0;
    /* 注册信号 */
    signal(SIGINT, SignalHandle);
    signal(SIGQUIT, SignalHandle);
 /* 清空信号集合 */
    sigemptyset(&set); 
    /* SIGINT 加入 set 集合 */ 
    sigaddset(&set, SIGINT); 

    /* set 集合加入阻塞集,在没有移除前,SIGINT 会被阻塞 */
    sigprocmask(SIG_BLOCK, &set, NULL);
    for(i=0; i<5; i++)
    {
        printf("SIGINT signal is blocked\n");
        sleep(1);
    }
    
    /* 移除 SIGINT, 加入 SIGQUIT 信号到 set */
    sigdelset(&set,SIGINT);
    sigaddset(&set,SIGQUIT);
    /* 将新的 set 加入阻塞集,此时 SIGINT 和 SIGQUIT 都会被阻塞 */
    sigprocmask(SIG_BLOCK,&set,NULL);
    for(i=0; i<5; i++)
    {
        printf("SIGINT and SIGQUIT signal unblocked\n");
        sleep(1);
    }
    
    /* 从阻塞集移除 set(只有 SIGQUIT),如果 SIGQUIT 信号在被阻塞时发生了,此刻,SIGQUIT 信号立马生效 */
    sigprocmask(SIG_UNBLOCK, &set, NULL);
    printf("SIGQUIT signal unblocked\n");

    /* 使 set 中只有信号 SIGINT */
    sigdelset(&set,SIGQUIT);
    sigaddset(&set,SIGINT);
    /* 从阻塞集移除 set(只有 SIGINT),如果 SIGINT 信号在被阻塞时发生了,此刻,SIGINT 信号立马生效 */
    sigprocmask(SIG_UNBLOCK, &set, NULL);
    printf("SIGINT signal unblocked\n");
 
 return 0;
}

从执行结果可以很容易理解阻塞集的功能及用法。令我不解的是我发送了两次信号 SIGINT,但是进程只收到一次,猜测这个方法不支持同样的信号多次排队。

[lingyun@manjaro study]$ gcc study.cpp -o study
[lingyun@manjaro study]$ ./study 
SIGINT signal is blocked
SIGINT signal is blocked
^CSIGINT signal is blocked
SIGINT signal is blocked
SIGINT signal is blocked
SIGINT and SIGQUIT signal unblocked
SIGINT and SIGQUIT signal unblocked
^\SIGINT and SIGQUIT signal unblocked
^CSIGINT and SIGQUIT signal unblocked
SIGINT and SIGQUIT signal unblocked
接收到 SIGQUIT 信号
SIGQUIT signal unblocked
接收到 SIGINT 信号
SIGINT signal unblocked
[lingyun@manjaro study]$

分享到 :
0 人收藏
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

积分:3875789
帖子:775174
精华:0
期权论坛 期权论坛
发布
内容

下载期权论坛手机APP