sigaction函数

sigaction函数

sigaction函数允许我们检查或修改(或两者)特定信号的动作。这个函数在早期的UNIX版本中代替了signal函数。在本节的最后,我们提供了一个使用sigactionsignal实现。

#include <signal.h>

int sigaction(int signo, const struct sigaction *restrict act,
              struct sigaction *restrict oact);

Returns: 0 if OK, 1 on error

signo参数是我们要检查或要修改的信号数。如果act指针是一个非空指针,那么会修改这个指针。如果oact指针是一个非空指针,系统通过oact指针返回之前该信号的动作。这个函数使用下面的结构:

     struct sigaction {
       void      (*sa_handler)(int);   /* addr of signal handler, */
                                       /* or SIG_IGN, or SIG_DFL */
       sigset_t sa_mask;               /* additional signals to block */
       int      sa_flags;              /* signal options, Figure 10.16 */

       /* alternate handler */
       void     (*sa_sigaction)(int, siginfo_t *, void *);
    };

当为信号改变动作时,如果sa_handler段包含了信号处理函数(signal-catching function)的地址(不包含SIG_IGNSIG_DFL),那么在信号处理函数(signal-catching function)被调用前sa_mask字段为进程指定了一套信号掩码。当且仅当信号处理函数返回时,进程的信号掩码重置成它之前的值。这种方法,我们能阻塞某种信号无论信号处理函数(signal handler)何时被调用。因此,我们可以保证无论何时进程收到信号时,其它相同信号都被阻塞,直到我们完成进程第一个收到的信号。回忆APUE10.8节,额外发生的相同信号通常不会排队。当我们未解锁信号时如果信号发生5次都被阻塞,为该信号设置的信号处理函数仅将会被调用一次。

一旦我们为某一信号“安装”了动作, 直到我们明显的通过sigaction改变它,它都会一直保留这个动作。

参数act结构的sa_flags字段为处理信号指定了多种选项。FIGURE10.16有这些选项的详细信息。SUS列中包含•表示这个标记是做为基于POSIX.1一部分定义的。包含XSI表示是基于XSI的。

Figure 10.16. Option flags (sa_flags) for the handling of each signal

Option

SUS

FreeBSD 5.2.1

Linux 2.4.22

Mac OS X 10.3

Solaris 9

Description

SA_INTERRUPT

系统被该信号中断后不会自动重启(the XSI default for sigaction)详情见APUE10.5节

SA_NOCLDSTOP

如果signoSIGCHLD,当子进程停止(任务控制)时不产生该信号。当然,当一个子进程终止时这个信号仍然会产生(但是要看下面的SA_NOCLDWAIT选项情况)。做为XSI扩展,当一个设置了该选项的子进程停止后又继续运行时SIGCHLD将不会被发送。

SA_NOCLDWAIT

XSI

如果signoSIGCHLD,这个选项可以防止当调用进程的子进程终止后系统建立僵尸进程。如果它随后调用wait,那么这个调用进程会被阻塞直到它的所有子进程都已经终止并返回1而且errno设置成ECHILD。 (Recall Section 10.7.)

SA_NODEFER

XSI

当这个信号被捕获后,当信号处理程序执行时信号不会被系统阻塞(除非信号被包含在sa_mask中)。注意这类操作适用于早期不可靠的信号。

SA_ONSTACK

XSI

如果备用栈(alternate stack)已被sigaltstack(2)声明过,这个信号被传送到进程的备用栈上。

SA_RESETHAND

XSI

该标记是用于信号重置SIG_DFL,并且清除传送到信号处理函数的SA_SIGINFO标记。注意,这个操作适用于早期不可靠的信号。无论如何,这个标记对于SIGILLSIGTRAP信号不能自动重启。设置这个标记会导致sigaction的行为就像同时设置了SA_NODEFER一样。

SA_RESTART

XSI

当系统调用被中断后,该标记会令系统调用自动重启。(详情APUE 10.5节)

SA_SIGINFO

这个选项提供了额外的信息给信号处理函数:一个指向siginfo结构的指针,并且一个指针指向进程文本区的标示符。

SA_SIGINFO标记被用于sigaction时,sa_sigaction字段是一个可选的信号处理函数。具体实现可能会使用相同的存储空间存放sa_sigaction字段和sa_handler字段,所以应用程序在同一时刻只能使用其中的一个。

一般来说,信号处理函数(signal handler)调用如下:

void handler(int signo);

但是如果SA_SIGINFO标记被设置,信号处理函数调用如下:

void handler(int signo, siginfo_t *info, void *context);

siginfo_t结构包含了关于为什么信号被产生的信息。下面有一个都能显示什么内容的例子。所有POSIX.1兼容实现必须至少包含si_signosi_code成员。另外,XSI兼容的具体实现至少包含如下字段:

struct siginfo {
      int    si_signo;  /* signal number */
      int    si_errno;  /* if nonzero, errno value from <errno.h> */
      int    si_code;   /* additional info (depends on signal) */
      pid_t  si_pid;    /* sending process ID */
      uid_t  si_uid;    /* sending process real user ID */
      void  *si_addr;   /* address that caused the fault */
      int    si_status; /* exit value or signal number */
      long   si_band;   /* band number for SIGPOLL */
      /* possibly other fields also */
    };

FIGURE 10.17显示了si_code对于各种信号的值,做为Single UNIX Specification。注意:具体实现可能定义了额外的值。

Figure 10.17. siginfo_t code values

Signal

Code

Reason

ILL_ILLOPC

illegal opcode

ILL_ILLOPN

illegal operand

ILL_ILLADR

illegal addressing mode

SIGILL

