Linux IPC 信号 kill,signal,sigaction,sigprocmask,sigpending信号处理机制

什么是信号

信号是进程在运行过程中,由自身产生或由进程外部发过来的消息(事件)。信号是硬件中断的软件模拟(软中断)。每个信号用一个整型常量宏表示,以 SIG 开头,比如 SIGCHLD、SIGINT 等,它们在系统头文件 <signal.h>中定义,也可以通过在 shell 下键入 kill –l 查看信号列表, 或者键入 man 7 signal 查看更详细的说明。

        信号         值      动作   说明
       ─────────────────────────────────────────────────────────────────────
       SIGHUP        1       A     在控制终端上是挂起信号, 或者控制进程结束
       SIGINT        2       A     从键盘输入的中断
       SIGQUIT       3       C     从键盘输入的退出
       SIGILL        4       C     无效硬件指令
       SIGABRT       6       C     非正常终止, 可能来自 abort(3)
       SIGFPE        8       C     浮点运算例外
       SIGKILL       9      AEF    杀死进程信号
       SIGSEGV      11       C     无效的内存引用
       SIGPIPE      13       A     管道中止: 写入无人读取的管道
       SIGALRM      14       A     来自 alarm(2) 的超时信号
       SIGTERM      15       A     终止信号
       SIGUSR1   30,10,16    A     用户定义的信号 1
       SIGUSR2   31,12,17    A     用户定义的信号 2
       SIGCHLD   20,17,18    B     子进程结束或停止
       SIGCONT   19,18,25          继续停止的进程
       SIGSTOP   17,19,23   DEF    停止进程
       SIGTSTP   18,20,24    D     终端上发出的停止信号
       SIGTTIN   21,21,26    D     后台进程试图从控制终端(tty)输入
       SIGTTOU   22,22,27    D     后台进程试图在控制终端(tty)输出

信号的生成来自内核,让内核生成信号的请求来自 3 个地方
用户:用户能够通过输入 CTRL+c、Ctrl+\,或者是终端驱动程序分配给信号控制字符的其他任
何键来请求内核产生信号。
内核:当进程执行出错时,内核会给进程发送一个信号,例如非法段存取(内存访问违规)、浮
点数溢出等。
进程:一个进程可以通过系统调用 kill 给另一个进程发送信号,一个进程可以通过信号和另外
一个进程进行通信。

进程接收到信号以后,可以有如下 3 种选择进行处理:
接收默认处理:接收默认处理的进程通常会导致进程本身消亡。例如连接到终端的进程,用户
按下 CTRL+c,将导致内核向进程发送一个 SIGINT 的信号,进程如果不对该信号做特殊的处
理,系统将采用默认的方式处理该信号,即终止进程的执行; signal(SIGINT,SIG_DFL)。
忽略信号:进程可以通过代码,显示地忽略某个信号的处理,例如:signal(SIGINT,SIG_IGN);
但是某些信号是不能被忽略的。
捕捉信号并处理:进程可以事先注册信号处理函数,当接收到信号时,由信号处理函数自动捕
捉并且处理信号。
有两个信号既不能被忽略也不能被捕捉,它们是 SIGKILL 和 SIGSTOP。即进程接收到这两个信号后,只能接受系统的默认处理,即终止进程。

The signals SIGKILL and SIGSTOP cannot be caught or ignored.

发送信号

Linux下,一个进程给其他进程发送信号的API是kill函数。其定义如下:

#include <sys/types.h>
#include <signal.h>
int kill(pid_t pid, int sig);

该函数把信号sig发送给目标进程;目标进程由pid参数指定,其可能的取值和含义如下:

pid > 0  信号发送给PID为pid的进程
pid = 0  信号发送给本进程组内的其他进程
pid = -1  信号发送给除init进程之外的其他所有进程,但发送者需要拥有对目标进程发送信号的权限
pid < -1  信号发送给组ID为-pid的进程组中的所有成员

该函数成功时返回0,失败时返回-1并设置errno,几种可能的errno如下所示:

EINVAL  无效的信号
EPERM   该进程没有权限发送信号给任何一个目标进程
ESRCH   目标进程或者进程组不存在

信号处理方式

目标进程在收到信号时,需要定义一个接收函数来处理。信号处理函数的原型如下:

#include <signal.h>
typedef void (*_sighandler_t)(int);

