Sigaction 信号安装
signaction 和signal都是信号安装函数
#include <signal.h>
int sigaction(int signum, const struct sigaction *act,
struct sigaction *oldact);
功能:
sigaction函数用于改变进程接收到特定信号后的行为。
参数
第一个参数为信号的值,可以为除SIGKILL及SIGSTOP外的任何一个特定有效的信号(为这两个信号定义自己的处理函数,将导致信号安装错误)
第二个参数是指向结构sigaction的指针,在结构 sigaction的实例中,指定了对特定信号的处理,可以为空,进程会以缺省方式对信号处理
第三个参数oldact指向的对象用来保存原来对相应信号的处理,可指定oldact为NULL。
返回值:函数成功返回0,失败返回-1
sigaction结构体
第二个参数最为重要,其中包含了对指定信号的处理、信号所传递的信息、信号处理函数执行过程中应屏蔽掉哪些函数等等
struct sigaction {
//信号处理程序 不接受额外数据(比较过时)
void (*sa_handler)(int);
//信号处理程序能接受额外数据,和sigqueue配合使用(支持信号排队,信号传送其他信息),推荐使用
void (*sa_sigaction)(int, siginfo_t *, void *);
sigset_t sa_mask; //屏蔽
int sa_flags; //表示信号的行为:SA_SIGINFO表示能接受数据
void (*sa_restorer)(void); //废弃不用了
};
#include <unistd.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <fcntl.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <signal.h>
#define ERR_EXIT(m) \
do \
{ \
perror(m); \
exit(EXIT_FAILURE); \
} while(0)
void handler(int sig);
int main(int argc, char *argv[])
{
struct sigaction act;
act.sa_handler = handler;
sigemptyset(&act.sa_mask);
act.sa_flags = 0;
//因为不关心SIGINT上一次的struct sigaction所以,oact为NULL
//与signal(handler,SIGINT)相同
if (sigaction(SIGINT, &act, NULL) < 0)
ERR_EXIT("sigaction error\n");
for (;;)
pause();
return 0;
}
void handler(int sig)
{
printf("recv a sig=%d\n", sig);
}
sa_mask指定在信号处理程序执行过程中,哪些信号应当被阻塞。缺省情况下当前信号本身被阻塞,防止信号的嵌套发送,除非指定SA_NODEFER或者SA_NOMASK标志位,处理程序执行完后,被阻塞的信号开始执行。
注:请注意sa_mask指定的信号阻塞的前提条件,是在由sigaction()安装信号的处理函数执行过程中由sa_mask指定的信号才被阻塞。
//利用sigaction实现signal
#include <unistd.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <fcntl.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <signal.h>
#define ERR_EXIT(m) \
do \
{ \
perror(m); \
exit(EXIT_FAILURE); \
} while(0)
void handler(int sig);
__sighandler_t my_signal(int sig, __sighandler_t handler);
int main(int argc, char *argv[])
{
my_signal(SIGINT, handler);
for (;;)
pause();
return 0;
}
__sighandler_t my_signal(int sig, __sighandler_t handler)
{
struct sigaction act;
struct sigaction oldact;
act.sa_handler = handler;
sigemptyset(&act.sa_mask);
act.sa_flags = 0;
if (sigaction(sig, &act, &oldact) < 0)
return SIG_ERR;
return oldact.sa_handler;
}
void handler(int sig)
{
printf("recv a sig=%d\n", sig);
}
验证sigaction.sa_mask效果
#include <unistd.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <fcntl.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <signal.h>
#define ERR_EXIT(m) \
do \
{ \
perror(m); \
exit(EXIT_FAILURE); \
} while(0)
void handler(int sig);
int main(int argc, char *argv[])
{
struct sigaction act;
act.sa_handler = handler;
sigemptyset(&act.sa_mask);
sigaddset(&act.sa_mask, SIGQUIT); //屏蔽SIGQUIT
act.sa_flags = 0;
if (sigaction(SIGINT, &act, NULL) < 0)
ERR_EXIT("sigaction error");
struct sigaction act2;
act2.sa_handler = handler;
sigemptyset(&act2.sa_mask);
act2.sa_flags = 0;
if (sigaction(SIGQUIT, &act2, NULL) < 0)
ERR_EXIT("sigaction error");
for (;;)
pause();
return 0;
}
void handler(int sig)
{
if(sig == SIGINT){
printf("recv a SIGINT signal\n");
sleep(5);
}
if (sig == SIGQUIT)
{
printf("recv a SIGQUIT signal\n");
}
}
可知,安装信号SIGINT时,将SIGQUIT加入到sa_mask阻塞集中,则当SIGINT信号正在执行处理函数时,SIGQUIT信号将被阻塞,只有当SIGINT信号处理函数执行完后才解除对SIGQUIT信号的阻塞,由于SIGQUIT是不可靠信号,不支持排队,所以只递达一次。
signal函数重定向信号的处理函数,默认为阻塞(BLOCK)模式,就是说在运行一个信号的处理函数时,再收到同样的信号,将直接被阻塞,而不是再次立即调用处理函数(不允许中断嵌套);直到这一次的信号处理函数调用完成之后,未决的被阻赛的中断会继续被发送给进程,从而进程可以再次调用信号处理函数。
sigaction.sa_mask可以设定调用信号处理函数时需要阻塞的信号;sigaction.sa_flags默认为0,效果跟signal类似,阻塞本身信号;如果设置为SA_NODEFER,那么在运行信号的处理函数时再次收到当前的信号,也会触发新处理函数调用,这些处理函数的通过递归的方式调用(允许中断嵌套)。
sigaction.sa_mask表示在执行handler 的时候, 如果此时进程收到了sa_mask所包含的信号, 则这些信号将不会被响应, 直到handler函数执行完毕。sigprocmask不同是: sigprocmask 是指定该进程的信号屏蔽字,屏蔽相应的信号,还没有进入处理函数就已经屏蔽
sigqueue
#include <signal.h>
int sigqueue(pid_t pid, int sig, const union sigval value);
sigqueue的第一个参数是指定接收信号的进程id,第二个参数确定即将发送的信号,第三个参数是一个联合数据结构union sigval,指定了信号传递的参数,即通常所说的4字节值。返回值成功返回0,失败返回-1
sigqueue()比kill()传递了更多的附加信息,但sigqueue()只能向一个进程发送信号,而不能发送信号给一个进程组。
typedef union sigval{
int sival_int;
void *sival_ptr;
} sigval_t;
//发送端
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
#include <stdlib.h>
int main(int argc, char **argv)
{
if(argc != 2){
fprintf(stderr,"usage:%s pid\n",argv[0]);
exit(EXIT_FAILURE);
}
pid_t pid = atoi(argv[1]);
sleep(2);
union sigval mysigval;
mysigval.sival_int = 100;
printf("sending SIGINT signal to %d......\n",pid);
if(sigqueue(pid,SIGINT,mysigval) == -1){
perror("sigqueue error");
exit(EXIT_FAILURE);
}
return 0;
}
//接收端
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
#include <stdlib.h>
void sighandler(int signo, siginfo_t *info,void *ctx);
//给自身传递信息
int main(void)
{
struct sigaction act;
act.sa_sigaction = sighandler;
sigemptyset(&act.sa_mask);
act.sa_flags = SA_SIGINFO;//信息传递开关
if(sigaction(SIGINT,&act,NULL) == -1){
perror("sigaction error");
exit(EXIT_FAILURE);
}
for(; ;){
printf("waiting a SIGINT signal....\n");
pause();
}
return 0;
}
void sighandler(int signo, siginfo_t *info,void *ctx)
{
//以下两种方式都能获得sigqueue发来的数据
printf("receive the data from siqueue by info->si_int is %d\n",info->si_int);
printf("receive the data from siqueue by info->si_value.sival_int is %d\n",info->si_value.sival_int);
}
发送端发送一个SIGINT信号,接收端接收到信号,取出接收到的信号信息siginfo
也可以给自身发送信号
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
#include <stdlib.h>
void sighandler(int signo, siginfo_t *info,void *ctx);
//给自身传递信息
int main(void)
{
struct sigaction act;
act.sa_sigaction = sighandler;
sigemptyset(&act.sa_mask);
act.sa_flags = SA_SIGINFO;//信息传递开关
if(sigaction(SIGINT,&act,NULL) == -1){
perror("sigaction error");
exit(EXIT_FAILURE);
}
sleep(2);
union sigval mysigval;
mysigval.sival_int = 100;
if(sigqueue(getpid(),SIGINT,mysigval) == -1){
perror("sigqueue error");
exit(EXIT_FAILURE);
}
return 0;
}
void sighandler(int signo, siginfo_t *info,void *ctx)
{
//以下两种方式都能获得sigqueue发来的数据
printf("receive the data from siqueue by info->si_int is %d\n",info->si_int);
printf("receive the data from siqueue by info->si_value.sival_int is %d\n",info->si_value.sival_int);
}
注意发送的是可靠信号的话支持排队,不可靠信号不支持排队,会丢失