函数介绍
- 包含头文件
<signal.h>
- 功能:sigaction函数用于改变进程接收到特定信号后的行为。
- 原型:
int sigaction(int signum,const struct sigaction *act, struct sigaction *old);
- 参数
- 该函数的第一个参数为信号的值,可以为除SIGKILL及SIGSTOP外的任何一 个特定有效的信号(为这两个信号定义自己的处理函数,将导致信号安装错误)
- 第二个参数是指向结构sigaction的一个实例的指针,在结构 sigaction的实例中,指定了对特定信号的处理,可以为空,进程会以缺省方式对信号处理
- 第三个参数oldact指向的对象用来保存原来对相应信号的处理,可指定oldact为NULL。
- 返回值:函数成功返回0,失败返回-1
关于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只能任选其一。
- 补充siginfo_t 结构体介绍
siginfo_t {
int si_signo; /* Signal number */
int si_errno; /* An errno value */
int si_code; /* Signal code */
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 */
void * si_addr; /* Memory location which caused fault */
int si_band; /* Band event */
int si_fd; /* File descriptor */
}
示例代码
- 最基本的用法
void handler(int sig)
{
if (sig == SIGINT)
{
printf("recv a sig=%d\n", sig);
}
}
int main()
{
struct sigaction act;
act.sa_handler = handler;
sigaction(SIGINT,&act,NULL);
while(1);
return 0;
}
- 利用sigaction实现my_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>
void handler(int sig)
{
printf("Aloha recv a sig=%d\n", sig);
}
/*打造一个和signal功能一样的函数*/
__sighandler_t my_signal(int sig, __sighandler_t handler)
{
struct sigaction act;
struct sigaction oldact;
/*在使用一个sigaction结构体之前,需要先对以下三个成员进行处理*/
act.sa_handler = handler;//指定新的处理函数
sigemptyset(&act.sa_mask);//设置信号屏蔽字
act.sa_flags = 0;
if (sigaction(sig, &act, &oldact) < 0)//注册信号,指定信号处理函数的同时备份之前的信号处理信息(包括之前该信号的处理函数)
return SIG_ERR;//注册失败--返回SID_ERR
return oldact.sa_handler;//注册成功--返回之前的信号处理函数
}
int main(int argc, char *argv[])
{
//模拟signal函数
my_signal(SIGINT, handler);
for (;;)
{
pause();
}
return 0;
}
sigqueue函数
功能:新的发送信号系统调用,主要是针对实时信号提出的支持信号带有参数,与函数sigaction()配合使用。
注意:和kill函数相比
int kill(pid_t pid, int siq)
多了参数原型:
int sigqueue(pid_t pid, int sig, const union sigval value);
- 参数
sigqueue的第1个参数是指定接收信号的进程id,第2个参数确定即将发送的信号,第3个参数是一个联合数据结构union sigval,指定了信号传递的参数,即通常所说的4字节值。 - 返回值成功返回0,失败返回-1
sigqueue()比kill()传递了更多的附加信息,但sigqueue()只能向一个进程发送信号,而不能发送信号给一个进程组。
- sigval联合体
typedef union sigval
{
int sival_int;
void *sival_ptr;
}sigval_t;
注意:
(1) 如果要让信号处理函数能够接收额外的数据,需要配合sigaction结构体中的sa_flags成员, SA_SIGINFO表示能接受数据。
(2) 信号处理函数的原型要使用void myHandle_forsigaction(int signum, siginfo_t *s_t, void *p)
,这个函数实在内核空间被调用的,所以形参也是在内核空间,同时,接收到的额外数据就放在siginfo_t 结构体的如下成员里。si_value和si_int是为了兼容性设置的,两个内容相同。si_ptr则对应额外数据结构体里面的void*指针部分。上述两个部分,在内核发送信号的时候,只能发送其中一部分,因为额外数据是一个union类型。
sigval_t si_value;
int si_int; /* POSIX.1b signal */
void * si_ptr; /* POSIX.1b 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>
void handler(int sig,siginfo_t *s_t,void *p)//能够接受额外数据的信号处理函数签名
{
int tmp = 0;
tmp = s_t->si_int; //si_int和si_value.sival_int是一样的--针对额外数据是int的时候。
printf("Aloha recv a sig=%d\tvar :%d\n and var is also: %d", sig,tmp,s_t->si_value.sival_int);
}
int main(int argc, char *argv[])
{
pid_t pid;
int ret = 0;
int i = 0;
union sigval mysigval;//用来存放额外数据
struct sigaction act;//用来注册信号
/*使用sigaction必须要初始化的三个成员*/
act.sa_sigaction = handler;//指定回调函数
act.sa_flags = SA_SIGINFO;//尤其重要--只有等于SA_SIGINFO,信号处理函数才能接受额外数据
sigemptyset(&act.sa_mask);//清空屏蔽字
if(sigaction(SIGINT,&act,NULL) < 0)//注册信号--指定毁掉函数
{
perror("sigaction error!\n");
exit(-1);
}
pid = fork();//创建子进程
if(-1 == pid)
{
perror("fork");
exit(-1);
}
else if(0 == pid)
{
mysigval.sival_int = 125;//设置要随着信号发送的额外数据
for(i = 0;i < 10;i++)//子进程发送十次信号--SIGINT是不可靠信号--传送有点慢
{
ret = sigqueue(getppid(),SIGINT,mysigval);//开始发送信号
if(ret != 0)//发送失败
{
perror("sigqueue");
exit(-1);
}
else{//返回0表示信号发送成功
printf("send ok!\n");
sleep(1);
}
}
}
else if(pid > 0)
{
while(1);//父进程死循环
}
return 0;
}
可靠信号和不可靠信号的区别及测试
- 可靠信号在遇到阻塞的时候需要缓存,所以缓冲区有上限,也就是发送可靠信号的数量是有限的,可用ulimit -a命令查看。
- 可靠信号会在解除阻塞以后全部抵达,但是不可靠信号只有最后一个会抵达!
#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>
/*TEST_SIG_LIMIT如果定义为1表示进行压力测试
如果定义位0表示进行可靠信号和不可靠信号的区别测试*/
#define TEST_SIG_LIMIT 0
#if (TEST_SIG_LIMIT==1)
#define MAX 1024
#else
#define MAX 1
#endif
void handler(int sig,siginfo_t *s_t,void *p)//能够接受额外数据的信号处理函数签名
{
int tmp = 0;
sigset_t bset;
if(sig == SIGINT || sig == SIGRTMIN)
{
tmp = s_t->si_int; //si_int和si_value.sival_int是一样的--针对额外数据是int的时候。
printf("Aloha recv a sig=%d\tvar :%d\t and var is also: %d\n", sig,tmp,s_t->si_value.sival_int);
}
else if(sig == SIGUSR1)
{
printf("unblock\n");
/*阻塞实时信号和非实时信号*/
sigemptyset(&bset);
sigaddset(&bset,SIGINT);
sigaddset(&bset,SIGRTMIN);
sigprocmask(SIG_UNBLOCK,&bset,NULL);
}
else
{
printf("other signal\n");
}
}
void my_handler(int sig)
{
sigset_t bset;
if(sig == SIGINT || sig == SIGRTMIN)
{
printf("The sig num is : %d\n",sig);
}
else if(sig == SIGUSR1)
{
printf("unblock\n");
/*阻塞实时信号和非实时信号*/
sigemptyset(&bset);
sigaddset(&bset,SIGINT);
sigaddset(&bset,SIGRTMIN);
sigprocmask(SIG_UNBLOCK,&bset,NULL);
}
else
{
printf("other signal\n");
}
}
int main(int argc, char *argv[])
{
pid_t pid;
int ret = 0;
int i = 0;
struct sigaction act;//用来注册信号
sigset_t bset;
union sigval v;
/*使用sigaction必须要初始化的三个成员*/
//act.sa_handler = my_handler;//指定回调函数
act.sa_sigaction = handler;
act.sa_flags = SA_SIGINFO;//尤其重要--只有等于SA_SIGINFO,信号处理函数才能接受额外数据
sigemptyset(&act.sa_mask);//清空屏蔽字
if(sigaction(SIGINT,&act,NULL) < 0)//注册非实时信号
{
perror("sigaction error!\n");
exit(-1);
}
if(sigaction(SIGRTMIN,&act,NULL) < 0)//注册实时信号
{
perror("sigaction error!\n");
exit(-1);
}
if(sigaction(SIGUSR1,&act,NULL) < 0)//注册用户自定义信号SIGUSR1
{
perror("sigaction error!\n");
exit(-1);
}
/*阻塞实时信号和非实时信号*/
sigemptyset(&bset);
sigaddset(&bset,SIGINT);
sigaddset(&bset,SIGRTMIN);
sigprocmask(SIG_BLOCK,&bset,NULL);
pid = fork();//创建子进程
if(-1 == pid)
{
perror("fork");
exit(-1);
}
else if(0 == pid)
{
v.sival_int = 0;
for(i = 1;i < MAX*1024;i++)//发送三次不可靠信号
{
v.sival_int++;
ret = sigqueue(getppid(),SIGINT,v);
if(ret != 0)
{
printf("sigqueue SIGINT failed!..ret=%d\terrno=%d\tindex=%d\n",ret,errno,i);//不可靠信号不用缓存--无限制--不会出错/不会执行!
exit(-1);
}
else{
if(i % (1024*512) == 0)
printf("send SIGINT OK!--index:%d\n",i);
}
}
v.sival_int = 0;
for(i = 1;i < MAX*1024;i++)//发送三次可靠信号
{
v.sival_int++;
ret = sigqueue(getppid(),SIGRTMIN,v);
if(ret != 0)
{
printf("sigqueue SIGRTMIN failed!..ret=%d\terrno=%d\tindex=%d\n",ret,errno,i);//可靠信号需要缓存--所以有限制--ulimit -a命令查看--此时index即上限
exit(-1);
}
else{
if(i % 512 == 0)
printf("send OK!--index:%d\n",i);
}
}
ret = kill(getppid(),SIGUSR1);//发送自定义信号--解除阻塞
if(ret == -1)
{
printf("sigqueue failed!..%d\t%d\n",ret,errno);
exit(-1);
}
else if(ret == 0){
printf("send OK!\n");
}
}
else if(pid > 0)
{
while(1);//父进程死循环
}
return 0;
}