Advanced Programming in UNIX Environment Episode 38

Signals

Introduction

Signals are software interrupts. Most nontrivial application programs need to deal with signals. Signals provide a way of handling asynchronous events.

Signals have been provided since the early versions of the UNIX System, but the signal model provided with systems such as Version 7 was not reliable. Signals could get lost, and it was difficult for a process to turn off selected signals when executing critical regions of code. Both 4.3BSD and SVR3 made changes to the signal model, adding what are called reliable signals. But the changes made by Berkeley and AT&T were incompatible. Fortunately, POSIX.1 standardized the reliable-signal routines.

Signal Concepts

First, every signal has a name. These names all begin with the three characters SIG.

SIGABRT is the abort signal that is generated when a process calls the abort function. SIGALRM is the alarm signal that is generated when the timer set by the alarm function goes off. Version 7 had 15 different signals; SVR4 and 4.4BSD both had 31 different signals. FreeBSD 8.0 supports 32 different signals. Mac OS X 10.6.8 and Linux 3.2.0 each support 31 different signals, whereas Solaris 10 supports 40 different signals. FreeBSD, Linux, and Solaris, however, support additional application-defined signals introduced to support real-time applications. Although the POSIX real-time extensions aren’t covered in this book as of SUSv4 the real-time signal interfaces have moved to the base specification.

Signal names are all defined by positive integer constants (the signal number) in the header <signal.h>.

Both FreeBSD 8.0 and Mac OS X 10.6.8 define the signals in <sys/signal.h>. Linux 3.2.0 defines the signals in <bits/signum.h>, and Solaris 10 defines them in <sys/iso/signal_iso.h>.

No signal has a signal number of 0. We’ll see in Section 10.9 that the kill function uses the signal number of 0 for a special case. POSIX.1 calls this value the null signal.

Numerous conditions can generate a signal:

  • The terminal-generated signals occur when users press certain terminal keys.
  • Hardware exceptions generate signals: divide by 0, invalid memory reference, and the like.
  • The kill function allows a process to send any signal to another process or process group.
  • The kill command allows us to send signals to other processes.
  • Software conditions can generate signals when a process should be notified of various events.

Signals are classic examples of asynchronous events. They occur at what appear to be random times to the process.

We can tell the kernel to do one of three things when a signal occurs. We call this the disposition of the signal, or the action associated with a signal.

1.Ignore the signal. This works for most signals, but two signals can never be ignored: SIGKILL and SIGSTOP.
2.Catch the signal. To do this, we tell the kernel to call a function of ours whenever the signal occurs.
3.Let the default action apply. Every signal has a default action, shown in Figure 10.1. Note that the default action for most signals is to terminate the process.

The generation of the core file is an implementation feature of most versions of the UNIX System. Although this feature is not part of POSIX.1, it is mentioned as a potential implementation-specific action in the Single UNIX Specification’s XSI option.

You can refer the further details of each signal in APUE. This article is not intended to list these signals here.

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);

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.
4.4BSD also provides the signal function, but it is defined in terms of the sigaction function.
Because the semantics of signal differ among implementations, we must use the sigaction function instead.

Many systems call the signal handler with additional, implementation-dependent arguments.

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

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 will 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.

#include "apue.h"

static void sig_usr(int);

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)
{
    if(signo==SIGUSR1)
        printf("received SIGUSR1\n");
    else if(signo==SIGUSR2)
        printf("recived SIGUSR2\n");
    else
        err_dump("received signal %d\n");
}

Simple program to catch SIGUSR1 and SIGUSR2

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.

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.

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);

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

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.

Unreliable Signals

In earlier versions of the UNIX System (such as Version 7), signals were unreliable.

Changes were made with 4.2BSD to provide what are called reliable signals. A different set of changes was then made in SVR3 to provide reliable signals under System V. POSIX.1 chose the BSD model to standardize.

Interrupted System Calls

A characteristic of earlier UNIX systems was that if a process caught a signal while the process was blocked in a ‘‘slow’’ system call, the system call was interrupted. The system call returned an error and errno was set to EINTR.

To support this feature, the system calls are divided into two categories: the ‘‘slow’’ system calls and all the others. The slow system calls are those that can block forever. Included in this category are

  • Reads that can block the caller forever if data isn’t present with certain file types (pipes, terminal devices, and network devices)
  • Writes that can block the caller forever if the data can’t be accepted immediately by these same file types
  • Opens on certain file types that block the caller until some condition occurs (such as a terminal device open waiting until an attached modem answers the phone)
  • The pause function (which by definition puts the calling process to sleep until a
    signal is caught) and the wait function
  • Certain ioctl operations
  • Some of the interprocess communication functions (Chapter 15)

The notable exception to these slow system calls is anything related to disk I/O. Although a read or a write of a disk file can block the caller temporarily (while the disk driver queues the request and then the request is executed), unless a hardware error occurs, the I/O operation always returns and unblocks the caller quickly.

POSIX.1 semantics for interrupted reads and writes changed with the 2001 version of the standard. Earlier versions gave implementations a choice of how to deal with reads and writes that have processed partial amounts of data.

Historically, implementations derived from System V fail the system call, whereas BSD-derived implementations return partial success. With the 2001 version of the POSIX.1 standard, the BSD-style semantics are required.

To prevent applications from having to handle interrupted system calls, 4.2BSD introduced the automatic restarting of certain interrupted system calls. The system calls that were automatically restarted are ioctl, read, readv, write, writev, wait, and waitpid.

POSIX.1 requires an implementation to restart system calls only when the SA_RESTART flag is in effect for the interrupting signal.
Historically, when using the signal function to establish a signal handler, implementations varied with respect to how interrupted system calls were handled. System V never restarted system calls by default. BSD, in contrast, restarted them if the calls were interrupted by signals. On FreeBSD 8.0, Linux 3.2.0, and Mac OS X 10.6.8, when signal handlers are installed with the signal function, interrupted system calls will be restarted. The default on Solaris 10, however, is to return an error (EINTR) instead when system calls are interrupted by signal handlers installed with the signal function. By using our own implementation of the signal function, we avoid having to deal with these differences.

One of the reasons 4.2BSD introduced the automatic restart feature is that sometimes we don’t know that the input or output device is a slow device.

Be aware that UNIX systems from other vendors can have values different from those shown in this figure. For example, sigaction under SunOS 4.1.2 restarts an interrupted system call by default, unlike the platforms listed in Figure 10.3.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值