信号处理函数只带一个整型参数,该参数用来指示信号类型,信号处理函数应该是可重入的,否则就很容易引发一些竞态条件。所以在信号函数中严禁调用一些不安全的函数。
除用户自定义信号处理函数之外,bits/signum.h头文件中还定义了信号的两种其他处理方式——SIG_IGN和SIG_DEL:

#include <bits/signum.h>
#define SIG_DFL ((_sighandler_t) 0)
#define SIG_IGN ((_sighandler_t) 1)

SIG_IGN表示忽略目标信号,SIG_DFL表示使用信号的默认处理方式

中断系统调用

如果程序在执行处于阻塞状态的系统调用时接收到信号,并且我们为该信号设置了信号处理函数,则默认情况下系统调用将被中断,并且errno被设置为EINTR。我们可以使用sigaction函数为信号设置SA_RESTART标志以自动启动被该信号中断的系统调用。

对于默认行为是暂停进程的信号(比如SIGSTOP、SIGTTIN),如果我们没有为它们设置信号处理函数,则它们也可以中断某些系统调用(比如:connect、epoll_wait)。POSIX没有规定这种行为,这是Linux独有的。

signal 信号处理机制

可以用函数 signal 注册一个信号捕捉函数。原型为:

#include <signal.h>
typedef void (*_sighandler_t)(int);  //函数指针
_sighandler_t signal(int signum, _sighandler_t _handler);

signal 的第 1 个参数 signum 表示要捕捉的信号,第 2 个参数是个函数指针,表示要对该信号进行捕捉的函数, 该参数也可以是 SIG_DFL(表示交由系统缺省处理, 相当于白注册了)或 SIG_IGN(表示忽略掉该信号而不做任何处理)。

signal() sets the disposition of the signal signum to handler, which is either SIG_IGN,SIG_DFL, or the address of a programmer-defined function (a “signal handler”).
If the signal signum is delivered to the process, then one of the following happens:
If the disposition is set to SIG_IGN, then the signal is ignored.
If the disposition is set to SIG_DFL, then the default action associated with the signal(see signal(7)) occurs.
If the disposition is set to a function, then first either the disposition is reset to SIG_DFL, or the signal is blocked , and then handler is called with argument signum. If invocation of the handler caused the signal to be blocked, then the signal is unblocked upon return from the handler.

signal 如果调用成功, 返回以前该信号的处理函数的地址, 否则返回 SIG_ERR

signal() returns the previous value of the signal handler, or SIG_ERR on error. In the event
of an error, errno is set to indicate the cause.

sighandler_t 是信号捕捉函数,由 signal 函数注册,注册以后,在整个进程运行过程中均有效,并且对不同的信号可以注册同一个信号捕捉函数。该函数只有一个整型参数,表示信号值。

signal.c

#include <func.h>
void sigFunc(int signum)
{
    printf("%d is coming\n",signum);
}
int main(int argc, char* argv[])
{
    if(signal(SIGINT,sigFunc)==SIG_ERR)
    {
        perror("signal");
        return -1;
    }
    while(1);
    return 0;
}

^C2 is coming
^C2 is coming
^C2 is coming
^C2 is coming
^\退出 (核心已转储)

signal_sleep_recover.c

#include <func.h>
int main(int argc, char* argv[])
{
    if(signal(SIGINT,SIG_IGN)==SIG_ERR)
    {
        perror("signal");
        return -1;
    }
    sleep(10);
    signal(SIGINT,SIG_DFL);   //将该信号改成默认的
    while(1);
    return 0;
}

^C ^C ^C ^C ^C ^C
>sleep(10) 
^C
terminate

signal_read_block.c

#include <func.h>
void sigFunc(int signum)
{
    printf("%d is coming\n",signum);
}
int main(int argc, char* argv[])
{
    signal(SIGINT,sigFunc);
    char buf[128]={0};
    int ret=read(STDIN_FILENO,buf,sizeof(buf));
    printf("ret=%d,buf=%s\n",ret,buf);
    return 0;
}
^C2 is coming
^C2 is coming
^C2 is coming
^C2 is coming
^C2 is coming
^C2 is coming
hello
ret=6,buf=hello

signal_two_kind.c

