≡ Menu

Linux Signals – Example C Program to Catch Signals (SIGINT, SIGKILL, SIGSTOP, etc.)

In the part 1 of the Linux Signals series, we learned about the fundamental concepts behind Linux signals.

Building on the previous part, in this article we will learn about how to catch signals in a process. We will present the practical aspect of signal handling using C program code snippets.

Catching a Signal

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

The following is the prototype of a signal handling function :

void <signal handler func name> (int sig)

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

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

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

This might seems a complicated declaration. If we try to decode it :

  • The function requires two arguments.
  • The first argument is an integer (signo) depicting the signal number or signal value.
  • The second argument is a pointer to the signal handler function that accepts an integer as argument and returns nothing (void).
  • While the ‘signal’ function itself returns function pointer whose return type is void.

Well, to make things easier, lets use typedef :

typedef void sigfunc(int)

So, here we have made a new type ‘sigfunc’.  Now using this typedef, if we redesign the prototype of the signal handler :

sigfunc *signal(int, sigfunc*);

Now we see that its easier to comprehend that the signal handler function accepts an integer and a sigfunc type function pointer while it returns a sigfunc type function pointer.

Example C Program to Catch a Signal

Most of the Linux users use the key combination Ctr+C to terminate processes in Linux.

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

#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;
}

In the code above, we have simulated a long running process using an infinite while loop.

A function sig_handler is used a s a signal handler. This function is registered to the kernel by passing it as the second argument of the system call ‘signal’ in the main() function. The first argument to the function ‘signal’ is the signal we intend the signal handler to handle which is SIGINT in this case.

On a side note, the use of function sleep(1) has a reason behind. This function has been used in the while loop so that while loop executes after some time (ie one second in this case). This becomes important because otherwise an infinite while loop running wildly may consume most of the CPU making the computer very very slow.

Anyways, coming back , when the process is run and we try to terminate the process using Ctrl+C:

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

We see in the above output that we tried the key combination ctrl+c several times but each time the process didn’t terminate. This is because the signal was handled in the code and this was confirmed from the print we got on each line.

SIGKILL, SIGSTOP and User Defined Signals

Apart from handling the standard signals(like INT, TERM etc) that are available. We can also have user defined signals that can be sent and handled. Following is the code handling a user defined signal 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;
}

We see that in the above code, we have tried to handle a user defined signal USR1. Also, as we know that two signals KILL and STOP cannot be handled. So we have also tried to handle these two signals so as to see how the ‘signal’ system call responds in this case.

When we run the above code :

$ ./sigfunc

can't catch SIGKILL

can't catch SIGSTOP

So the above output makes clear that as soon as the system call ‘signal’ tries to register handler for KILL and STOP signals, the signal function fails indicating that these two signals cannot be caught.

Now we try to pass the signal USR1 to this process using the kill command:

$ kill -USR1 2678

and on the terminal where the above program is running we see :

$ ./sigfunc

can't catch SIGKILL

can't catch SIGSTOP
received SIGUSR1

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

Add your comment

If you enjoyed this article, you might also like..

  1. 50 Linux Sysadmin Tutorials
  2. 50 Most Frequently Used Linux Commands (With Examples)
  3. Top 25 Best Linux Performance Monitoring and Debugging Tools
  4. Mommy, I found it! – 15 Practical Linux Find Command Examples
  5. Linux 101 Hacks 2nd Edition eBook Linux 101 Hacks Book

Bash 101 Hacks Book Sed and Awk 101 Hacks Book Nagios Core 3 Book Vim 101 Hacks Book

