Linux Signals – 捕获信号的示例 C 程序

举报
Tiamo_T 发表于 2022/06/07 09:38:48 2022/06/07
【摘要】 在本文中,我们将学习如何在进程中捕获信号。我们将介绍使用 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,并得到了正确的处理。

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。