#include <func.h>
void sigFunc(int signum)
{
    printf("before sleep %d is coming\n",signum);
    sleep(3);
    printf("after sleep %d is coming\n",signum);
}
int main(int argc, char* argv[])
{
    if(signal(SIGINT,sigFunc)==SIG_ERR)
    {
        perror("signal");
        return -1;
    }
    if(signal(SIGQUIT,sigFunc)==SIG_ERR)
    {
        perror("signal");
        return -1;
    }
    while(1);
    return 0;
}

对运行结果的分析:当发送2号信号的时候,这时2号信号在自己的sigFunc函数处理流程中,2号信号无法自身打断自己。
而当3号信号到来时,3号信号会打断2号信号的处理流程,去执行3号信号的sigFunc处理流程,这里相当与函数的递归调用。
在3号信号的处理流程中sleep(3)后,会输出after sleep 3 is coming这句话。接下来因为在3号信号的处理程序之中继续执行时,又从终端发送了3号信号,将内核中标志位从0变为了1,当处理程序结束时,内核发现3号信号的该标志位为1,所有会再次执行一次信号处理函数,打印before sleep 3 is coming和after sleep 3 is coming。之后再返回2号信号的处理流程中打印after sleep 2 is coming。同理,最后2号信号的处理流程也会多执行一次。

^Cbefore sleep 2 is coming
^C^C^C^C^C^C^\before sleep 3 is coming
^\^\^\^\^\after sleep 3 is coming
before sleep 3 is coming
after sleep 3 is coming
after sleep 2 is coming
before sleep 2 is coming
after sleep 2 is coming
^Z
[1]+  已停止               ./a.out

问题:当不同信号到来时,会打断信号处理流程嘛,假设现在的信号发送是2 3 2,最后一次发送的2号信号会打断3号信号的处理流程嘛?
不会,因为第二次发送3号信号打断了2号信号,但是还在2号信号的处理流程之中的。这时再发送2号信号,因为本身就处于2号信号的处理流程,所以不能打断。

sigaction 信号处理机制

函数原型:

#include <signal.h>
int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);

sigaction 也用于注册一个信号处理函数
参数 signum 为需要捕捉的信号
参数 act 是一个结构体,里面包含信号处理函数地址、处理方式等信息
参数 oldact 是一个传出参数,sigaction 函数调用成功后,oldact 里面包含以前对 signum 的处理方式的信息,通常为 NULL。
如果函数调用成功,将返回 0,否则返回-1
结构体 struct sigaction(注意名称与函数 sigaction 相同)的原型为

struct sigaction {
	void (*sa_handler)(int); //老类型的信号处理函数指针
	void (*sa_sigaction)(int, siginfo_t *, void *);//新类型的信号处理函数指针
	sigset_t sa_mask; //将要被阻塞的信号集合
	int sa_flags; //信号处理方式掩码
	void (*sa_restorer)(void); //保留,不要使用
};
typedef struct {
	unsigned long sig[_NSIG_WORDS]} sigset_t

该结构体的各字段含义及使用方式:

1、字段 sa_handler 是一个函数指针,用于指向原型为 void handler(int)的信号处理函数地址,
即老类型 的信号处理函数(如果用这个再将 sa_flags = 0,就等同于 signal()函数)

2、字段 sa_sigaction 也是一个函数指针,用于指向原型为:
void handler(int iSignNum, siginfo_t *pSignInfo, void *pReserved);
的信号处理函数,即新类型的信号处理函数
该函数的三个参数含义为:
iSignNum:传入的信号
pSignInfo:与该信号相关的一些信息,它是个结构体
pReserved:保留,通常为 NULL

