Go4Expert

Go4Expert (http://www.go4expert.com/)
-   C (http://www.go4expert.com/articles/c-tutorials/)
-   -   Unix/Linux Signal Handling (http://www.go4expert.com/articles/unix-linux-signal-handling-t26840/)

poornaMoksha 4Oct2011 08:36

Unix/Linux Signal Handling
 
Signals are the interrupts. They are a way of providing asynchronous events. For example a user typing ctrl-c on terminal to stop a program. Most of the programs need to handle signals. Every signal has a name like SIGxxx. For example : SIGALRM is the alarm signal that is generated when the timer set by the alarm function goes off.

On Linux, different signals are defined in <bits/signum.h>.

The are various scenarios in which is signal is generated :
  1. When a user press a terminal key (like ctrl-c). This type of signal are known as terminal generated signals and normally cause the interrupt signal SIGINT to be generated. SO now we know how/why our programs stop when we press ctrl-c.
  2. Whenever hardware exceptions occur. Like whenever divide by zero, illegal memory access etc occur. These type of exceptions are usually detected by the hardware and then the hardware notifies the kernel. Its the kernel that notifies the process which generated this exception through a signal known as :SIGSERV.
  3. From inside a process, kill() function allows you to pass any signal to other process provided that you are the owner of that process or you are the superuser.
  4. Then there is kill command through which we can kill the processes in our system. Its just an interface to the kill() function so the conditions for killing a process remain the same(as described in point above).
  5. Scenarios like in which some software condition occurs like alarm set by the process goes off, or when a process tries to write to a pipe after the reader has been terminated etc.

Going a bit deeper



Whenever a signal is generated, the process has to tell the kernel what to do with this signal. So in all three kind of options are available for a process :
  • Catch a Signal: In order to make this happen, we tell the kernel to call a function of ours when a signal is generated. In that particular function, we can do our stuff that we want to do when a signal is generated. For example, If the SIGCHLD signal is caught, it means that a child process has terminated, so the signal-catching function can call waitpid to fetch the child's process ID and termination status. Similarly a SIGTERM signal which is sent when we issue kill command or kill function. Please note here that the two signals SIGKILL and SIGSTOP cannot be caught.
  • Ignore a signal: Well, this action works for most of the signals except the two signals SIGKILL and SIGSTOP. The reason is that these two signals provide kernel or superuser a sure-shot way of stopping any process.Please note here that if you ignore signals generated through an exception such that 'divide by zero' etc then the behavior of the process is undefined.
  • Let the default action apply: Every signal has a default action. For most of the signals, default action is to terminate the process.
Following is a list of signals and their description for your reference :

Code:

SIGABRT    abnormal termination (abort)   
SIGALRM    timer expired (alarm )         
SIGBUS    hardware fault                 
SIGCANCEL  threads library internal use   
SIGCHLD    change in status of child     
SIGCONT    continue stopped process       
SIGEMT    hardware fault                 
SIGFPE    arithmetic exception           
SIGFREEZE  checkpoint freeze             
SIGHUP    hangup                         
SIGILL    illegal instruction           
SIGINFO    status request from keyboard   
SIGINT    terminal interrupt character   
SIGIO      asynchronous I/O               
SIGIOT    hardware fault                 
SIGKILL    termination                   
SIGLWP    threads library internal use   
SIGPIPE    write to pipe with no readers 
SIGPOLL    pollable event (poll)         
SIGPROF    profiling time alarm (setitimer)
SIGPWR    power fail/restart           
SIGQUIT    terminal quit character       
SIGSEGV    invalid memory reference     
SIGSTKFLT  co-processor stack fault       
SIGSTOP    stop                               
SIGSYS    invalid system call           
SIGTERM    termination                   
SIGTHAW    checkpoint thaw               
SIGTRAP    hardware fault                 
SIGTSTP    terminal stop character       
SIGTTIN    background read from control tty
SIGTTOU    background write to control tty
SIGURG    urgent condition (sockets)               
SIGUSR1    user-defined signal                 
SIGUSR2    user-defined signal                 
SIGVTALRM  virtual time alarm (setitimer)     
SIGWAITING threads library internal use       
SIGWINCH  terminal window size change         
SIGXCPU    CPU limit exceeded (setrlimit)     
SIGXFSZ    file size limit exceeded (setrlimit)
SIGXRES    resource control exceeded

Example



The function used for implementing signals is :

Code:

#include <signal.h>
void (*signal(int signo, void (*func )(int)))(int);

As it could be a bit difficult to decode this prototype, so lets start with its explanation :

The prototype states that, the function requires two arguments. The first argument is signo(an integer). The second argument is a function pointer that takes single integer and returns nothing. While the function itself returns function pointer whose return type is void.

I understand that the prototype could be still cryptic, lets try to ease out a level more by creating the following typedef :

Code:

typedef void Sigfunc(int);
Now, the prototype of the above function becomes :

Code:

Sigfunc *signal(int, Sigfunc *);
Now, I think its far more easy to understand.

The signo argument is just the name of the signal. The value of func is
  1. SIG_IGN : If we want to ignore the signal
  2. SIG_DFL: If we want default action to be applied
  3. Function pointer of a function to be called when the signal occurs.
When we specify the address of a function to be called when the signal occurs, we mean to "catch" the signal.

Lets consider the following code :

Code:

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

void sig_handler(int signo)
{
  if (signo == SIGUSR1)
      printf("received SIGUSR1\n");
  else if (signo == SIGUSR2)
      printf("received SIGUSR2\n");
  else
      printf("ERR : received signal %d\n", signo);
}

int main(void)
{
    if (signal(SIGUSR1, sig_handler) == SIG_ERR)
        printf("\ncan't catch SIGUSR1\n");
    if (signal(SIGUSR2, sig_handler) == SIG_ERR)
        printf("\ncan't catch SIGUSR2\n");
    while(1)  // Simulate a dummy wait
        sleep(1);

    return 0;
}

In the above program, we have created a sig_handler function which we have registered with kernel through signal function call for the signals SIGUSR1 and SIGUSR2. So the idea is that whenever a signal USR1 or USR2 from kill command is sent to this process, the function sig_handler should be called and we should be able to handle the signal cleanly. In this case we just print to stdout when a signal is received.

Lets run it :

Code:

./signal
Now, in some other terminal, find the PID of signal process and kill it using signal USR1.

Code:

$ ps -aef | grep signal
himanshu  5018  4659  0 08:27 pts/1    00:00:00 ./signal
himanshu  5043  4918  0 08:28 pts/2    00:00:00 grep --colour=auto signal

$ kill -USR1 5018

Now come to the same terminal where signal process was running and you will see that the process is not killed as it has handled the signal:
Code:

$ ./signal
received SIGUSR1

Conclusion



To Conclude, signals are un-avoidable part of system programming. Almost all kind of tools/utilities etc take care of signal handling and process them accordingly.

lionaneesh 5Oct2011 09:13

Re: Unix/Linux Signal Handling
 
A Pretty Simple and Easy to understand tutorial! Thanks!


All times are GMT +5.5. The time now is 11:56.