Linux 改进捕捉信号机制(sigaction,sigqueue)
sigaction函数 sigaction函数的功能是用于改变进程接收到特定信号后的行为。 int sigaction(int signum, const struct sigaction *act,struct sigaction *oldact); 参数 --第一个参数是信号的值,可以为除了SIGKILL及SIGSTOP外的任何一个特定有效的信号(因为这两个信号定义了自己的处理函数,将导致信号安装错误) --第二个参数是指向节后sigaction的一个实例的指针,在sigaction的实例中,指定了对特定信号的处理,可以为NULL,进程会以缺省方式对信号处理 --第三个参数oldact指向的对象用来保存原来对相应信号的处理,可以为NULL 返回值:函数成功返回0,失败返回-1。
sigaction函数检查或修改与指定信号相关联的处理动作,该函数取代了signal函数。
因为signal函数在信号未决时接收信号可能出现问题,所以使用sigaction更安全。
sigaction结构体 struct sigaction { void (*sa_handler)(int);//信号处理程序 不接受额外数据 void (*sa_sigaction)(int, siginfo_t *, void *);//信号处理程序,能接受额外数据,可以和sigqueue配合使用 sigset_t sa_mask; int sa_flags;//影响信号的行为SA_SIGINFO表示能接受数据 void (*sa_restorer)(void);//废弃 }; --第二个参数最为重要,其中包含了对指定信号的处理、信号所传递的信息、信号处理函数执行过程中赢屏蔽掉哪些函数等等。 --回调函数sa_handler、sa_sigaction只能任选其一
//捕捉信号 #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <errno.h> #include <signal.h> void catch_signal(int sign) { switch(sign) { case SIGINT: //SIGINT默认行为是退出进程 printf("SIGINT signal\n"); exit(0); break; case SIGALRM: //SIGALRM默认行为是退出进程 printf("SIGALRM signal\n"); break; case SIGKILL: printf("SIGKILL signal\n"); break; } } //建议使用封装之后的mysignal int mysignal(int sign,void (*func)(int)) { struct sigaction act,oact; //传入回调函数 act.sa_handler=func; //将act的属性sa_mask设置一个初始值 sigemptyset(&act.sa_mask); act.sa_flags=0; return sigaction(sign,&act,&oact); } int main(int arg, char *args[]) { mysignal(SIGINT,catch_signal); mysignal(SIGALRM,catch_signal); mysignal(SIGKILL,catch_signal); int i=0; while(1) { printf("hello god %d\n",i++); sleep(1); } return 0; }
sigqueue函数 --新的发送信号系统调用,主要是针对实时信号提出的支持信号带有参数,与sigaction()函数配合使用 --注意:和kill函数相比int kill(pid_t pid,int signo)多了参数 --原型 int sigqueue(pid_t pid,int signo,const union sigval value); --参数 sigqueue的第一个参数是指定接收信号的进程pid,第二个参数确定即将发送的信号,
第三个参数是一个联合数据结构union sigval,指定了信号传递的参数,即通常所说的4字节值。 --函数成功返回0,失败返回-1,并且更新errno --sigqueue()比kill()传递了更多的附加信息,但sigqueue()只能向一个进程发送信号,而不能发送信号给一个进程组 --union sigval联合体 typedef union sigval { int sival_int; void * sival_ptr; }sigval_t;
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <errno.h> #include <sys/types.h> #include <sys/wait.h> #include <signal.h> /* siginfo_t { int si_signo; // Signal number int si_errno; // An errno value int si_code; // Signal code int si_trapno; // Trap number that caused hardware-generated signal (unused on most architectures) pid_t si_pid; // Sending process ID uid_t si_uid; // Real user ID of sending process int si_status; // Exit value or signal clock_t si_utime; // User time consumed clock_t si_stime; // System time consumed sigval_t si_value; // Signal value int si_int; // POSIX.1b signal void *si_ptr; // POSIX.1b signal int si_overrun; // Timer overrun count; POSIX.1b timers int si_timerid; // Timer ID; POSIX.1b timers void *si_addr; // Memory location which caused fault int si_band; // Band event int si_fd; // File descriptor } */ void catch_signal(int signo,siginfo_t *resdata,void *unkonwp) { printf("signo=%d\n",signo); printf("return data :%d\n",resdata->si_value.sival_int); printf("second return data:%d\n",resdata->si_int); return ; } int main(int arg, char *args[]) { pid_t pid=0; pid=fork(); if(pid==-1) { printf("fork() failed! error message:%s\n",strerror(errno)); return -1; } if(pid==0) { printf("i am child!\n"); //等待父进程执行完信号安装 sleep(5); //向父进程发送带数据的信号 union sigval sigvalue; sigvalue.sival_int=222; //发送信号 if(sigqueue(getppid(),SIGINT,sigvalue)==-1) { printf("sigqueue() failed ! error message:%s\n",strerror(errno)); exit(0); } printf("子进程信号发送成功!\n"); exit(0); } if(pid>0) { printf("i am father!\n"); //安装信号 struct sigaction act; //初始化sa_mask sigemptyset(&act.sa_mask); act.sa_sigaction=catch_signal; //一旦使用了sa_sigaction属性,那么必须设置sa_flags属性的值为SA_SIGINFO act.sa_flags=SA_SIGINFO; //安装信号 if(sigaction(SIGINT,&act,NULL)!=0) { printf("sigaction() failed! \n"); return -1; } //等待子进程返回 int status=0; wait(&status); printf("game over!\n"); } return 0; }