siginfo_t {
               int      si_signo;     /* Signal number */
               int      si_errno;     /* An errno value */
               int      si_code;      /* Signal code */
               int      si_trapno;    /* Trap number that caused
                                         hardware-generated signal
                                         (unused on most architectures) */
               pid_t    si_pid;       /* Sending process ID */
               uid_t    si_uid;       /* Real user ID of sending process */
               int      si_status;    /* Exit value or signal */
               clock_t  si_utime;     /* User time consumed */
               clock_t  si_stime;     /* System time consumed */
               sigval_t si_value;     /* Signal value */
               int      si_int;       /* POSIX.1b signal */
               void    *si_ptr;       /* POSIX.1b signal */
               int      si_overrun;   /* Timer overrun count;
                                         POSIX.1b timers */
               int      si_timerid;   /* Timer ID; POSIX.1b timers */
               void    *si_addr;      /* Memory location which caused fault */
               long     si_band;      /* Band event (was int in
                                         glibc 2.3.2 and earlier) */
               int      si_fd;        /* File descriptor */
               short    si_addr_lsb;  /* Least significant bit of address
                                         (since Linux 2.6.32) */
               void    *si_lower;     /* Lower bound when address violation
                                         occurred (since Linux 3.19) */
               void    *si_upper;     /* Upper bound when address violation
                                         occurred (since Linux 3.19) */
               int      si_pkey;      /* Protection key on PTE that caused
                                         fault (since Linux 4.6) */
               void    *si_call_addr; /* Address of system call instruction
                                         (since Linux 3.5) */
               int      si_syscall;   /* Number of attempted system call
                                         (since Linux 3.5) */
               unsigned int si_arch;  /* Architecture of attempted system call
                                         (since Linux 3.5) */
           }

3、字段 sa_handler 和 sa_sigaction 只应该有一个生效,如果想采用老的信号处理机制,就应该让
sa_handler 指向正确的信号处理函数,并且让字段 sa_flags 为 0;否则应该让 sa_sigaction 指向正确的信号处理函数,并且让字段 sa_flags 包含 SA_SIGINFO 选项

4、字段 sa_mask 是一个包含信号集合的结构体, 该结构体内的信号表示在进行信号处理时,将要
被阻塞的信号。针对 sigset_t 结构体,有一组专门的函数对它进行处理,它们是:

sa_mask specifies a mask of signals which should be blocked (i.e., added to the signal mask of the thread in which the signal handler is invoked) during execution of the signal han dler. In addition, the signal which triggered the handler will be blocked, unless the SA_NODEFER flag is used.

#include <signal.h>
int sigemptyset(sigset_t *set);     //清空信号集合 set
int sigfillset(sigset_t *set);      //将所有信号填充进 set 中
int sigaddset(sigset_t *set, int signum);  //往set中添加信号 signum
int sigdelset(sigset_t *set, int signum);  //从set中移除信号 signum
int sigismember(const sigset_t *set, int signum);  //判断 signum 是否包含在 set 中(是:返回 1,否:0)
int sigpending(sigset_t  *set);    //被阻塞的信号集合由参数set指针返回(挂起信号)

其中,对于函数 sigismember 而言,如果 signum 在 set 集中,则返回 1。不在,则返回 0,出错时返回-1,其他的函数都是成功返回 0,失败返回-1。

5、 字段 sa_flags 是一组掩码的合成值,指示信号处理时所应该采取的一些行为,各掩码的含义为:

sa_flags specifies a set of flags which modify the behavior of the signal. It is formed by
the bitwise OR of zero or more of the following:

SA_RESETHAND
处理完毕要捕捉的信号后,将自动撤消信号处理函数的注册,即必须再重新注册信号处理函数,才能继续处理接下来产生的信号。

Restore the signal action to the default upon entry to the signal handler. This flag is meaningful only when establishing a signal handler

SA_NODEFER
在处理信号时,如果又发生了其它的信号,则立即进入其它信号的处理,等其它信号处理完毕后,再继续处理当前的信号,即递归地处理。

Do not prevent the signal from being received from within its own signal handler. This flag is meaningful only when establishing a signal handler.

SA_RESTART
如果在发生信号时,程序正阻塞在某个系统调用,例如调用read()函数,则在处理完毕信号后,接着从阻塞的系统调用返回。如果不指定该参数,中断处理完毕之后,read 函数读取失败。

Provide behavior compatible with BSD signal semantics by making certain system calls restartable across signals. This flag is meaningful only when establishing a signal handler.

SA_SIGINFO
指示结构体的信号处理函数指针是哪个有效,如果 sa_flags 包含该掩码 , 则 sa_sigaction 指针有效,否则是 sa_handler 指针有效。

The signal handler takes three arguments, not one. In this case, sa_sigaction should be set instead of sa_handler. This flag is meaningful only when establishing a signal handler.

sigaction.c

