[C] signal信号
我们应该遇到过这样的几个场景
- 程序在运行过程中需要终止
ctrl+c
,但是程序正在读写数据,为了防止程序在buffer中没有写入磁盘,需要在终止之前flush一次文件buffer - 设置一个定时任务,n秒之后进行某些操作
- …(后续补充)
实际上这些都是信号处理,程序运行中按下键盘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 <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <stdlib.h>
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 <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <stdlib.h>
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简单易用