COMP421
Unix Environment for Programmers
Lecture 17: Signals____________________________________________

Jeff Wiegley, Ph.D.
Computer Science
jeffw@csun.edu

10/17/2005

     

‘‘Ships that pass in the night, and speak each other in passing, only a signal shown, and a distant voice in the darkness; So on the ocean of life, we pass and speak one another, only a look and a voice, then darkness again and a silence.’’

– Henry Wadsworth Longfellow

1


Signals:________________________________________________

“Signals” present a simple mechanism for processes to communicate with each other.

They are software interrupts.

They function like interrupt vectors (which will be familiar to those with some background in electrical engineering or micro-controllers.)

2


Signal names._________________________________________

Every signal has a name (which maps to a number). Common names are SIGHUP, SIGKILL, SIGTERM and SIGUSR1

There is no difference between signals. They can all do the same thing.1

The variety allows a program to tailer its behavior depending on which signal it received.

3


What causes signals to be sent:____________________

Signals are caused by a variety of events:

4


What can we do with signals:______________________

If we change the handler then, when a matching signal is received, the program will immediately interrupt what is doing and run the signal handler function instead.

When the signal handler returns the programs picks up where it left off when it was interrupted.

5


Changing the handler:_______________________________

The simplest method for changing the handler is:

       #include <signal.h>  
 
       typedef void (*sighandler_t)(int);  
 
       sighandler_t signal(int signum, sighandler_t handler);

Which is basically a simple function that takes two arguments:

  1. The number of the signal for which you want to change behavior.
  2. A function pointer to the function that you want run when the signal is received. SIG IGN and SIG DFL can also be passed to ignore the signal or choose default handling.

6


An example:__________________________________________

    #include <signal.h>  
    #include <stdio.h>  
 
    void myhandler(int signo)  
    {  
      fprintf(stderr,"Damn it! I refuse to quit!!");  
    }  
 
    void updateconf(int signo)  
    {  
      fprintf(stderr,"reloading configuration file");  
    }  
 
    int main(int argc, char *argv[])  
    {  
      signal(SIGINT, myhandler);  
      signal(SIGHUP, updateconf);  
      while (1); /* infinite loop */  
    }

7


Exec and signals:_____________________________________

When exec() is called to change a process. (such as after fork()) exec does the following:

8


Problems with earlier systems:_____________________

In early systems, signals where “unreliable”.

This was due to the fact the signal handler was reset to SIG IGN each time the signal was received.

Classic solution was to re-enable the signal handler as the first or last step in the signal handling function itself.

This meant that there was a small window of time after a first occurrence of a signal was received when a second instance of the signal could occur before the signal handler was reset and the default handler would execute. For signals such as SIGTERM this meant the process died when it shouldn’t have. (The default action for SIGTERM is to kill the process.)

9


Another problem:____________________________________

There are times when you want to ignore a signal (during a critical calculation, for instance)

But you still want to know that the signal occurred so that you can process it after the critical computation is completed.

Early systems only allowed you to suspend signals by simply ignoring them. This has no memory that signals ever occurred and therefore aren’t suitable to solve this problem.

10


Signals can interrupt System calls:________________

Remember that read() is a blocking system call?3

Q: Well guess what happens if the process receives a signal while read() is blocked?

A: read() unblocks! (and more specifically returns -1 indicating that an error occurred.)

So, as programmers we have to be very careful when using system calls in conjunction with signals that we very carefully check the return status of the system call.

In this case, read() returned -1 AND set errno to EINTR. So before assuming that read() failed because of some fatal error we have to check errno first.

11


WARNING!!!!:________________________________________

Signal handler should not make use of any NON-reentrant functions.

This includes malloc() and free(), all system calls that utilize these and all other non-reentrant system calls.

Figure 10.4 of the book lists the known reentrant system calls that are safe to use in signal handlers.

12


Some signal functions:_______________________________

There is a lot of code based on earlier, unreliable, signals.

POSIX provides a suite of functions that implement reliable signals.

13


Signal sets:____________________________________________

POSIX provides a suite of functions that implement reliable signals.

First we need to know about signal sets:

14


Reliable Signals:______________________________________

15


Blocking signals:_____________________________________

int sigprocmask(int how, const sigset_t *set, sigset_t *oldset)

how is one of SIG_BLOCK, SIG_UNBLOCK or SIG_SETMASK.

if oldset is not NULL then the previous signal mask is stored in oldset.

if set is not NULL then the set of signals specified is either blocked or unblocked. (or if how==SIG_SETMASK then the set of block signals is set equal to set.

Unblocking a signal causes any pending signals to be delivered. However, multiple generations of a signal while blocked do not queue up. When unblocked only a single signal is delivered.4

16


Checking for pending signals:______________________

int sigpending(sigset_t *set);

The set returned consists of the signals that have been raised while blocked.

17


Changing signal handlers:___________________________

int sigaction(int signum,
              const struct sigaction *act,
              struct sigaction *oldact)

      struct sigaction {  
                  void (*sa_handler)(int);  
                  void (*sa_sigaction)(int, siginfo_t *, void *);  
                  sigset_t sa_mask;  
                  int sa_flags;  
                  void (*sa_restorer)(void);  
              }

sa_mask provides a set of signals which are blocked during execution of the sa_handler.

sa_flags can be used to modify behavior of the signal handler. SA_RESETHAND, for instance, will cause the signal’s handler to be the default action after the handler runs (=early implementations).

18


Conclusion.___________________________________________

In conclusion, the reliable signal routines should be used for all new applications.

19