#include <func.h>
void sigFunc(int signum, siginfo_t *p,void *p1)
{
    printf("%d is coming\n",signum);
}
int main(int argc, char* argv[])
{
    struct sigaction act;        //定义sigaction结构体
    bzero(&act,sizeof(act));     //清空该结构体,置0
    act.sa_flags=SA_SIGINFO;     //信号处理方式掩码
    act.sa_sigaction=sigFunc;    //新类型的信号处理函数
    int ret=sigaction(SIGINT,&act,NULL);  //SIGINT 2号信号
    ERROR_CHECK(ret,-1,"sigaction");
    while(1);
    return 0;
}

如果信号处理函数正在处理信号,并且还没有处理完毕时,又发生了一个同类型的信号,这时
对于signal: 会挨着执行,后续相同信号忽略(会多执行一次)。
对于sigaction: 当信号处理函数正在处理信号,并且还没有处理完毕时,若收到一个同类型的信号,sigaction是会打断当下的处理程序。

^C2 is coming
^C2 is coming
^C2 is coming
^C2 is coming
^C2 is coming
^C2 is coming
^C2 is coming
^\退出 (核心已转储)

sigaction_mask.c

#include <func.h>
void sigFunc(int signum, siginfo_t *p,void *p1)
{
    printf("before sleep %d is coming\n",signum);
    sleep(3);
    printf("after sleep %d is coming\n",signum);
}
int main(int argc, char* argv[])
{
    struct sigaction act;
    bzero(&act,sizeof(act));
    act.sa_flags=SA_SIGINFO;
    act.sa_sigaction=sigFunc;
    sigemptyset(&act.sa_mask);    //清空信号集合sigset_t
    sigaddset(&act.sa_mask,SIGQUIT);   //向信号集合中添加3号信号
    int ret=sigaction(SIGINT,&act,NULL);
    ERROR_CHECK(ret,-1,"sigaction");
    ret=sigaction(SIGQUIT,&act,NULL);
    ERROR_CHECK(ret,-1,"sigaction");
    while(1);
    return 0;
}

如果信号处理函数正在处理信号,并且还没有处理完毕时,又发生了一个不同类型的信号。sigaction可以实现不跳转到另一个信号。比如,先发送2号信号,再发送3号信号,这时sigaction会将2号信号指向完毕再去执行3号信号,不会被3号信号打断。而对于signal,则会直接跳转去执行另一个信号,之后再返回执行剩下的没有处理完的信号。

^Cbefore sleep 2 is coming
^C^C^Cafter sleep 2 is coming
before sleep 2 is coming
after sleep 2 is coming
^Cbefore sleep 2 is coming
^\^\^\after sleep 2 is coming
before sleep 3 is coming
after sleep 3 is coming
^Z
[1]+  已停止               ./sigaction_mask

sigaction_sa_nodefer.c

#include <func.h>
void sigFunc(int signum, siginfo_t *p,void *p1)
{
    printf("before sleep %d is coming\n",signum);
    sleep(3);
    printf("after sleep %d is coming\n",signum);
}
int main(int argc, char* argv[])
{
    struct sigaction act;
    bzero(&act,sizeof(act));
    act.sa_flags=SA_SIGINFO|SA_NODEFER;    //新增了参数SA_NODEFER
    act.sa_sigaction=sigFunc;
    int ret=sigaction(SIGINT,&act,NULL);
    ERROR_CHECK(ret,-1,"sigaction");
    ret=sigaction(SIGQUIT,&act,NULL);
    ERROR_CHECK(ret,-1,"sigaction");
    while(1);
    return 0;
}

SA_NODEFER:在处理信号时,如果又发生了其它的信号,则立即进入其它信号的处理,等其它信号处理完毕后,再继续处理当前的信号,即递归地处理。

^Cbefore sleep 2 is coming
^Cbefore sleep 2 is coming
^Cbefore sleep 2 is coming
^\before sleep 3 is coming
^Cbefore sleep 2 is coming
^\before sleep 3 is coming
^\before sleep 3 is coming
after sleep 3 is coming
after sleep 3 is coming
after sleep 2 is coming
after sleep 3 is coming
after sleep 2 is coming
after sleep 2 is coming
after sleep 2 is coming

sigaction_pending.c

sigpending() returns the set of signals that are pending for delivery to the calling thread(i.e., the signals which have been raised while blocked). The mask of pending signals is returned in set.