ILL_ILLTRP

illegal trap

ILL_PRVOPC

privileged opcode

ILL_PRVREG

privileged register

ILL_COPROC

coprocessor error

ILL_BADSTK

internal stack error

FPE_INTDIV

integer divide by zero

FPE_INTOVF

integer overflow

FPE_FLTDIV

floating-point divide by zero

FPE_FLTOVF

floating-point overflow

SIGFPE

FPE_FLTUND

floating-point underflow

FPE_FLTRES

floating-point inexact result

FPE_FLTINV

invalid floating-point operation

FPE_FLTSUB

subscript out of range

SIGSEGV

SEGV_MAPERR

address not mapped to object

SEGV_ACCERR

invalid permissions for mapped object

BUS_ADRALN

invalid address alignment

SIGBUS

BUS_ADRERR

nonexistent physical address

BUS_OBJERR

object-specific hardware error

trAP_BRKPT

process breakpoint trap

SIGTRAP

TRAP_TRACE

process trace trap

CLD_EXITED

child has exited

CLD_KILLED

child has terminated abnormally (no core)

CLD_DUMPED

child has terminated abnormally with core

SIGCHLD

CLD_TRAPPED

traced child has trapped

CLD_STOPPED

child has stopped

CLD_CONTINUED

stopped child has continued

POLL_IN

data can be read

POLL_OUT

data can be written

SIGPOLL

POLL_MSG

input message available

POLL_ERR

I/O error

POLL_PRI

high-priority message available

POLL_HUP

device disconnected

SI_USER

signal sent by kill

SI_QUEUE

signal sent by sigqueue (real-time extension)

Any

SI_TIMER

expiration of a timer set by timer_settime (real-time extension)

SI_ASYNCIO

completion of asynchronous I/O request (real-time extension)

SI_MESGQ

arrival of a message on a message queue (real-time extension)

如果信号是SIGCHLD,那么si_pidsi_statussi_uid字段将被设置。如果信号是SIGILLSIGSEGV,那么si_addr含有失败地址的责任,虽然地址可能不是非常的精准。如果信号是SIGPOLL,那么si_band字段将包含STREAMS信息的优先级波段(priority band),该信息产生POLL_INPOLL_OUTPOLL_MSG事件。si_errno字段包含了引发信号条件的错误号,虽然错误号是由具体实现定义的。

信号处理函数的context参数是无类型指针,它能被强制转换成ucontext_t结构,用于在信号传送时识别进程内容。

当一个系统支持实时信号扩展时,信号处理函数使用SA_SIGINFO标记建立将会让信号可靠的排队。一个单独保留的信号范围平均用于实时应用程序。如果信号是被sigqueue产生的,siginfo结构能包含应用程序指定数据。本书并不讨论实时扩展功能。

signal函数例子

现在让我们使用sigaction实现signal函数功能。很多平台都是这么做的。系统使用二进制兼容约束,换言之,可以让signal函数支持更老的、不可靠的语义。除非你指定要求更老的、不可靠的语义,不然你应该直接使用当前实现的signalsigaction。(因为你可能猜到了,一个使用老语义的signal实现,可以调用sigaction函数指定SA_RESETHANDSA_NODEFER选项实现。)本文中所说的signal都在figure10.18中实现。

注意,我们必须使用sigemptyset去初始化sa_mask结构的成员。使用act.sa_mask=0不能保证和使用sigemptyset具有相同效果。

我们有意的尝试对除了SIGALRM信号外的所有信号使用SA_RESTART标记,所以任何系统调用被中断后都会自动重新启动。不对SIGALRM信号使用SA_RESTART标记的原因是我们要为I/O操作设置超时。

一些老系统,就像SunOS,定义了SA_INTERRUPT标记。这些系统默认会重新启动被中断的系统调用,所以指定这个标记会导致系统调用被中断(我估计应该是不能重新启动了)。Linux也定义了这个标记,为了兼容使用该标记的程序,但是当使用sigaction安装信号处理函数时,默认是不重启系统调用。Single UNIX Specification的XSI扩展指定了sigaction函数不重启被中断的系统调用,除非SA_RESTART标记被指定。

Figure 10.18. An implementation of signal using sigaction

#include "apue.h"

/* Reliable version of signal(), using POSIX sigaction(). */
Sigfunc *
signal(int signo, Sigfunc *func)
{
    struct sigaction    act, oact;

    act.sa_handler = func;
    sigemptyset(&act.sa_mask);
    act.sa_flags = 0;
    if (signo == SIGALRM) {
#ifdef SA_INTERRUPT
       act.sa_flags |= SA_INTERRUPT;
#endif
    } else {
#ifdef  SA_RESTART
        act.sa_flags |= SA_RESTART;
#endif
    }
    if (sigaction(signo, &act, &oact) < 0)
        return(SIG_ERR);
    return(oact.sa_handler);
}

signal_intr函数例子

Figure 10.19显示了一个signal函数的版本,它试图防止任何系统调用中断后重启。

为了改善移植性,我们指定了SA_INTERRUPT标记,如果由系统定义了,去防止被中断的系统调用自动重启。

Figure 10.19. The signal_intr function

#include "apue.h"

Sigfunc *
signal_intr(int signo, Sigfunc *func)
{
    struct sigaction    act, oact;

    act.sa_handler = func;
    sigemptyset(&act.sa_mask);
    act.sa_flags = 0;
#ifdef  SA_INTERRUPT
    act.sa_flags |= SA_INTERRUPT;
#endif
    if (sigaction(signo, &act, &oact) < 0)
        return(SIG_ERR);
    return(oact.sa_handler);
}

发表回复