第十章
10.1 引言
10.2 信号概念
SIGABRT是夭折信号,当进程调用abort函数时产生这种信号。 SIGALRM是闹钟信号,当由alarm函数设置的时间已经超过后产生此信号 在头文件<signal.h>中,这些信号都被定义为正整数(信号编号)。没有一个信号其编号为0。 SIGKILL和SIGSTOP信号却决不能被忽略。 信号表以及各个信号说明参见APUE第251-256页。
10.3 函数signal
# include <signal.h>
void ( * signal ( int signo, void ( * func) ( int ) ) ) ( int ) ;
signo参数是表10-1中的信号名。func的值是:(a)常数SIG_IGN,或(b)常数SIG_DFL,或©当接到此信号后要调用的函数的地址。如果指定SIG_IGN,则向内核表示忽略此信号。(记住有两个信号SIGKILL和SIGSTOP不能忽略。)如果指定SIG_DFL,则表示接到此信号后的动作是系统默认动作(见表10-1中的最后1列)。当指定函数地址时,我们称此为捕捉此信号。我们称此函数为信号处理程序(signalhandler)或信号捕捉函数(signal-catchingfunction)。 kill命令和kill函数只是将一个信号送给一个进程或进程组。 exec函数将原先设置为要捕捉的信号都更改为默认动作 当一个进程调用fork时,其子进程继承父进程的信号处理方式。 shell自动将后台进程中对中断和退出信号的处理方式设置为忽略。
10.5 中断的系统调用
如果在进程执行一个低速系统调用而阻塞期间捕捉到一个信号,则该系统调用就被中断不再继续执行。该系统调用返回出错,其errno设置为EINTR。 低速系统调用是可能会使进程永远阻塞的一类系统调用。 被中断的系统调用的自动再起动。自动再起动的系统调用包括:ioctl、read、readv、write、writev、wait和waitpid。 请注意,本地磁盘不是慢速设备;
10.6 可重入函数
不可重入函数原因:它们使用静态数据结构,它们调用malloc或free,它们是标准I/O函数 在信号处理程序中保证调用安全的函数。这些函数是可重入的并被称为是异步信号安全的,除了可重入以外,在信号处理操作期间,它会阻塞任何会引起不一致的信号发送。 当在信号处理程序中调用可重入函数时,应当在其前保存,在其后恢复errno。
10.7 SIGCLD语意
如果进程特地指定对该信号的配置为SIG_IGN,则调用进程的子进程将不产生僵死进程。 如果将SIGCLD的配置设置为捕捉,则内核立即检查是否有子进程准备好被等待,如果是这样,则调用SIGCLD处理程序。 在linux中,#define SIGCLD SIGCHLD
10.8 可靠信号术语和语义
向一个进程递送了一个信号。在信号产生(generation)和递送(delivery)之间的时间间隔内,称信号未决(pending)。 如果为进程产生了一个选择为阻塞的信号,而且对该信号的动作是系统默认动作或捕捉该信号,则为该进程将此信号保持为未决状态,直到该进程对此信号解除了阻塞,或者将对此信号的动作更改为忽略。 每个进程都有一个信号屏蔽字,它规定了当前要阻塞递送到该进程的信号集。
10.9 函数kill和raise
kill函数将信号发送给进程或进程组。raise函数则允许进程向自身发送信号。
# include <signal.h>
int kill ( pid_t pid, int signo) ;
int raise ( int signo) ;
pid大于零时,pid是信号欲送往的进程的标识。 pid等于零时,信号将送往所有与调用kill()的那个进程属同一个使用组的进程。 pid等于-1时,信号将送往所有调用进程有权给其发送信号的进程,除了进程1(init)。 pid小于-1时,信号将送往以-pid为组标识的进程。
进程将信号发送给其他进程需要许可权 规则是发送者的实际或有效用户ID必须等于接收者的实际或有效用户ID。 如果被发送的信号是SIGCONT,则进程可将它发送给属于同一对话期的任一其他进程。 如果signo参数是0,则kill仍执行正常的错误检查,但不发送信号。
10.10 函数alarm和pause
使用alarm函数可以设置一个时间值,在将来的某个时刻该时间值会被超过。当所设置的时间值被超过后,产生SIGALRM信号。如果不忽略或不捕捉此信号,则其默认动作是终止该进程。
# include <unistd.h>
unsigned int alarm ( unsigned int seconds) ;
参数seconds的值是秒数。 每个进程只能有一个闹钟时间。如果在调用alarm时,以前已为该进程设置过闹钟时间,而且它还没有超时,则该闹钟时间的余留值作为本次alarm函数调用的值返回。以前登记的闹钟时间则被新值代换。 如果有以前登记的尚未超过的闹钟时间,而且seconds值是0,则取消以前的闹钟时间,其余留值仍作为函数的返回值。
# include <unistd.h>
int pause ( void ) ;
10.11 信号集
# include <signal.h>
int sigemptyset ( sigset_t * set) ;
int sigfillset ( sigset_t * set) ;
int sigaddset ( sigset_t * set, int signo) ;
int sigdelset ( sigset_t * set, int signo) ;
int sigismember ( const sigset_t * set, int signo) ;
函数sigemptyset初始化由set指向的信号集,使排除其中所有信号。函数sigfillset初始化由set指向的信号集,使其包括所有信号。 函数sigaddset将一个信号添加到现存集中,sigdelset则从信号集中删除一个信号。
10.12 函数sigprocmask
函数sigprocmask可以检测或更改(或两者)进程的信号屏蔽字。
# include <signal.h>
int sigprocmask ( int how, const sigset_t * set, sigset_t * oset) ;
oset是非空指针,进程的当前信号屏蔽字通过oset返回。 若set是一个非空指针,则参数how指示如何修改当前信号屏蔽字。 SIG_BLOCK:添加信号 SIG_UNBLOCK:移除信号 SIG_SETMASK:赋值。
10.13 函数sigpending
sigpending返回对于调用进程被阻塞不能递送和当前未决的信号集。该信号集通过set参数返回。
# include <signal.h>
int sigpending ( sigset_t * set) ;
10.14 函数sigaction
sigaction函数的功能是检查或修改(或两者)与指定信号相关联的处理动作。
# include <signal.h>
int sigaction ( int signo, const struct sigaction * act, struct sigaction * oact) ;
参数signo是要检测或修改具体动作的信号的编号数。若act指针非空,则要修改其动作。如果oact指针非空,则系统返回该信号的原先动作。
struct sigaction {
void ( * sa_handler) ( ) ;
sigset_t sa_mask;
int s a_flags;
void ( * sa_sigaction) ( int , siginfo_t * , void * ) ;
} ;
如果sa_handler指向一个信号捕捉函数,则sa_mask字段说明了一个信号集,在调用信号捕捉函数之前,该信号集要加到进程的信号屏蔽字中。仅当从信号捕捉函数返回时再将进程的信号屏蔽字恢复为原先值。 sa_flags: SA_INTERRUPT :由此信号中断的系统调用不自动重启动 SA_NOCLDSTO :当一子进程停止时,不产生此信号。 SA_NOCLDWAIT :若signo是SIGCHLD,则当调用进程的子进程终止时,不创建僵死进程。 SA_NODEFER :当捕捉到此信号时,在执行其信号捕捉函数时,系统不自动阻塞此信号。 SA_ONSTACK :如果利用sigaltstack()建立信号专用堆栈,则此标志会把所有信号送往该堆栈。 SA_RESETHAN :在此信号捕捉函数的入口处,将此信号的处理方式重置为SIG_DFL,并清除SA_SIGINFO标志。不能自动重置SIGILL和SIGTRAP这两个信号的配置。设置此标志使sigaction的行为如同设置了SA_NODEFER标志 SA_RESTAR :由此信号中断的系统调用自动再起动 SA_SIGINF :此选项对信号处理程序提供了附加信息:一个指向siginfo结构的指针以及一个指向进程上下文标识符的指针。 如果设置了SA_SIGINFO标志,那么按下列方式调用信号处理程序: void handler(int signo, siginfo_t *info, void *context); siginfo和context详见APUE第280页。
10.15 函数sigsetjmp和siglongjmp
在信号处理程序中进行非局部转移时应当使用sigsetjmp和siglongjmp函数。
# include <setjmp.h>
int sigsetjmp ( sigjmp_buf env, int savemask) ;
void siglongjmp ( sigjmp_buf env, int val) ;
如果savemask非0,则sigsetjmp在env中保存进程的当前信号屏蔽字。则siglongjmp从其中恢复保存的信号屏蔽字。
10.16 函数sigsuspend
sigsuspend函数在一个原子操作中实现恢复信号屏蔽字,然后使进程睡眠。
# include <signal.h>
int sigsuspend ( const sigset_t * sigmask) ;
进程的信号屏蔽字设置为由sigmask指向的值。 进程的信号屏蔽字设置为由sigmask指向的值。在捕捉到一个信号或发生了一个会终止该进程的信号之前,该进程也被挂起。如果捕捉到一个信号而且从该信号处理程序返回,则sigsuspend返回,并且该进程的信号屏蔽字设置为调用sigsuspend之前的值。
10.17 函数abort
# include <stdlib.h>
void abort ( void ) ;
ANSIC要求若捕捉到此信号而且相应信号处理程序返回,abort仍不会返回到其调用者。
10.18 函数system
POSIX.2要求system忽略SIGINT和SIGQUIT,阻塞SIGCHLD。 system的返回值是shell的终止状态,其终止状态是128加上它所执行的命令由一个信号终止时的该信 号编号数。仅当shell本身异常终止时,system的返回值才报告一个异常终止。
10.19 函数sleep、nanosleep和clock_nanosleep
# include <unistd.h>
unsigned int sleep ( unsigned int seconds) ;
nanosleep函数与sleep函数癸似,但提供了纳杪级的精度。
# include <time.h>
int nanosleep ( const struct timespec * reqp, struct timespec * remtp) ;
reqgtp参数用秒和纳秒指定了需要休眠的时间长度。如果某个信号中断了休眠间隔,进程并没有终止,remtp参数指向的timespec结构就会被设置为未休眠完的时间长度。如果对未休眠完的时间并不感兴趣,可以把该参数置为NULL。 nanosleep函数并不涉及产生任何信号
使用一个特殊时钟来实现一段延时。函数clock_nanosleep实现了这一功能。
# include <time.h>
int clock_nanosleep ( clockid_t clock_id, int flags, const struct timespec * reqtp, struct timespec * remtp) ;
参数clock_id用于指定延时是相对于哪一种时钟时间进行的评估。当flags被设置为0的时候,睡眠时间是相对的(想要睡眠多久),当其数值被设置为TIMER_ABSTIME的时候,睡眠时间是绝对的。
10.20 函数sigqueue
使用sigaction函数安装信号处理程序时指定SA_STGINFO标志。 在sigaction结构的sa_sigaction成员中提供信号处理程序。 使用sigqueue函数发送信号。
# include <signal.h>
int sigqueue ( pid_t pid, int signo, const union sigvalvalue) ;
信号不能被无限排队。 随着实时信号的增强,引入了用于应用程序的独立信号集。这些信号的编号在SIGRTMI~SIGRTMAX之间,包括这两个限制值。注意,这些信号的默认行为是终止进程。
10.21 作业控制信号
当对一个进程产生SIGTSTP,SIGSTOP,SIGTTIN或SIGTTOU中的任意一种时,对该进程的任一未决的SIGCONT信号就被丢弃。当对一个进程产生SIGCONT信号时,对同一进程的任一未决的停止信号被丢弃。
10.22 信号名和编号
extern char *sys_siglist[];:下标是信号编号,数组中的元素是指向一个信号字符串名字的指针。
可以使用psignal函数可移植地打印与信号编号对应的字符串。
# include <signal.h>
void psignal ( int signo, const char * msg)
字符串msg(通常是程序名)输出到标准出错,后面跟着一个冒号和一个空格,再跟着对该信号的说明,最后是一个新行符。
如果在sigaction信号处理程序中有siginfo结构,可以使用psiginfo函数打印信号信息。
# include <signal.h>
void psiginfo ( const siginfo_t * info, const char * msg) ;
给出一个信号编号,strsignal 将返回描述该信号的字符串。
# include <string.h>
char * strsignal ( int signo) ;
# include <signal.h>
int sig2str ( int signo, char * str) ;
int str2sig ( const char * str, int * signop) ;
信号名是不带SIG前缀的信号名,str2sig函数可以使用标识十进制信号编号的字符串。