通过signal()函数在Linux中进行信号处理

时间:2020-03-05 15:29:40  来源:igfitidea点击:

在本系列有关Linux信号的第1部分中,我们讨论了Linux信号的基础。

在本文中,我们将通过signal()函数讨论Linux中信号处理的实际方面。
所有的解释将附有实际的例子。

信号处理器

信号处理程序是一种特殊功能(在软件程序代码中定义并已在内核中注册),当特定信号到达时便会执行。
这导致当前执行过程的中断,并且所有当前寄存器也被保存。
信号处理程序返回后,被中断的过程将恢复。

signal()函数

向内核注册信号处理程序函数的最简单方法是使用signal()函数。

这是signal()函数的语法:

#include <signal.h>
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);

因此,我们可以看到信号处理程序是一个接受整数参数但返回void的函数。
可以使用接受特定信号编号和信号处理程序函数名称的signal()函数(如上所述)将信号处理程序注册到内核(尽管第二个参数可以有其他值,但我们将在后面进行讨论)。

这是一个通过signal()函数在Linux中进行信号处理的工作示例:

#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
void sig_handler(int sig_num)
{
if(sig_num == SIGINT)
{
printf("\n Caught the SIGINT signal\n");
}
else
{
printf("\n Caught the signal number [%d]\n", sig_num);
}
//Do all the necessary clean-up work here
exit(sig_num);
}
int main(void)
{
//Register signal handler through the signal() function
signal(SIGINT, sig_handler);
while(1)
{
//simulate a delay -- as if the program is doing some other stuff
sleep(1);
}
return 0;
}

在上面显示的程序代码中:

  • 在main()函数内部,signal()函数用于注册SIGINT信号的处理程序(sig_handler())。
  • while循环模拟无限延迟。因此,程序无限等待SIGNIT信号。
  • 信号处理程序“ sig_handler”通过检查所需信号是否为SIGINT来打印调试语句。
  • 信号处理程序主要用于在将信号传递到进程后进行所有清理和其他相关工作。

这是该程序的输出:

$./sig_example
^C
Caught the SIGINT signal

因此,我们可以看到程序(sig_example)已执行,然后通过按Ctrl + c组合键将信号SIGINT传递给它。
从输出中的调试打印可以看出,信号处理程序在信号SIGINT传递到进程后就立即执行。

信号处置

为了理解信号处理的概念,让我们重新回顾一下信号函数的声明:

sighandler_t signal(int signum, sighandler_t handler);

signal()函数的第二个参数称为信号的处理。
到目前为止,我们了解到第二个参数是信号处理函数,但这不是唯一的信号处理方法类型。
布置信号的其他方式是将SIG_IGN或者SIG_DFL指定为信号函数的第二个参数。

如果将处置方式设置为SIG_IGN,则忽略信号(作为signal()函数的第一个参数传递),并且不将其传递给进程,但是如果将处置方式设置为SIG_DFL,则将采用与该信号相对应的默认操作。

这是忽略SIGINT信号的代码示例:

#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
int main(void)
{
//Register signal handler through the signal() function
signal(SIGINT, SIG_IGN);
while(1)
{
//simulate a delay -- as if the program is doing some other stuff
sleep(1);
}
return 0;
}

因此,我们可以看到现在没有信号处理程序功能,因为信号功能的第二个参数已替换为SIG_IGN。

这是该程序的输出:

$./sig_example
^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C

因此,我们可以看到,尽管反复按Ctrl + c,该过程并未受到任何影响。
这是因为忽略了信号SIGINT(在按Ctrl + c时生成)。

同样,以下是将SIGINT保留为其默认操作的程序代码:

#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
int main(void)
{
//Register signal handler through the signal() function
signal(SIGINT, SIG_DFL);
while(1)
{
//simulate a delay -- as if the program is doing some other stuff
sleep(1);
}
return 0;
}

因此,我们可以看到在这种情况下,信号函数的第二个参数(在上面显示的代码中)已替换为SIG_DFL。

这是该程序的输出:

$./sig_example
^C
$

因此,我们可以看到按Ctrl + c可以终止该进程,因为SIGINT的默认操作是终止该进程。

signal()函数的局限性

虽然signal()函数是处理信号的最古老,最简单的方法,但它有两个主要限制:

  • 当信号处理程序针对当前信号执行时,其他信号不会被signal()函数阻止。这可能会产生不希望的结果。
  • 信号一经传送,就将特定信号的信号操作重置为其默认值,即SIG_DFL。这意味着,即使信号处理程序再次将信号操作设置为第一步,也可能存在一个时间窗口,在该时间窗口中,当其操作设置为SIG_DFL时,信号可能会再次出现。

由于这些主要限制,现在建议使用克服所有这些限制的函数sigaction()。