CS111 Fall 10

Scribe notes for 10/12/2010, Lecture 6

by Shijia Liu, Jiajun Zhang and Xiaotian Chen

Signals

Let's take a look at the case that program operating on a computer with unreliable power. When the cable is pulled out, the power does not die immediately. Actually, it takes several milliseconds before power are completely gone. What we need is a way to notify the process so that it can save critical states before exitting. There are several ways to accomplish this:

1) Obviously we can use a trap or interrupt, etc.

2) We can have a bit in the file "/dev/power" so that the process constantly reads it: If the file read "1", the computer has power. However, if it reads zero, the process knows that the power is about to gone. However, the biggest disadvantage of this method is that it uses a polling approach so that applications need to constantly "read", which chews up CPU.

3) We can use an improved version of method #2. When the process reads from "/dev/power", if we have power, it waits. If the power is about to go out, the reading succeeds and continues. The disadvantage is that the it's hard for the process to do useful work when the processing is waiting.

4) We can also use two processes. Process A does the real work and process B performs the tasks specified in method #3. Hence, another question arises: How does B notify A when the power state changes?

The above discussion brings up a task that we have to handle: asynchronous notification of events. To achieves this, we need signals to notify the process when something changes.

Signal Sampler

The following table lists some of the signals. All signals are defined in signal.h.

Events

Signals

A child died SIGCHLD
Hangup SIGHUP
Timer expired SIGALRM
Terminal stop SIGTSTP
Program stop SIGSTOP
Terminal interrupt SIGINT
Get this when continue SIGCONT
Bad address(segmentation fault) SIGSEGV
Bus error SIGBUS
Force the process to exit SIGKILL
Power failure SIGPWR

How to utilize signals?

Let's go back to our power failure example. This is how the power failure signal is implemented:

    int main (void) {
            signal (SIGPWR,power_has_failed);
    }

    
    void power_has_failed (int signum){
            printf("Out of power\n");
            unlink (tmpfile);
            _exit(1);
    }

The signal() call in the main function establishes a handler for SIGPWR signal. The first argument specifies what signal we are interested, and the second argument is a signal handler which points to a function. When we receive a signal, the signal handler specifies what the process should do.

What can go wrong with signals?

Although signals are very useful, some stubborn proplems may still appear. For example, there probably exists nested signals. This means that the signal arrives while a signal handler is running. To avoid such situation, we should know how to block signals. To block signal is to tell the operating system to hold and deliver the signal later. One method to block a signal is to use sigprocmask. Its structure is as follows:

      
     int sigprocmask(int how, 
                     sigset_t cont* restrict set,
                     sigset_t *restrict oset);

The "how" parameter is one of the following three values: SIG_BLOCK, SIG_SETMASK and SIG_UNBLOCK. The "set" and the "oset" parameters hold new and original masks and cannot be overlaping.

Safe use of signal hanler

Let's take a look at the following code:

    char* filename;
    int main(){
         signal(SIGINT,inthandler);
         signal(SIGHUP,inthandler);
         sigprocmask(SIG_BLOCK);
         malloc(4096);
    }

    void inthandler(int signum){
         char* msg = malloc(strlen(filename)+100);
         sprintf(msg,"%s: removed\n",filename);
         write(STDERR,msg,strlen(msg));
         unlink(filename);
         _exit(1);
    }

The problem is the malloc() function. Since signals are asynchronous function and can be raised at any time, if the signal arrives while the malloc() function is allocating memory on heap, unexpected things may occur. This is because malloc usually maintains a linked list of all its allocated area and the signal may come and change it. Also, processes that deal with standard I/O may also cause havoc when signal arrives because the buffer may be inconsistent.

There are several ways to solve the above problem.

1) block and restore signals around every new/malloc

2) have new/malloc block/store all signals itself. (The disadvantages are that it worsens real time response and it adds 2 system calls to each new/malloc).

3) We can simply don't do this because malloc is not asynchronous-signal safe.

Summarization

Let's summarize the advantages and disadvantages of signals.

Advantages:

+ Improved performance over polling

+ The SIGKILL signal can let you manage processes

Disadvantages:

- race conditions

- horrible to debug

- processes are no longer isolated as well as before