signal信号

[C] signal信号

我们应该遇到过这样的几个场景

  1. 程序在运行过程中需要终止ctrl+c,但是程序正在读写数据,为了防止程序在buffer中没有写入磁盘,需要在终止之前flush一次文件buffer
  2. 设置一个定时任务,n秒之后进行某些操作
  3. …(后续补充)
    实际上这些都是信号处理,程序运行中按下键盘ctrl+c实际上是给程序发了一个SIGINT信号,默认情况下程序会直接退出,但是我们可以定制这些信号处理。

信号处理的头文件是signal.h,里面有一个特定的数据结构sigaction

struct  sigaction {
    union __sigaction_u __sigaction_u;  /* signal handler 信号处理函数*/
    sigset_t sa_mask;               /* signal mask to apply 信号处理过程中需要屏蔽掉的其他信号*/
    int     sa_flags;               /* see signal options below 选项*/
};

另外还有一个sigaction函数,参数为

intsigaction(int, const struct sigaction * __restrict,
    struct sigaction * __restrict);

下面看下具体用法

#include 
#include 
#include 
#include 

void INIProcess(int nsig)
{
    printf("signal:%d\n", nsig);
    int i=0;
    while(i<20){
        // do sth
        printf("%d/n",i);
        sleep(1);
        i++;
    }
}

int main()
{
    struct sigaction act;
    act.sa_handler = INIProcess;
    act.sa_flags = SA_NODEFER|SA_RESETHAND;
    sigaction(SIGINT,&act,NULL);
    while(1)sleep(100);
    return 0;
}

Sigaction方法的意思是捕捉SIGNIT(ctrl+c)信号,并且交给act指定的参数去进行信号处理,act指定了sa_handler为INIProcess去处理信号,同时有两个sa_flags参数,分别是如下解释
SA_NODEFER: 信号处理函数过程中不阻塞对于信号处理函数自身信号功能
SA_RESETHAND:用户注册的信号处理函数执行一次后被重置为系统默认的信号处理
所以上面这个例子,在按下ctrl+c以后,就已经把SIGINI的控制权交给了系统,再次按下以后就会退出

(base) ➜  C++ ./signal
^Csignal:2
^C

如果将flag改成SA_NODEFER的话,每次SIGINI处理都会进入注册函数,再也无法退出(除非在main函数内退出)

^C0/n1/n2/n3/n4/n5/n6/nsignal:2
^C0/nsignal:2
^C0/n1/n2/n3/nsignal:2
^C0/n1/n2/n3/nsignal:2
^C0/nsignal:2
^C0/nsignal:2
^C0/nsignal:2
^C0/nsignal:2
^C0/nsignal:2
^C0/nsignal:2
^C0/nsignal:2
^C0/nsignal:2
^C0/n1/n2/nsignal:2
^C0/nsignal:2
^C0/nsignal:2
^C0/nsignal:2
^C0/n1/n2/n3/n4/n5/n6/nsignal:2
^C0/nsignal:2
^C0/nsignal:2
^C0/n1/n2/n3/n4/n5/n6/n7/n8/n9/n10/n11/n12/n13/n14/n15/n16/n17/n18/n19/n1/n2/n3/n4/n5/n6/n7/n8/n9/n10/n11/n12/n13/n14/n15/n16/n17/n18/n19/n1/n2/n3/n4/n5/n6/n7/n8/n9/n10/n11/n12/n13/n14/n15/n16/n17/n18/n19/n7/n8/n9/n10/n11/n12/n13/n14/n15/n16/n17/nsignal:2

如果将flag设置成0,那么每次都会处理SIGINI信号都会被阻塞,下面是一段解释

情况下,我们去掉了SA_NODEFER标志位。程序在执行信号处理函数过程中,ctrl+c信号将会被阻止,但是在执行信号处理函数期发送的ctrl+c信号将会被阻塞,知道信号处理函数执行完成,才有机会处理信号函数执行期间产生的ctrl+c,但是在信号函数执行产生的多次ctrl+c,最后只会产生ctrl+c。2)情况下,由于设置了SA_NODEFER,ctrl+c信号将不会被阻塞。所以能够并行执行下次的信号处理函数。

同时处理SIGINI和SIGALRM

有时候不光要处理ctrl+c,还要处理一些定时操作。但是一旦收到某些信号,比如SIGQUIT就要开始做一些收尾的工作,但是与此同时有其他一些定时信号可能会干扰到这个信号的处理,就需要在注册的时候屏蔽掉定时信号(或者其他信号)

#include 
#include 
#include 
#include 

void INIProcess(int nsig)
{
    printf("signal:%d\n", nsig);
    int i=0;
    while(i<10){
        sleep(1);
        i++;
    }
    exit(0);
}

int main()
{
    struct sigaction act;
    act.sa_handler  = INIProcess;
    
    act.sa_flags = SA_NODEFER; // 只进行一次ini,否则全部重来
    sigaddset(&act.sa_mask,SIGALRM); // 处理ini信号的时候屏蔽掉alarm信号
    sigaction(SIGINT,&act,NULL);
    alarm(5);
    while(1)sleep(5);
    return 0;
}

所以上面这个例子,如果没有SIGINI信号,程序5s就会停止,因为收到了SIGALRM,但是如果收到了SIGINI,程序的结束时间就是10s了,因为SIGALRM已经被屏蔽掉了。

另外有一个signal函数,比sigaction简单易用

你可能感兴趣的:(signal信号)