Linux — 信号 信号处理和信号处理函数详解(一)

时间:2021-08-03 10:57:33

 信号处理(一)





在Linux下当我们想强制结束一个程序的时候,我们通常会给它发送一个信号然后该进程捕捉到信号,再然后该进程执行一定操作最

被终止.信号是UNIX和Linux系统响应某些条件而产生的一个事件,接收到该信号的进程会相应地采取一些行动。通常信号是由一

个错误产生的。但它们还可以作为进程间通信或修改行为的一种方式,明确地由一个进程发送给另一个进程。一个信号的产生叫生

成,接收到一个信号叫捕获。信号的捕捉这篇可能不会详细的说到,因为我想给它专门讲一个博客,因为信号捕捉的实例也是蛮多的.


Linux — 信号 信号处理和信号处理函数详解(一)


首先我们认识一下这些信号:


 $ kill -l
 1) SIGHUP        2) SIGINT        3) SIGQUIT       4) SIGILL
 5) SIGTRAP       6) SIGABRT       7) SIGBUS        8) SIGFPE
 9) SIGKILL      10) SIGUSR1      11) SIGSEGV      12) SIGUSR2
13) SIGPIPE      14) SIGALRM      15) SIGTERM      17) SIGCHLD
18) SIGCONT      19) SIGSTOP      20) SIGTSTP      21) SIGTTIN
22) SIGTTOU      23) SIGURG       24) SIGXCPU      25) SIGXFSZ
26) SIGVTALRM    27) SIGPROF      28) SIGWINCH     29) SIGIO
30) SIGPWR       31) SIGSYS       34) SIGRTMIN     35) SIGRTMIN+1
36) SIGRTMIN+2   37) SIGRTMIN+3   38) SIGRTMIN+4   39) SIGRTMIN+5
40) SIGRTMIN+6   41) SIGRTMIN+7   42) SIGRTMIN+8   43) SIGRTMIN+9
44) SIGRTMIN+10  45) SIGRTMIN+11  46) SIGRTMIN+12  47) SIGRTMIN+13
48) SIGRTMIN+14  49) SIGRTMIN+15  50) SIGRTMAX-14  51) SIGRTMAX-13
52) SIGRTMAX-12  53) SIGRTMAX-11  54) SIGRTMAX-10  55) SIGRTMAX-9
56) SIGRTMAX-8   57) SIGRTMAX-7   58) SIGRTMAX-6   59) SIGRTMAX-5
60) SIGRTMAX-4   61) SIGRTMAX-3   62) SIGRTMAX-2   63) SIGRTMAX-1
64) SIGRTMAX

列表中,编号为1 ~ 31的信号为传统UNIX支持的信号,是不可靠信号(非实时的),编号为32 ~ 63的信号是后来扩充的,称做可靠

号(实时信号)。不可靠信号和可靠信号的区别在于前者不支持排队,可能会造成信号丢失,而后者不会。我们这些新手就着重

前31个,后面的对于现在来说用处不大.



信号的产生:



产生信号的条件主要有:


1.用户在终端按下某些建时,终端驱动程序会发送信号给前台进程,例如ctrl -c 产生SIGINT信号.ctrl-\产生SIGQUIT信号,


Ctrl-Z产生SIGTSTP信号(可使前台进程停止)


2.硬件异常产生信号,这些条件由硬件检测到并通知内核,然后内核向当前进程发送适当的信号.例如当前进程执行了除以0的


指令,CPU的运算单元会产生异常,内核将这个异常解释为SIGFPE信号发送给进程.在比如当前进程访问了非法内存地址,MMU会


产生异常,内核将这个异常解释为SIGSEGV信号发送给进程.


3,一个进程调用kill(2)函数可以发送信号到另一个进程.可以用kill(1)命令发送信号给某个进程,kill(1)命令也是调用kill


(2)函数实现的,如果不明确指定信号则发送SIGTERM信号,该信号的默认处理是终止进程.当内核检测到某种软件条件发生时也


可以通过信号通知进程. 例如闹钟超时产生SIGALRM信号,向读端已关闭的管道写数据时产生SIGPIPE信号.如果不想按默认动作


处理号,用户程序可以调用sigacttion(2)函数告诉内核处理某种信号.


再然后,你接收到信号的处理动作有一下三种:


1.忽略此信号

2.执行该信号的默认处理动作

