Section 10.3. signal Function

team bbl


10.3. signal Function

The simplest interface to the signal features of the UNIX System is the signal function.

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

Returns: previous disposition of signal (see following) if OK, SIG_ERR on error


The signal function is defined by ISO C, which doesn't involve multiple processes, process groups, terminal I/O, and the like. Therefore, its definition of signals is vague enough to be almost useless for UNIX systems.

Implementations derived from UNIX System V support the signal function, but it provides the old unreliable-signal semantics. (We describe these older semantics in Section 10.4.) This function provides backward compatibility for applications that require the older semantics. New applications should not use these unreliable signals.

4.4BSD also provides the signal function, but it is defined in terms of the sigaction function (which we describe in Section 10.14), so using it under 4.4BSD provides the newer reliable-signal semantics. FreeBSD 5.2.1 and Mac OS X 10.3 follow this strategy.

Solaris 9 has roots in both System V and BSD, but it chooses to follow the System V semantics for the signal function.

On Linux 2.4.22, the semantic of signal can follow either the BSD or System V semantics, depending on the version of the C library and how you compile your application.

Because the semantics of signal differ among implementations, it is better to use the sigaction function instead. When we describe the sigaction function in Section 10.14, we provide an implementation of signal that uses it. All the examples in this text use the signal function that we show in Figure 10.18.

The signo argument is just the name of the signal from Figure 10.1. The value of func is (a) the constant SIG_IGN, (b) the constant SIG_DFL, or (c) the address of a function to be called when the signal occurs. If we specify SIG_IGN, we are telling the system to ignore the signal. (Remember that we cannot ignore the two signals SIGKILL and SIGSTOP.) When we specify SIG_DFL, we are setting the action associated with the signal to its default value (see the final column in Figure 10.1). When we specify the address of a function to be called when the signal occurs, we are arranging to "catch" the signal. We call the function either the signal handler or the signal-catching function.

The prototype for the signal function states that the function requires two arguments and returns a pointer to a function that returns nothing (void). The signal function's first argument, signo, is an integer. The second argument is a pointer to a function that takes a single integer argument and returns nothing. The function whose address is returned as the value of signal takes a single integer argument (the final (int)). In plain English, this declaration says that the signal handler is passed a single integer argument (the signal number) and that it returns nothing. When we call signal to establish the signal handler, the second argument is a pointer to the function. The return value from signal is the pointer to the previous signal handler.

Many systems call the signal handler with additional, implementation-dependent arguments. We discuss this further in Section 10.14.

The perplexing signal function prototype shown at the beginning of this section can be made much simpler through the use of the following typedef [Plauger 1992]:

    typedef void Sigfunc(int); 

Then the prototype becomes

    Sigfunc *signal(int, Sigfunc *); 

We've included this typedef in apue.h (Appendix B) and use it with the functions in this chapter.

If we examine the system's header <signal.h>, we probably find declarations of the form

    #define SIG_ERR (void (*)())-1    #define SIG_DFL (void (*)())0    #define SIG_IGN (void (*)())1 

These constants can be used in place of the "pointer to a function that takes an integer argument and returns nothing," the second argument to signal, and the return value from signal. The three values used for these constants need not be -1, 0, and 1. They must be three values that can never be the address of any declarable function. Most UNIX systems use the values shown.

Example

Figure 10.2 shows a simple signal handler that catches either of the two user-defined signals and prints the signal number. In Section 10.10, we describe the pause function, which simply suspends the calling process until a signal is received.

We invoke the program in the background and use the kill(1) command to send it signals. Note that the term kill in the UNIX System is a misnomer. The kill(1) command and the kill(2) function just send a signal to a process or process group. Whether or not that signal terminates the process depends on which signal is sent and whether the process has arranged to catch the signal.

    $ ./a.out &                   start process in background    [1]      7216                 job-control shell prints job number and process ID    $ kill -USR1 7216             send it SIGUSR1    received SIGUSR1    $ kill -USR2 7216             send it SIGUSR2    received SIGUSR2    $ kill 7216                   now send it SIGTERM    [1]+  Terminated    ./a.out 

When we send the SIGTERM signal, the process is terminated, since it doesn't catch the signal, and the default action for the signal is termination.

Figure 10.2. Simple program to catch SIGUSR1 and SIGUSR2
 #include "apue.h" static void sig_usr(int);   /* one handler for both signals */ int main(void) {     if (signal(SIGUSR1, sig_usr) == SIG_ERR)         err_sys("can't catch SIGUSR1");     if (signal(SIGUSR2, sig_usr) == SIG_ERR)         err_sys("can't catch SIGUSR2");     for ( ; ; )         pause(); } static void sig_usr(int signo)      /* argument is signal number */ {     if (signo == SIGUSR1)         printf("received SIGUSR1\n");     else if (signo == SIGUSR2)         printf("received SIGUSR2\n");     else         err_dump("received signal %d\n", signo); } 

Program Start-Up

When a program is executed, the status of all signals is either default or ignore. Normally, all signals are set to their default action, unless the process that calls exec is ignoring the signal. Specifically, the exec functions change the disposition of any signals being caught to their default action and leave the status of all other signals alone. (Naturally, a signal that is being caught by a process that calls exec cannot be caught by the same function in the new program, since the address of the signal- catching function in the caller probably has no meaning in the new program file that is executed.)

One specific example is how an interactive shell treats the interrupt and quit signals for a background process. With a shell that doesn't support job control, when we execute a process in the background, as in

     cc main.c & 

the shell automatically sets the disposition of the interrupt and quit signals in the background process to be ignored. This is so that if we type the interrupt character, it doesn't affect the background process. If this weren't done and we typed the interrupt character, it would terminate not only the foreground process, but also all the background processes.

Many interactive programs that catch these two signals have code that looks like

          void sig_int(int), sig_quit(int);          if (signal(SIGINT, SIG_IGN) != SIG_IGN)              signal(SIGINT, sig_int);          if (signal(SIGQUIT, SIG_IGN) != SIG_IGN)              signal(SIGQUIT, sig_quit); 

Doing this, the process catches the signal only if the signal is not currently being ignored.

These two calls to signal also show a limitation of the signal function: we are not able to determine the current disposition of a signal without changing the disposition. We'll see later in this chapter how the sigaction function allows us to determine a signal's disposition without changing it.

Process Creation

When a process calls fork, the child inherits the parent's signal dispositions. Here, since the child starts off with a copy of the parent's memory image, the address of a signal-catching function has meaning in the child.

    team bbl



    Advanced Programming in the UNIX Environment
    Advanced Programming in the UNIX Environment, Second Edition (Addison-Wesley Professional Computing Series)
    ISBN: 0321525949
    EAN: 2147483647
    Year: 2005
    Pages: 370

    flylib.com © 2008-2017.
    If you may any questions please contact us: flylib@qtcs.net