Comments on this entry are closed.

  • Anderson Venturini March 9, 2012, 6:34 am

    Thanks! Great post! Pretty didactic and objective. Congrats! 😉

  • Jalal Hajigholamali March 9, 2012, 11:15 am

    Hi,

    Thanks a lot, very nice article..

    Thanks again..

  • Nima March 10, 2012, 6:28 am

    Hi, I edited the code to show how many times ctrl+c is pressed:

    void sig_handler (int signo) {
    static int counter=1;
    if (signo == SIGINT)
    counter++;
    printf(“received SIGINT %s times\n”, counter);
    }

    But when I press Ctrl+c it says: “Segmentation fault (core dumped)”
    Why?

  • Himanshu March 10, 2012, 11:40 pm

    @Nima
    Probably the following line is causing the crash :
    printf(“received SIGINT %s times\n”, counter);

    Since counter is of type ‘int’ so you need to use %d instead of %s in the line above.
    Let me know if this solves your problem…

  • Nima March 10, 2012, 11:56 pm

    Thanks. I didn’t think the eproblem is so simple.

  • Himanshu Arora March 12, 2012, 3:31 am

    @Nima
    You are welcome 🙂

  • Ganesh April 27, 2012, 12:01 am

    @Nima
    You have declared
    static int counter=1;
    whenever your are pressing the Ctrl+c key, each time the
    the counter values is initialized to 1 , and as it is static, So you should declare static as global variable.,that is don’t declare in the sig_handler function, declare above the main, hopefully it will work…….

  • mohd saif August 29, 2012, 5:48 am

    @ganesh

    bhai dont give any misconception okey………just go and read abt static variable then make cmnt okey….both are wrking….static n global….i mean ur xplanation is vry vry wrong…..

  • champidead September 27, 2012, 9:49 am

    Thanks a lot for this article,

    it really helped me to understand my course and to do my practical work.

    Regards from france!

  • kiran December 16, 2012, 3:31 am

    please help me to find some programs in unix to differentiate fork and vfork

  • Chomp January 9, 2013, 11:51 am

    Thanks. I’ve learned a great deal from this and many other pages on your site. Very informative.

  • Vlad January 15, 2013, 7:48 am

    Please consider __not__ writing a signal handler like that. This is obsolete and may trigger an undefined behavior. signal() does not work well in multi-threaded environment and printf() function is not reentrant. See this for more details.

  • Arjun Pakrashi December 4, 2013, 12:52 pm

    The behaviour of ‘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 signal, you need to call the ‘signal’ function again to set the signal disposition. This is the original Unix behaviour. BSD implementation will not set the disposition, but block that signal while inside the handler, so the handler will not get called recursively.

    Best is to use ‘sigaction’ to set signals and define the precise behaviour. This is defined by POSIX and the behaviour is defined in POSIX complaint systems.

    Also another thing to note is ‘printf’ is not an async-signal safe function, therefore it is not safe to call printf within a signal handler. For demo, it is fine, but worth mentioning.

  • ram veer March 8, 2014, 4:43 am

    Hi plz can any one tell me what problem you r facing while developing RRC-LTE Layer in your project UE side plz help me out bcz i did not work in this layer but i m giving my interview and interviewer asking
    –>what was yur work and i mention as programmer so what prblms u was facing
    –>how u was handling signal error in yur code
    –>and what is scrum ,jira

  • Radu March 15, 2014, 10:17 am

    Mixing sleep() with alarm() is a bad idea, according to the Linux manual because they both share the same timer: calls to one will interfere with use of other

    Anyway, I like your stuff !

  • huy May 29, 2014, 12:51 am

    Thanks for your very helpful article!
    In the signal handler function I didn’t see the code to reinitialize signal handler itself, so why could handler function be executed at the second time of Ctrl-C was clicked?
    Deitel book puts a line of function handler inside implementation of it.

  • manikandan vs July 2, 2014, 3:03 am

    what is the use of signal(SIGINT,);

  • Nick H. September 17, 2015, 4:50 am

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

  • schezel2000 December 17, 2015, 3:27 pm

    I would be careful using printf inside a signal handler because it is not signal save. You should use write instead. I learned this the hard way.

  • Rahul April 30, 2016, 10:51 am

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

  • Andro Nooh February 6, 2019, 7:18 pm

    Hi Ramesh,
    Thanks for writing this tutorial. I noticed that you are using printf() function inside the signal handler function (sig_handler) . Please note that you should not use printf() in any signal handling function,because it is not an async-signal-safe function, and could potentially destabilize the system.
    Please take a look at: http://man7.org/linux/man-pages/man7/signal-safety.7.html

    Thanks,
    Andro