3.提供一个信号处理函数,要求内核处理该信号时切换到用户执行这个处理函数,这种方式称为捕捉一个信号.


信号的阻塞


实际执行信号的处理动作称为信号抵达,信号从产生到抵达之间的状态,称为信号未决.进程可以选择阻塞某个信号。 被阻塞的信

号产生时将保持在未决状态,直到进程解除对此信号的阻塞,才执行抵达的动作. 阻塞和忽略是不相同的,只要信号被阻塞就不会

抵达,而忽略是在抵达之后可选的一种处理动作.


Linux — 信号 信号处理和信号处理函数详解(一)


每个信号都有两个标志位分别表示阻塞(block) 和 未决(pendin).还有一个函数指针表示处理动作。

信号产生时,内核在进程控制块中设置该信号的未决标志,直到信号抵达才消除该标志.

1.SIGHUP信号未阻塞也未产生过,当它抵达时执行默认处理动作.

2.SIGINT信号产生过,但正在被阻塞,所以暂时不能递达。虽然他的处理动作是忽略,但在没有解除阻

塞之前不 能忽略这个信号,因为进程仍有机会 改变处理动作之后再解除阻塞.

3.SIGQUIT信号未产生过,一旦产生SIGQUIT信号将被阻塞,它的处理动作是用户自定义函数sighandler。

如果在进程在阻塞某信号时,该信号产生过多次,Liunx这样实现的:常规信号在抵达之前产生多次只计一次,而实时信号在递达之

前产生多可以依次 放在一个队列里. 每个信号只有一个bit的未决标志,非0既1,不记录该信号产生了多少次,阻塞标志也是这样表

示的. 因此呢,未 决和阻塞标志可以用相同的数据类型sigset_t来存储,sigset_t为信号集,这个类型可以表示每个信号的"有效"或

"无效“状态,在阻塞信号集中"有效"和"无效"的含义是该信号是否被阻塞,而在未决信号集中类似. 阻塞信号集也叫做当前进程的

信号屏蔽字. 屏蔽这样理解 "是阻塞 是阻塞 不是忽略"

主要信号的函数:


sigset_t类型对于每种信号用一个bit表示 "有效"或者"无效" 接下来我们来认识一下信号集操作函数
#Include<signal.h>

int sigemptyset(sigset_t *set);
初始化set所指向的信号集,使其中所有信号的对应的bit清零,表示该信号集不包含任何有效信号.

int sigfillset(sigset_t *set);
初始化set所指向的信号集,使其中所有信号的对应bit置位,表示该信号机的有效信号包括系统支持的所有信号.

int sigaddset(sigset_t *set,int signo);
在该信号集中添加某种有效信号.

int sigdelset(sigset_t *set,int signo);
在该信号集中删除某种有效信号

int sigismemeber(const sigset_t *set,int signo);
是一个布尔函数,用于判断一个信号集的有效信号中是否包含某种信号,若包含贼返回1,不包含则返回0,出错返回-1

int sigprocmask(int how,const sigset_t *set,sigset_t *oset);
读取或更改进程的信号屏蔽字(阻塞信号集)如果成功返回0 失败返回-1

int sigpending(sigset_t *set);
读取当前进程的未决信号集,通过set参数传出,调用成功则返回0,出错则返回-1.


例题:


这里我引用了一个很经典的例子,因为这个例子几乎运用到了所有信号集处理函数,可以让我们基础扎实的运用这些函数.

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

void printsigset(sigset_t *set)
{
int i = 0;
for(;i<32;i++){
if(sigismember(set,i))
putchar('1');
else
putchar('0');
}
puts("");
}

int main()
{
sigset_t s,p;
sigemptyset(&s);
sigaddset(&s,SIGINT);
sigprocmask(SIG_BLOCK,&s,NULL);
while(1)
{
sigpending(&p);
printsigset(&p);
sleep(1);
}
return 0;
}

我们加上注释:

Linux — 信号 信号处理和信号处理函数详解(一)


这个程序的大概意思就是 我们阻塞一个信号集,让它一直处于未决状态,并把它里面的信号编号显示出来,比如中途我们加入了


个ctrl+c,后面信号集里面就会出现这个信号,然后他们还是一直处于未决状态.


Linux — 信号 信号处理和信号处理函数详解(一)


特别提醒:如果一个信号被进程阻塞,它就不会传递给进程,但会停留在待处理状态,当进程解除对待

          处理信号的阻塞时,待处理信号就会立刻被处理。