1.signal系统调用
要为一个信号设置处理函数,可以使用signal系统调用:
#include<signal.h>
_sighandler_t signal(int sig, _sighandler_t _handler)
sig参数指出要捕获的信号类型。_handler参数是_sighandler_t类型的函数指针,用于指定信号sig的处理函数。signal函数成功时返回一个函数指针,该函数指针的类型也是_sighandler_t类型。这个返回值是前一次调用signal函数时传入的函数指针,或者是信号sig对应的默认处理函数指针SIG_DEF(如果是第一次调用signal的话)。
signal系统调用出错时返回SIG_ERR,并设置errno。
2.sigaction系统调用
设置信号处理函数更加健壮的接口是如下系统调用:
#include<signal.h>
int sigaction(int sig, const struct sigaction *act, struct sigaction *oact)
sig参数指出要捕获的信号类型,act参数指定新的信号处理方式,oact参数则输出信号先前的处理方式(如果不为NULL的话)。act和oact都是sigaction结构体类型的指针,sigaction结构体描述了信号处理细节,其定义如下:
struct sigaction
{
#ifdef __USE_POSIX199309
union
{
_sighandler_t sa_handler;
void (*sa_sigaction)(int, siginfo_t*, void *);
}_sigaction_handler;
#define sa_hander __sigaction_handler.sa_handler
#define sa_sigaction __sigaction_handler.sa_sigaction
#else
_sigaction_handler sa_handler;
#endif
_sigset_t sa_mask;
int sa_flags;
void (*sa_restorer)(void);
};
该结构体中的sa_handler成员指定信号处理函数。sa_mask成员设置进程的信号掩码(确切地说是在原有进程的信号掩码基础上增加信号掩码),以指定哪些信号不能发送给本进程。sa_mask是信号集sigset_t(_sigset_t的同义词)类型,该类型指定一组信号。sa_flags成员用于设置程序收到信号时的行为:
- SA_RESTART用在为某个信号设置信号处理函数时,给该信号设置的一个标记。一般情况下 ,进程正在执行某个系统调用,那么在该系统调用返回前信号是不会被递送的。但慢速系统调用除外,如读写终端、网络、磁盘,以及wait和pause。这些系 统调用都会返回-1,errno置为EINTR当系统调用被中断时,我们可以选择使用循环再次调用,或者设置重新启动该系统调用 (SA_RESTART)。一旦给信号设置了SA_RESTART标记,那么当执行某个阻塞系统调用时,收到该信号时,进程不会返回,而是重新执行该系统调用。
-
SA_NOCLDSTOP:如果参数signum为SIGCHLD,当子进程被中断时,并不通知父进程。
-
SA_NOCLDWATI:当信号为SIGCHLD,时可避免子进程僵死。
-
SA_NODEFER:当信号处理函数正在进行时,不堵塞对于信号处理函数自身信号功能。
-
SA_NOMASK:同SA_NODEFER,在处理此信号前允许信号再次递送,相当于中断嵌套。
-
SA_ONESHOT:当用户注册的信号处理函数被执行过一次后,该信号的处理函数被设为系统默认的处理函数。
-
SA_RESETHAND:同SA_ONESHOT
-
SA_RESTART:是本来不能重新于运行的系统调用自动重新运行。
-
SA_SIGINFO:如果设置了该标志,则信号处理函数由三参数的sa_sigaction指定而不是sa_handler指定。
3.关于信号掩码字段
sa_mask声明一个信号集,在调用信号捕捉函数之前,该信号集会增加到进程的信号掩码中。新的信号掩码会自动包含正在处理的信号(sa_flags未指定SA_NODEFER和SA_NOMASK)掩码集中。当从信号函数返回时,进程掩码会恢复原来的值。因此当处理一个给定信号时,如果这种信号再次发生,那么它会阻塞直到本次信号处理结束为止。若这种信号发生了多次,则对于不可靠信号,他只会阻塞一次,即本次信号处理结束以后会再处理一次(相当于丢失了信号),对于可靠信号(实时信号),则会被阻塞多次,即信号不会丢失,信号发生了多少次就会调用处理函数多少次。
可以通过下面这段代码示例来进行实验(注意SIGALRM是不可靠信号):
#include<signal.h>
#include<stdio.h>
#include<assert.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
void sig_handler(int sig)
{
sleep(5);
printf("call signal handler.\n");
// alarm(1);
}
void addsig(int sig)
{
struct sigaction sa;
memset(&sa, 0, sizeof(sa));
sa.sa_handler = sig_handler;
sa.sa_flags |= SA_RESTART;
sigfillset(&sa.sa_mask);
assert(sigaction(sig, &sa, NULL) != -1);
}
int main(int argc, char *argv[])
{
addsig(SIGALRM);
alarm(3);
// sleep(5);
alarm(5);
alarm(6);
// sleep(2);
// alarm(3);
printf("go on\n");
// sleep(30);
while(1){}
return 0;
}
本文详细介绍了在Unix系统中如何使用signal和sigaction系统调用来设置信号处理函数,包括信号处理函数的设置、信号掩码的作用及如何使用SA_RESTART等信号标志。通过代码示例展示了信号处理的过程。
169

被折叠的 条评论
为什么被折叠?



