Signal Handling In Linux Through The signal() Function

December 5, 2013 | By
| Reply More

In the part-1 of this series on Linux signals, we discussed the fundamentals of signals in Linux. In this article, we will discuss the practical aspects of signal handling in Linux through the signal() function. All the explanations will be accompanied by practical examples.

c signal function

Signal Handlers

A signal handler is special function (defined in the software program code and registered with the kernel) that gets executed when a particular signal arrives. This causes the interruption of current executing process and all the current registers are also saved. The interrupted process resumes once the signal handler returns.

The signal() Function

The simplest way to register signal handler function with the kernel is by using the signal() function.

Here is the syntax of signal() function :

#include <signal.h>

typedef void (*sighandler_t)(int);

sighandler_t signal(int signum, sighandler_t handler);

So you can see that a signal handler is a function that accepts an integer argument but returns void. The signal handler can be registered with kernel using the signal() function (described above) that accepts a particular signal number and signal handler function name (though there can be other values for the second argument but we will discuss them later).

Here is a working example of signal handling in Linux through the signal() function :

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

In the program code shown above :

  • Inside the main() function, the signal() function is used to register a handler (sig_handler()) for SIGINT signal.
  • The while loop simulates an infinite delay. So, the program waits for SIGNIT signal infinitely.
  • The signal handler 'sig_handler' prints a debug statement by checking if the desired signal is SIGINT or not.
  • The signal handler is mostly used to do all the clean-up and other related stuff after a signal is delivered to a process.

Here is the output of this program :

$ ./sig_example
^C
Caught the SIGINT signal

So you can see that the program (sig_example) was executed and then the signal SIGINT is passed to it by pressing the Ctrl+c key combination. As can be seen from the debug print in the output, the signal handler was executed as soon as the signal SIGINT was delivered to the process.

Disposition Of A Signal

To understand the concept of disposition of a signal, lets revisit the declaration of signal function :

sighandler_t signal(int signum, sighandler_t handler);

The second argument to the signal() function is known as the disposition of a signal. As of now, we learned that the second argument is a signal handling function but this is not the only type of signal disposition method. Other ways of disposition of a a signal are to specify SIG_IGN or SIG_DFL as the second argument to the signal function.

If the disposition is set to SIG_IGN then the signal (passed as the first argument to signal() function) is ignored and not delivered to the process but if the disposition is set to SIG_DFL then the default action corresponding to that signal is taken.

Here is an example of a code that ignores the SIGINT signal :

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

So you can see that there is no signal handler function now as the second argument to the signal function is replaced with SIG_IGN.

Here is the output of this program :

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

So you can see that despite of repeatedly pressing Ctrl+c, the process was not affected in any way. This is because the signal SIGINT (generated when Ctrl+c is pressed) is ignored.

Similarly, here is a program code that leaves SIGINT to its default action :

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

So you can see that the second argument to the signal function (in the code shown above) is replaced with SIG_DFL in this case.

And here is the output of this program :

$ ./sig_example
^C
$

So you can see that pressing Ctrl+c terminated the process because the default action of SIGINT is to terminate the process.

The signal() Function Limitations

Though the signal() function is the oldest and the easiest way to handle signals, it has a couple of major limitations :

  • Other signals are not blocked by the signal() function while the signal handler is executing for current signal. This may produce undesired results.
  • The signal action for a particular signal is reset to its default value i.e., SIG_DFL as soon as the signal is delivered. This means that even if the signal handler again sets the signal action as the first step, there is a possible time window in which the signal may occur again while its action is set to SIG_DFL.

Due to these major limitations, it is now advised to use the function sigaction() that overcomes all these limitations.

Filed Under : HOWTOS, PROGRAMMING

Free Linux Ebook to Download

Leave a Reply

Commenting Policy:
Promotion of your products ? Comment gets deleted.
All comments are subject to moderation.