#include <func.h>
void sigFunc(int signum, siginfo_t *p,void *p1)
{
    printf("before sleep %d is coming\n",signum);
    sleep(3);
    sigset_t pending;
    sigpending(&pending);  //从进程控制块PCB中拿出进程尚未处理的信号
    if(sigismember(&pending,SIGQUIT))  //判断3号信号是否被阻塞
    {
        printf("SIGQUIT is pending\n");
    }
    else
    {
        printf("SIGQUIT is not pending\n");
    }
    printf("after sleep %d is coming\n",signum);
}
int main(int argc, char* argv[])
{
    struct sigaction act;
    bzero(&act,sizeof(act));
    act.sa_flags=SA_SIGINFO;
    act.sa_sigaction=sigFunc;
    sigemptyset(&act.sa_mask);
    sigaddset(&act.sa_mask,SIGQUIT);
    int ret=sigaction(SIGINT,&act,NULL);
    ERROR_CHECK(ret,-1,"sigaction");
    ret=sigaction(SIGQUIT,&act,NULL);
    ERROR_CHECK(ret,-1,"sigaction");
    while(1);
    return 0;
}
^Cbefore sleep 2 is coming
^C^CSIGQUIT is not pending
after sleep 2 is coming
before sleep 2 is coming
^\^\SIGQUIT is pending
after sleep 2 is coming
before sleep 3 is coming
SIGQUIT is not pending
after sleep 3 is coming

sigaction_sa_resethand.c

#include <func.h>
void sigFunc(int signum, siginfo_t *p,void *p1)
{
    printf("%d is coming\n",signum);
}
int main(int argc, char* argv[])
{
    struct sigaction act;
    bzero(&act,sizeof(act));
    act.sa_flags=SA_SIGINFO|SA_RESETHAND;   //添加了SA_RESETHAND
    act.sa_sigaction=sigFunc;
    int ret=sigaction(SIGINT,&act,NULL);
    ERROR_CHECK(ret,-1,"sigaction");
    while(1);
    return 0;
}

对于sigaction可以实现第一次信号生效,但当下次发送时就失效这种效果。

^C2 is coming
^C
terminate

sigaction_sa_restart.c

#include <func.h>
void sigFunc(int signum, siginfo_t *p,void *p1)
{
    printf("%d is coming\n",signum);
}
int main(int argc, char* argv[])
{
    struct sigaction act;
    bzero(&act,sizeof(act));
    act.sa_flags=SA_SIGINFO|SA_RESTART;
    act.sa_sigaction=sigFunc;
    int ret=sigaction(SIGINT,&act,NULL);
    ERROR_CHECK(ret,-1,"sigaction");
    char buf[128]={0};
    ret=read(STDIN_FILENO,buf,sizeof(buf));     //read()阻塞
    printf("ret=%d,buf=%s\n",ret,buf);
    return 0;
}

SA_RESTART: 如果在发生信号时,程序正阻塞在某个系统调用,例如调用read()函数,则在处理完毕信号后,接着从阻塞的系统返回。如果不指定该参数,中断处理完毕之后,read 函数读取失败。

^C2 is coming
^C2 is coming
^C2 is coming
^C2 is coming
hello
ret=6,buf=hello

若不加SA_RESTART的结果:

^C2 is coming
ret=-1,buf=

sigaction_siginfo.c

#include <func.h>
void sigFunc(int signum, siginfo_t *p,void *p1)
{
    printf("%d is coming,send pid=%d,send uid=%d\n",signum,p->si_pid,p->si_uid);
}
int main(int argc, char* argv[])
{
    struct sigaction act;
    bzero(&act,sizeof(act));
    act.sa_flags=SA_SIGINFO;
    act.sa_sigaction=sigFunc;
    int ret=sigaction(SIGINT,&act,NULL);
    ERROR_CHECK(ret,-1,"sigaction");
    while(1);
    return 0;
}

sigprocmask信号阻塞机制

函数 sigaction 中设置的被阻塞信号集合只是针对于要处理的信号,例如:

struct sigaction act;
sigemptyset(&act.sa_mask);
sigaddset(&act.sa_mask,SIGQUIT);
sigaction(SIGINT,&act,NULL);

表示只有在处理信号 SIGINT 时,才阻塞信号 SIGQUIT;
函数 sigprocmask 是全程阻塞,在 sigprocmask 中设置了阻塞集合后,被阻塞的信号将不能再被信号处理函数捕捉,直到重新设置阻塞信号集合。

