≡菜单

Linux信号–示例C程序来捕获信号(SIGINT,SIGKILL,SIGSTOP等)

In the part 1 of the Linux信号series, we learned about the fundamental concepts behind Linux 信号s.

Building 上 the previous part, in 这个 article we will learn about how to catch 信号s in a process. We will present the practical aspect of 信号 handling using C program code snippets.

捕捉信号

As already discussed in the previous article, If a process wishes to handle certain 信号s then in the code, the process has to register a 信号 handling function to the kernel.

The following is the prototype of a 信号 handling function :

void <signal handler func name> (int sig)

The 信号 handler function has void return type and accepts a 信号 number corresponding to the 信号 that needs to be handled.

To get the 信号 handler function registered to the kernel, the 信号 handler function pointer is passed as second argument to the ‘signal’ function. The prototype of the 信号 function is :

void (*signal(int signo, void (*func )(int)))(int);

这似乎是一个复杂的声明。如果我们尝试对其进行解码:

  • 该函数需要两个参数。
  • The first argument is an 整型eger (signo) depicting the 信号 number or 信号 value.
  • The second argument is a pointer to the 信号 handler function that accepts an 整型eger as argument and returns nothing (void).
  • 而‘signal’函数本身返回函数指针,其返回类型为void。

好吧,为了使事情变得简单,让我们使用typedef:

typedef void 西格芬奇(int)

所以,在这里我们做了一个新的类型‘sigfunc’.  Now using 这个 typedef, if we redesign the prototype of the 信号 handler :

西格芬奇 *signal(int, 西格芬奇*);

Now we see that its easier to comprehend that the 信号 handler function accepts an 整型eger and a 西格芬奇 type function pointer while it returns a 西格芬奇 type function pointer.

示例C程序来捕获信号

大多数Linux用户使用组合键Ctr + C终止Linux中的进程。

Have you ever thought of what goes behind 这个. Well, whenever ctrl+c is pressed, a 信号 SIGINT is sent to the process. The default action of 这个 信号 is to terminate the process. But 这个 信号 can also be handled. The following code demonstrates 这个 :

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

void sig_handler(int signo)
{
  如果(signo == SIGINT)
    打印 ("received SIGINT\n");
}

int main(void)
{
  if (signal(SIGINT, sig_handler) == SIG_ERR)
  打印 ("\ncan't catch SIGINT\n");
  // A long long wait so that we can easily issue a 信号 to 这个 process
  while(1) 
    sleep(1);
  return 0;
}

在上面的代码中,我们使用无限while循环模拟了一个长时间运行的过程。

A function sig_handler is used a s a 信号 handler. This function is registered to the kernel 通过 passing it as the second argument of the system call ‘signal’在main()函数中。该函数的第一个参数‘signal’ is the 信号 we 整型end the 信号 handler to handle which is SIGINT in 这个 case.

附带说明一下,使用函数sleep(1)有一个原因。该函数已在while循环中使用,因此while循环会在一段时间后执行(在这种情况下为一秒钟)。这很重要,因为否则,无限期的while循环会疯狂运行,可能会消耗大量CPU,从而使计算机非常缓慢。

无论如何,返回,当进程运行时,我们尝试使用Ctrl + C终止进程:

$ ./sigfunc
^Creceived SIGINT
^Creceived SIGINT
^Creceived SIGINT
^Creceived SIGINT
^Creceived SIGINT
^Creceived SIGINT
^Creceived SIGINT

我们在上面的输出中看到,我们多次尝试了组合键ctrl + c,但是每次该过程’t terminate. This is because the 信号 was handled in the code and 这个 was confirmed from the print we got 上 each line.

SIGKILL,SIGSTOP和用户定义的信号

Apart from handling the standard 信号s(like INT, TERM etc) that are available. We can also have user defined 信号s that can be sent and handled. Following is the code handling a user defined 信号 USR1 :

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

void sig_handler(int signo)
{
    if (signo == SIGUSR1)
        打印 ("received SIGUSR1\n");
    else if (signo == SIGKILL)
        打印 ("received SIGKILL\n");
    else if (signo == SIGSTOP)
        打印 ("received SIGSTOP\n");
}

int main(void)
{
    if (signal(SIGUSR1, sig_handler) == SIG_ERR)
        打印 ("\ncan't catch SIGUSR1\n");
    if (signal(SIGKILL, sig_handler) == SIG_ERR)
        打印 ("\ncan't catch SIGKILL\n");
    if (signal(SIGSTOP, sig_handler) == SIG_ERR)
        打印 ("\ncan't catch SIGSTOP\n");
    // A long long wait so that we can easily issue a 信号 to 这个 process
    while(1) 
        sleep(1);
    return 0;
}

We see that in the above code, we have tried to handle a user defined 信号 USR1. Also, as we know that two 信号s KILL and STOP cannot be handled. So we have also tried to handle these two 信号s so as to see how the ‘signal’在这种情况下,系统调用会响应。

当我们运行上面的代码时:

$ ./sigfunc

can't catch SIGKILL

can't catch SIGSTOP

所以上面的输出清楚地表明,只要系统调用‘signal’ tries to register handler for KILL and STOP 信号s, the 信号 function fails indicating that these two 信号s cannot be caught.

Now we try to pass the 信号 USR1 to 这个 process using the 杀死命令:

$ kill -USR1 2678

在运行上述程序的终端上,我们看到:

$ ./sigfunc

can't catch SIGKILL

can't catch SIGSTOP
received SIGUSR1

So we see that the user defined 信号 USR1 was received in the process and was handled properly.

