Unix/Linux Signal Handling

Discussion in 'C' started by poornaMoksha, Oct 4, 2011.

  1. poornaMoksha

    poornaMoksha New Member

    Joined:
    Jan 29, 2011
    Messages:
    150
    Likes Received:
    33
    Trophy Points:
    0
    Occupation:
    Software developer
    Location:
    India
    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 :

    • 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.
    • 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.
    • 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.
    • 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).
    • 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
    • SIG_IGN : If we want to ignore the signal
    • SIG_DFL: If we want default action to be applied
    • 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 likes this.
  2. lionaneesh

    lionaneesh Active Member

    Joined:
    Mar 21, 2010
    Messages:
    848
    Likes Received:
    224
    Trophy Points:
    43
    Occupation:
    Student
    Location:
    India
    A Pretty Simple and Easy to understand tutorial! Thanks!
     

Share This Page

  1. This site uses cookies to help personalise content, tailor your experience and to keep you logged in if you register.
    By continuing to use this site, you are consenting to our use of cookies.
    Dismiss Notice