sigprocmask() is used to fetch and/or change the signal mask of the calling thread. The signal mask is the set of signals whose delivery is currently blocked for the caller

函数原型:

#include <signal.h>
int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);

参数 how的值为如下 3 者之一:
SIG_BLOCK ,将参数 2 的信号集合添加到进程原有的阻塞信号集合中
SIG_UNBLOCK ,从进程原有的阻塞信号集合移除参数 2 中包含的信号
SIG_SETMASK,重新设置进程的阻塞信号集为参数 2 的信号集
参数 set 为阻塞信号集
参数 oldset 是传出参数,存放进程原有的信号集,通常为 NULL

If oldset is non-NULL, the previous value of the signal mask is stored in oldset.
If set is NULL, then the signal mask is unchanged

signal_ignore_def.c

#include <func.h>
int main(int argc, char* argv[])
{
    signal(SIGINT,SIG_IGN);
    slepp(10);   //关键代码
    signal(SIGINT,SIG_DFL);   //回归成默认
    return 0;
}

sigprocmask.c

#include <func.h>
int main(int argc, char* argv[])
{
    sigset_t procMask;      //定义阻塞集合
    sigemptyset(&procMask);    //清空集合
    sigaddset(&procMask,SIGINT);   //向集合中添加SIGINT信号
    int ret;
    ret=sigprocmask(SIG_BLOCK,&procMask,NULL);  //全程阻塞该信号,不能被捕捉
    ERROR_CHECK(ret,-1,"sigprocmask");
    sleep(10);
    ret=sigprocmask(SIG_UNBLOCK,&procMask,NULL);  //重新设置信号阻塞集合,取出SIGINT
    ERROR_CHECK(ret,-1,"sigprocmask");
    return 0;
}

sigprocmask_pending.c

#include <func.h>

int main(int argc, char* argv[])
{
    sigset_t procMask;
    sigemptyset(&procMask);
    sigaddset(&procMask,SIGINT);
    int ret;
    ret=sigprocmask(SIG_BLOCK,&procMask,NULL);
    ERROR_CHECK(ret,-1,"sigprocmask");
    sleep(3);
    sigset_t pending;     //定义信号集合pending
    sigemptyset(&pending);
    sigpending(&pending);   //被阻塞的信号集合由参数pending返回
    if(sigismember(&pending,SIGINT))  //判断阻塞的信号集合中是否存在SIGINT
    {
        printf("SIGINT is pending\n");
    }
    else
    {
        printf("SIGINT is not pending\n");
    }
    ret=sigprocmask(SIG_UNBLOCK,&procMask,NULL);  //解除设定的阻塞集合proMask
    ERROR_CHECK(ret,-1,"sigprocmask");
    return 0;
}

sigprocmask_pending_ignore.c

若是在全局阻塞该信号之前,将该信号忽略了,这时会显示pending?
1、考虑阻塞的优先级高,还是忽略的优先级高,若是先对该信号忽略,在内核中将标志符从1改为0,则不会显示pending。
2、若是阻塞的优先级更高,则会显示出pending,

#include <func.h>

int main(int argc, char* argv[])
{
    signal(SIGINT,SIG_IGN);     //忽略SIGINT
    sigset_t procMask;
    sigemptyset(&procMask);
    sigaddset(&procMask,SIGINT);
    int ret;
    ret=sigprocmask(SIG_BLOCK,&procMask,NULL);  //阻塞SIGINT
    ERROR_CHECK(ret,-1,"sigprocmask");
    sleep(3);
    sigset_t pending;
    sigemptyset(&pending);
    sigpending(&pending);
    if(sigismember(&pending,SIGINT))
    {
        printf("SIGINT is pending\n");
    }
    else
    {
        printf("SIGINT is not pending\n");
    }
    ret=sigprocmask(SIG_UNBLOCK,&procMask,NULL);
    ERROR_CHECK(ret,-1,"sigprocmask");
    return 0;
}

当解除阻塞以后,会立刻进行忽略,所以返回值为0。若解除阻塞以后,没有设置忽略,返回值则是130。内核中阻塞的优先级高于忽略的优先级。

^CSIGINT is pending
terminate
echo $? 返回值为0,不是130
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章