Linux Signals – 捕获信号的示例 C 程序
在本文中,我们将学习如何在进程中捕获信号。我们将介绍使用 C 程序代码片段进行信号处理的实际方面。
捕捉信号
正如在上一篇文章中已经讨论过的,如果一个进程希望处理某些信号,那么在代码中,该进程必须向内核注册一个信号处理函数。
下面是一个信号处理函数的原型:
void <signal handler func name> (int sig)
信号处理函数具有 void 返回类型,并接受与需要处理的信号对应的信号编号。
为了让信号处理函数注册到内核,信号处理函数指针作为第二个参数传递给“信号”函数。信号函数的原型是:
void (*signal(int signo, void (*func)(int)))(int);
这似乎是一个复杂的声明。如果我们尝试解码它:
- 该函数需要两个参数。
- 第一个参数是一个整数(signo),表示信号编号或信号值。
- 第二个参数是一个指向信号处理函数的指针,它接受一个整数作为参数并且什么都不返回(void)。
- 而“信号”函数本身返回返回类型为 void 的函数指针。
好吧,为了让事情更容易,让我们使用 typedef :
typedef void sigfunc(int)
所以,我们在这里制作了一个新的类型“sigfunc”。现在使用这个 typedef,如果我们重新设计信号处理程序的原型:
sigfunc *signal(int, sigfunc*);
现在我们更容易理解信号处理函数接受一个整数和一个 sigfunc 类型的函数指针,而它返回一个 sigfunc 类型的函数指针。
捕获信号的示例 C 程序
大多数 Linux 用户使用组合键 Ctr+C 来终止 Linux 中的进程。
你有没有想过这背后的原因。好吧,每当按下 ctrl+c 时,都会向进程发送一个信号 SIGINT。该信号的默认动作是终止进程。但是这个信号也可以处理。以下代码演示了这一点:
#include<stdio.h>
#include<signal.h>
#include<unistd.h>
void sig_handler(int signo)
{
if (signo == SIGINT)
printf("received SIGINT\n");
}
int main(void)
{
if (signal(SIGINT, sig_handler) == SIG_ERR)
printf("\ncan't catch SIGINT\n");
// A long long wait so that we can easily issue a signal to this process
while(1)
sleep(1);
return 0;
}
在上面的代码中,我们使用无限 while 循环模拟了一个长时间运行的进程。
函数 sig_handler 用作信号处理程序。该函数通过将其作为 main() 函数中系统调用“信号”的第二个参数传递给内核来注册。函数“signal”的第一个参数是我们希望信号处理程序处理的信号,在这种情况下是 SIGINT。
附带说明一下,函数 sleep(1) 的使用是有原因的。此函数已在 while 循环中使用,以便 while 循环在一段时间后执行(即在本例中为一秒)。这变得很重要,因为否则无限循环运行可能会消耗大部分 CPU,从而使计算机变得非常非常慢。
无论如何,回来,当进程运行时,我们尝试使用 Ctrl+C 终止进程:
$ ./sigfunc
^Creceived SIGINT
^Creceived SIGINT
^Creceived SIGINT
^Creceived SIGINT
^Creceived SIGINT
^Creceived SIGINT
^Creceived SIGINT
我们在上面的输出中看到,我们多次尝试了组合键 ctrl+c,但每次进程都没有终止。这是因为信号是在代码中处理的,这从我们在每一行得到的打印中得到了证实。
SIGKILL、SIGSTOP 和用户定义的信号
除了处理可用的标准信号(如 INT、TERM 等)。我们还可以拥有可以发送和处理的用户定义信号。以下是处理用户定义信号 USR1 的代码:
#include<stdio.h>
#include<signal.h>
#include<unistd.h>
void sig_handler(int signo)
{
if (signo == SIGUSR1)
printf("received SIGUSR1\n");
else if (signo == SIGKILL)
printf("received SIGKILL\n");
else if (signo == SIGSTOP)
printf("received SIGSTOP\n");
}
int main(void)
{
if (signal(SIGUSR1, sig_handler) == SIG_ERR)
printf("\ncan't catch SIGUSR1\n");
if (signal(SIGKILL, sig_handler) == SIG_ERR)
printf("\ncan't catch SIGKILL\n");
if (signal(SIGSTOP, sig_handler) == SIG_ERR)
printf("\ncan't catch SIGSTOP\n");
// A long long wait so that we can easily issue a signal to this process
while(1)
sleep(1);
return 0;
}
我们看到在上面的代码中,我们试图处理一个用户定义的信号 USR1。另外,我们知道不能处理两个信号 KILL 和 STOP。因此,我们也尝试处理这两个信号,以了解在这种情况下“信号”系统调用如何响应。
当我们运行上面的代码时:
$ ./sigfunc
can't catch SIGKILL
can't catch SIGSTOP
因此,上面的输出清楚地表明,只要系统调用“信号”尝试为 KILL 和 STOP 信号注册处理程序,信号函数就会失败,表明无法捕获这两个信号。
现在我们尝试使用kill 命令将信号 USR1 传递给该进程:
$ kill -USR1 2678
在运行上述程序的终端上,我们看到:
$ ./sigfunc
can't catch SIGKILL
can't catch SIGSTOP
received SIGUSR1
所以我们看到在这个过程中收到了用户定义的信号USR1,并得到了正确的处理。
- 点赞
- 收藏
- 关注作者
评论(0)