如果您喜欢这篇文章,您可能还会喜欢..

  1. 50个Linux Sysadmin教程
  2. 50个最常用的Linux命令(包括示例)
  3. 排名前25位的最佳Linux性能监视和调试工具
  4. 妈妈,我找到了! – 15个实用的Linux Find命令示例
  5. Linux 101 Hacks第二版电子书 Linux 101黑客手册

Bash 101 Hacks书 Sed和Awk 101黑客手册 Nagios Core 3书 Vim 101黑客手册

{ 21 评论… 加一 }

  • 安德森·文图里尼(Anderson Venturini) 2012年3月9日,上午6:34

    谢谢!很棒的帖子!相当讲究和客观。恭喜! --

  • 贾拉尔·哈吉霍拉玛利 2012年3月9日,上午11:15

    你好

    非常感谢,非常好的文章。

    再次感谢..

  • 尼玛 2012年3月10日,上午6:28

    嗨,我编辑了代码以显示按下ctrl + c的次数:

    void sig_handler(int signo){
    static 整型 counter=1;
    如果(signo == SIGINT)
    计数器++;
    打印 (“已收到SIGINT%s次\ n”, counter);
    }

    但是当我按Ctrl + c时,它说:“分段故障(核心已转储)”
    为什么?

  • Himanshu 2012年3月10日,晚上11:40

    @尼玛
    可能是以下几行导致崩溃:
    打印 (“已收到SIGINT%s次\ n”, counter);

    由于计数器是类型‘int’因此您需要在上一行中使用%d而不是%s。
    让我知道这是否解决了您的问题…

  • 尼玛 2012年3月10日,晚上11:56

    谢谢。一世 didn’认为问题是如此简单。

  • Himanshu Arora 2012年3月12日,上午3:31

    @尼玛
    别客气 -

  • 加内什 2012年4月27日,上午12:01

    @尼玛
    您已经声明
    static 整型 counter=1;
    每当您按下Ctrl + c键时,
    计数器值初始化为1,并且它是静态的,因此您应该将static声明为全局变量。’t在sig_handler函数中声明,在main之上声明,希望它将起作用…….

  • 赛义德 2012年8月29日,上午5:48

    @ganesh

    bhai不要给任何误解………只是去阅读abt静态变量,然后使cmnt okey….both are wrking….static n global…。我的意思是您的xplanation很错…..

  • 冠军 2012年9月27日,上午9:49

    非常感谢这篇文章,

    它确实帮助我了解了自己的课程并进行了实际工作。

    来自法国的问候!

  • 基兰 2012年12月16日,上午3:31

    请帮助我在Unix中找到一些程序来区分fork和vfork

  • 排骨 2013年1月9日,上午11:51

    谢谢。一世’您已经从此页面以及您网站上的许多其他页面中学到了很多。非常丰富。

  • 弗拉德 2013年1月15日,上午7:48

    Please consider __not__ writing a 信号 handler like that. This is obsolete and may trigger an undefined behavior. 信号() does not work well in multi-threaded environment and 打印 () function is not reentrant. See 这个 更多细节。

  • Arjun Pakrashi 2013年12月4日,下午12:52

    的行为‘signal’ is different in different platforms. Some may reset the handler to SIG_IGN immediately after it enters the handler, therefore inorder to handle another occurrence of the 信号, you need to call the ‘signal’ function again to set the 信号 disposition. This is the original Unix behaviour. BSD implementation will not set the disposition, but block that 信号 while inside the handler, so the handler will not get called recursively.

    最好是用‘sigaction’ to set 信号s and define the precise behaviour. This is defined 通过 POSIX and the behaviour is defined in POSIX complaint systems.

    另外要注意的是‘printf’ is not an async-signal safe function, therefore it is not safe to call 打印 within a 信号 handler. For demo, it is fine, but worth mentioning.

  • 拉姆韦尔 2014年3月8日,上午4:43

    嗨,请问我能告诉我您在项目中开发RRC-LTE层时遇到的问题吗?UE方面请帮助我,因为我没有在这一层工作,但我正在给我的面试官和面试官询问
    –>您的工作是什么,我以程序员的身份提及您所面临的问题
    –>how u was handling 信号 error in yur code
    –>什么是Scrum,jira

  • 拉杜 2014年3月15日,上午10:17

    根据Linux手册,将sleep()与alarm()混合是一个坏主意,因为它们都共享相同的计时器:对一个调用将干扰其他计时器的使用

    无论如何,我喜欢你的东西!

  • hu 2014年5月29日,上午12:51

    感谢您的帮助文章!
    In the 信号 handler function I didn’t see the code to reinitialize 信号 handler itself, so why could handler function be executed at the second time of Ctrl-C was clicked?
    Deitel的书在其实现中放了一行函数处理程序。

  • 人体模型vs 2014年7月2日,上午3:03

    what is the use of 信号(SIGINT,);

  • 尼克·H 2015年9月17日,上午4:50

    Calling pause() blocks until a 信号 is received. This is even more efficient than while(1) { sleep(1); }

  • schezel2000 2015年12月17日,下午3:27

    I would be careful using 打印 inside a 信号 handler because it is not 信号 save. You should use write instead. I learned 这个 the hard way.

  • 拉胡尔 2016年4月30日,上午10:51

    信号(2,);
    Or 信号(SIGINT,); will generate error of too few arguments in 信号 function.

  • 安德鲁·努 二月6,2019,7:18下午

    嗨Ramesh,
    Thanks for writing 这个 tutorial. I noticed that you are using 打印 () function inside the 信号 handler function (sig_handler) . Please note that you should not use 打印 () in any 信号 handling function,because it is not an async-signal-safe function, and could potentially destabilize the system.
    请看一下: http://man7.org/linux/man-pages/man7/signal-safety.7.html

    谢谢,
    安德罗

发表评论