sigaction():信号处理函数 可以携带信息~
头文件 #include <signal.h>
int sigaction(int signum,const struct sigaction *act, struct sigaction *oldact));
一个参数为信号的值,可以为除SIGKILL及SIGSTOP外的任何一个特定有效的信号(为这两个信号定义自己的处理函数,将导致信号安装错误)。
第二个参数是指向结构sigaction的一个实例的指针,在结构sigaction的实例中,指定了对特定信号的处理,可以为空,进程会以缺省方式对信号处理;
第三个参数oldact指向的对象用来保存原来对相应信号的处理,可指定oldact为NULL。
如果把第二、第三个参数都设为NULL,那么该函数可用于检查信号的有效性。
struct sigaction { union { __sighandler_t _sa_handler; void (*_sa_sigaction)(int, struct siginfo *, void *); } _u sigset_t sa_mask; unsigned long sa_flags; void (*sa_restorer)(void); }
1、联合数据结构中union的两个元素_sa_handler以及*_sa_sigaction指定信号关联函数,
由_sa_handler指定的处理函数只有一个参数,即信号值,所以信号不能传递除信号值之外的任何信息;
由_sa_sigaction是指定的信号处理函数带有三个参数,是为实时信号而设的(当然同样支持非实时信号),它指定一个3参数信号处理函数。第一个参数为信号值,第三个参数没有使用(posix没有规范使用该参数的标准),第二个参数是指向siginfo_t结构的指针,结构中包含信号携带的数据值
typedef struct { int si_signo; /*信号值,对所有信号有意义*/ int si_errno; /*errno值,对所有信号有意义*/ int si_code; /*信号产生的原因,对所有信号有意义*/ union { sigval si_value; } }siginfo_t; //结构的第四个域同样为一个联合数据结构: union sigval { int si_int; void *si_ptr; }
深深的不能理解为什么结构体中的第四个变量又多次一举声明为sigval?
像下面这样不是更好?是不是因为处理的信号不同,结构体里的值也会发生变化?
// 自己理解 typedef struct { int si_signo; /*信号值,对所有信号有意义*/ int si_errno; /*errno值,对所有信号有意义*/ int si_code; /*信号产生的原因,对所有信号有意义*/ union { int si_int; void *si_ptr; } }siginfo_t;//
接受信号例子:
#include <stdio.h> #include <stdlib.h> #include <signal.h> #include <sys/types.h> #include <unistd.h> void new_op(int,siginfo_t*, void*); int main(int argc,char **argv) { struct sigaction act;// 创建sigaction结构体 int sig; pid_t pid; pid=getpid(); printf("PID is %d\n", (int)pid); sig=atoi(argv[1]); //输入时第一个参数 信号的值 (kill -l) 可以查看所有信号 sigemptyset(&act.sa_mask); act.sa_sigaction=new_op;//处理函数 act.sa_flags=SA_SIGINFO;//标志 if(sigaction(sig,&act,NULL)<0) { printf("install sigal error\n"); exit(0); } while(1) { sleep(2); printf("wait for the signal\n"); } } void new_op(int signum,siginfo_t *info,void *myact) { printf("the int value is %d \n",info->si_int); }
sa_sigaction函数指针指向了处理函数new_op,在接收到规定信号,执行相应的new_op 操作,并将获得的值(info->si_int)显示出来
也就是说在编程中,对于sigaction()特定信号的处理,只需要将指针指向sa_sigaction 指向处理函数就行,剩下数据的接受可在处理函数参数 siginfo_t 中直接操作
此时的new_op 更像一个中断函数
@ubuntu:~/Desktop$ ./sig1 50 //输入参数
PID is 3344 // 结果显示
wait for the signal
wait for the signal
wait for the signal
wait for the signal
the int value is 8
wait for the signal
wait for the signal
wait for the signal
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
发送信号例子:
系统调用sigqueue发送信号时,sigqueue的第三个参数就是sigval联合数据结构,当调用sigqueue时,该数据结构中的数据就将拷贝到信号处理函数的第二个参数中。
#include <stdio.h> #include <stdlib.h> #include <signal.h> #include <sys/time.h> #include <unistd.h> #include <sys/types.h> // sigqueue 发送信号信息,带sival_int的信息 main(int argc,char **argv) { pid_t pid; int signum; union sigval mysigval; signum=atoi(argv[1]); //要发送信号的信号值 pid=(pid_t)atoi(argv[2]);//发送进程的pid mysigval.sival_int=8;//不代表具体含义,只用于说明问题 if(sigqueue(pid, signum, mysigval)==-1) { printf("send error\n"); exit(0); } sleep(2); }@ubuntu:~/Desktop$ ./sig2 50 3344 // 输入参数
程序运行次序 先运行sig1 使其不断的等待接收 再运行sig2 并在后面加入信号值为50 进程号(pid)为3344参数
结果解释:当sig1 不断等待时,如果sig2发送的信号到达,就显示the int value is 8 表示已经接收到信号
二。 signal()
#include <signal.h>
void (*signal( int signum, void(*handler) (int) ))(int);
第一个参数指定信号的值,第二个参数指定针对前面信号值的处理
如果signal()调用成功,返回最后一次为安装信号signum而调用signal()时的handler值;失败则返回SIG_ERR。
Linux中signal 和signalaction 的区别就在是否能携带信号信息,在Uinx中貌似还有区别
三.实时信号 非实时信号
// 这个例子主要是对于实时信号和非实时信号阻塞的验证 // 利用另一个终端连续发送 kill -SIGRTMIN 进程号 可验证实时信号是可靠的,不会丢失 // 利用在本终端连续发送ctrl+c可发现最终是受到一个信号! 即信号不可靠 //方法:先将两个信号屏蔽,并sleep一会儿 ,这期间连续发送信号~ #include <stdio.h> #include <string.h> #include <signal.h> #include <unistd.h> void sig_handler(int,siginfo_t*,void*); int main(int argc,char**argv) { struct sigaction act; sigset_t newmask, oldmask; int rc; sigemptyset(&newmask); sigaddset(&newmask, SIGINT); //非实时信号 sigaddset(&newmask, SIGRTMIN); //实时信号 sigprocmask(SIG_BLOCK, &newmask, &oldmask); //屏蔽信号,使其不会立即执行信号的动作 act.sa_sigaction = sig_handler; act.sa_flags = SA_SIGINFO; if(sigaction(SIGINT, &act, NULL) < 0) { printf("install sigal error\n"); } if(sigaction(SIGRTMIN, &act, NULL) < 0) { printf("install sigal error\n"); } printf("pid = %d\n", getpid()); sleep(20); // 休眠中 ,发送过来的信号会排队 sigprocmask(SIG_SETMASK, &oldmask, NULL); //打开屏蔽,信号执行相应的操作 return 0; } void sig_handler(int signum,siginfo_t *info,void *myact) { if(signum == SIGINT) printf("Got a common signal\n"); else printf("Got a real time signal\n"); }
非实时信号都不支持排队,都是不可靠信号;实时信号都支持排队,都是可靠信号。
四。信号集合
信号集的数据类型
typedef struct {
unsigned long sig[_NSIG_WORDS];
} sigset_t
信号集用来描述信号的集合,linux所支持的所有信号可以全部或部分的出现在信号集中,主要与信号阻塞相关函数配合使用。下面是为信号集操作定义的相关函数:
#include <signal.h> int sigemptyset(sigset_t *set); int sigfillset(sigset_t *set); int sigaddset(sigset_t *set, int signum) int sigdelset(sigset_t *set, int signum); int sigismember(const sigset_t *set, int signum); sigemptyset(sigset_t *set)初始化由set指定的信号集,信号集里面的所有信号被清空; sigfillset(sigset_t *set)调用该函数后,set指向的信号集中将包含linux支持的64种信号; sigaddset(sigset_t *set, int signum)在set指向的信号集中加入signum信号; sigdelset(sigset_t *set, int signum)在set指向的信号集中删除signum信号; sigismember(const sigset_t *set, int signum)判定信号signum是否在set指向的信号集中。