信号函数
一、函数signal
系统信号机制最简单的接口函数是signal函数。该函数的原型为:
#include <signal.h> void (*signal(int signo,void (*func)(int)))(int);
signal函数原型说明此函数要求两个参数,返回一个函数指针,而该函数指针所指向的函数无返回值。第一个参数
signo是一个整型数,第二个参数是函数指针,它所指向的需要一个整型参数,无返回值。signal的返回值是一个函数
地址,该函数有一个整型参数。要向信号处理程序传送一个整型参数,而它却无返回值。当调用signal设置信号处理程
序时,第二个参数是指向该函数(即信号处理程序)的指针。signal的返回值则是指向在此之前的信号处理程序的指针。
二、函数kill和raise
kill函数将信号发送给进程或进程组。raise函数则允许进程向自身发送信号。函数的原型为:
#include <signal.h> int kill(pid_t pid,int signo); int raise(int signo);
调用raise(signo);等价于调用kill(getpid(),signo);kill的pid参数有4种不同的情况:pid>0,将该信号发送给
进程ID为pid的进程。pid==0,将该信号发送给与发送进程属于同一进程组的所有进程,而且发送进程具有权限向这些
进程发送信号。pid<0,将该信号发送给其进程组ID等于pid绝对值,而且发送进程具有权限向其发送信号的所有进程。
pid==-1,将该信号发送给发送进程有权限向它们发送信号的所有进程。
三、函数alarm和pause
使用alarm函数可以设置一个定时器,在将来的某个时刻给定时器会超时。当定时器超时时,产生SIGALRM信号。如
果忽略或不捕捉此信号,则其默认动作是终止调用该alarm函数的进程。函数的原型为:
#include <unistd.h> unsigned int alarm(unsigned int seconds);
参数seconds的值是产生信号SIGALRM需要经过的时钟秒数。当这一时刻到来时,信号由内核产生,由于进程调度的
延迟,所以进程得到控制从而能够处理该信号还需要一个时间间隔。虽然SIGALRM的默认动作是终止进程,但是大多数
使用闹钟的进程捕捉此信号。如果此时进程要终止,则在终止之前它可以执行所需的清理操作。如果想捕捉SIGALRM信
号,则必须在调用alarm之前安装该信号的处理程序。如果先调用alarm,然后在安装SIGALRM处理程序之前已接到该信
号,那么进程将终止。
pause函数使调用进程挂起直至捕捉到一个信号。函数的原型为:
#include <unistd.h>
int pause();
只有执行了一个信号处理程序并从其返回时,pause才返回。在这种情况下,pause返回-1,errno设置为EINTR。
四、信号集
数据类型sigset_t包含一个信号集,并定义了5个处理信号集的函数。函数的原型为:
#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指向的信号集,使
其包括所有的信号。所有应用程序在使用信号集前,要对该信号集调用sigemptyset或sigfillset一次。因为C编译程序
将不赋初值的尾部变量和静态变量都初始化为0,而这是否与给定系统上信号集的实现相对应。一旦已经初始化了一个信
号集,以后就可在该信号集中增、删特定的信号。函数sigaddset将一个信号添加到已有的信号集中,sigdelset则从信
号集中删除一个信号。对所有以信号集作为参数的函数,总是以信号集地址作为向其传送的参数。
五、函数sigprocmask和sigpending
一个进程的信号屏蔽字规定了当前阻塞而不能递送给该进程的信号集。调用函数sigprocmask可以检测或更改,或同
时进行检测和更改进程的信号屏蔽字。该函数的原型为:
#include <signal.h> int sigprocmask(int how,const sigset_t *restrict set,sigset_t *restrict oset);
返回值:若成功,返回0;若出错,返回-1
首先,若oset是非空指针,那么进程的当前信号屏蔽字通过oset返回。其次,若set是一个非空指针,则参数how指示
如何修改当前信号屏蔽字。下面说明了how可选的值。SIG_BLOCK是或操作,而SIG_SETMASK则是赋值操作。并且注意,不
能阻塞SIGKILL和SIGSTOP信号。
用sigprocmask更改当前信号屏蔽字的方法
如果set是个空指针,则不改变该进程的信号屏蔽字,how的值也无意义。在调用sigprocmask后如果有任何未决的、
不再阻塞的信号,则在sigprocmask返回前,至少将其中一个递送给该进程。sigprocmask是仅为单线程进程定义的。处
理多线程进程中信号的屏蔽使用另一个函数。
sigpending函数返回一信号集,对于调用进程而言,其中的各信号是阻塞不能传递的,因而也一定是当前未决的。
该信号集通过set参数返回。函数的原型为:
#include <signal.h> int sigpending(sigset_t *set); 返回值:若成功,返回0;若出错,返回-1
六、函数sigaction、sigsetjmp和siglongjmp
sigaction函数的功能是检查或修改(或检查并修改)与指定信号相关联的处理动作。该函数的原型为:
#include <signal.h> int sigaction(int signo,const struct sigaction *restrict act, struct sigaction *restrict oact); 返回值:若成功,返回0;若出错,返回-1
其中,参数signo是要检测或修改其具体动作的信号编号。若act指针非空,则要修改其动作。如果oact指针非空,
则系统经由oact指针返回该信号的上一个动作。此函数使用下列结构:
struct sigaction { void (*sa_handler)(int); sigset_t sa_mask; int sa_flags; void (*sa_sigaction)(int, siginfo_t *,void *); };
当更改信号动作时,如果sa_handler字段包含一个信号捕捉函数的地址(不是常量SIG_IGN或SIG_DFL),则sa_mask
字段说明了一个信号集,在调用该信号捕捉函数之前,这一信号集要加到进程的信号屏蔽字中。仅当从信号捕捉函数返
回时再将进程的信号屏蔽字恢复为原先值。这样,在调用信号处理程序时就能阻塞某些信号。在信号处理程序被调用时,
操作系统建立的新信号屏蔽字包括正被传递的信号。因此保证了在处理一个给定的信号时,如果这种信号再次发生,那
么它会被阻塞到对前一个信号的处理结束为止。
在信号处理程序中进行非局部转移时应当使用sigsetjmp和siglongjmp这两个函数。函数原型为:
#include <signal.h> int sigsetjmp(sigjmp_buf env, int savemask); 返回值:若直接调用,返回0;若从siglongjmp调用返回,则返回非0 void siglongjmp(sigjmp_buf env, int val);
这两个函数和setjmp、longjmp之间唯一的区别是sigsetjmp增加了一个参数。如果savemask非0,则sigsetjmp在
env中保存进程的当前信号屏蔽字。调用siglongjmp时,如果非0 savemask的sigsetjmp调用已经保存了env,则siglo
ngjmp从其中恢复保存的信号屏蔽字。
七、函数sigsuspend和abort
更改进程的信号屏蔽字可以阻塞所选择的信号,或解除对它们的阻塞。使用这种技术可以保护不希望由信号中断
的代码临界区。如果希望对一个信号解除阻塞,然后pause以等待以前被阻塞的信号发生,如果在信号阻塞时,产生了
信号,那么该信号的传递就被推迟直到对它解除了阻塞。对应用程序而言,该信号好像发生在解除对SIGINT的阻塞和
pause之间(取决于内核如何实现信号)。如果发生这种情况,或者如果在解除阻塞时刻和pause之间确实发生了信号,
那么就会产生问题。因为可能不会再见到该信号,所以从这种意义上讲,在此时间窗口中发生的信号丢失了,这样就
使得pause永远阻塞。这是早期的不可靠信号机制的另一个问题。
因此需要在一个原子操作中先恢复信号屏蔽字,然后使进程休眠。这种功能是由sigsuspend函数提供的。原型为:
#include <signal.h> int sigsuspend(const sigset_t *sigmask); 返回值:-1,并将errno设置为EINTR
进程的信号屏蔽字设置为由sigmask指向的值。在捕捉到一个信号或发生了一个会终止该进程的信号之前,该进程
被挂起。如果捕捉到一个信号而且从该信号处理程序返回,则sigsuspend返回,并且该进程的信号屏蔽字设置为调用
sigsuspend之前的值。注意,此函数没有成功返回值。如果它返回到调用者,则总是返回-1,并将errno设置为EINTR
(表示一个被中断的系统调用)。
abort函数的功能是使程序异常终止。该函数的原型为:
#include <stdlib.h> void abort(void); 此函数不返回值
此函数将SIGABRT信号发送给调用进程(进程不应忽略此信号)。调用abort将向主机环境递送一个未成功终止的
通知,其方法是调用raise(SIGABRT)函数。若捕捉到此信号而且相应信号处理程序返回,abort仍不会返回到其调用
者。如果捕捉到此信号,则信号处理程序不能返回的唯一方法是它调用exit、_exit、_Exit、longjmp或siglongjmp。
让进程捕捉SIGABRT的意图是:在进程终止之前由其执行所需的清理操作。如果进程并不在信号处理程序中终止
自己,当信号处理程序返回时,abort终止该进程。