调函数模式
#include<time.h>
#include<stdio.h>
#include<signal.h>//必须,一些定义在这里
#include<unistd.h>
#include<string.h>
//#include <sys/time.h>
volatile int a=0;
int b=0;
void timerOutFun(union sigval v)//和sigev_notify_function格式一致,带有参数,参数表示信号编号
{
printf("interrupt happened!\n");
b=v.sival_int;
a=1;
}
/*
struct sigevent
{
int sigev_notify; //notification type,SIGEV_THREAD触发回调函数,SIGEV_NONE无,SIGEV_SIGNAL触发信号
int sigev_signo; //signal number,指定触发什么信号,如果指定sigev_notify = SIGEV_SIGNAL。
union sigval
void (*sigev_notify_function)(union sigval);
pthread_attr_t *sigev_notify_attributes;//回调函数的属性。
}
union sigval
{//共同体,用于回调函数
int sival_int; //integer value
void *sival_ptr; //pointer value
}
*/int main()
{
int temp;
timer_t t_id;
struct sigevent sevt;
struct itimerspec tsp;
memset(&sevt, 0, sizeof(sevt));//必须,否则不能进中断
sevt.sigev_notify = SIGEV_THREAD;//define in signal.h,SIGEV_NONE无,SIGEV_SIGNAL将发出sevt.sigev_signo信号(用于信号与事件的结合)
sevt.sigev_notify_function = timerOutFun;//指定函数
sevt.sigev_value.sival_ptr = &t_id;// 用的指针,虽然t_id在timer_create才会填其内容,但是指针的内容是可变的。区分是哪个定时器产生了信号。
printf("t_id=%d\n",t_id);
if((temp = timer_create(CLOCK_REALTIME, &sevt, &t_id)) == (-1))//0正常
{
printf("create timer failed!%d\n",temp);
return 0;
}
printf("create timer succed! ID=%d,create return=%d\n", t_id,temp);
tsp.it_interval.tv_sec=1;//这是重装区,为0不会再次中断
tsp.it_interval.tv_nsec=0;
tsp.it_value.tv_sec=1;//计数器
tsp.it_value.tv_nsec=0;
if(timer_settime(t_id, 0, &tsp, NULL))//0正常
{
printf("set time fail\n");
return 0;
}
printf("set time ok\n");
while(1)
{
// printf("a=%d\n", a);
usleep(500000);
;
}
printf("timer break!b=%d\n", b);
timer_delete(t_id);
return 1;
}
编译:g++ -o xxx xxx.cpp -lrt//需要将timer库添加进来
信号模式:
#include<time.h>
#include<unistd.h>
#include<signal.h>
#include<iostream>
#include<stdio.h>
using namespace std;
//函数原型:void (*signal(int signum,void(* handler)(int)))(int);
//或者:typedef void (*sig_t)( int );
//sig_t signal(int signum,sig_t handler);
void timefun(int a)//因为void(* handler)(int)带来一个参数,因此这里需要添加一个int a。参数表示信号编号,即:SIGRTMIN = 34,参数为34
{
printf("break%d\n",a);
}
int main()
{
int flag;
timer_t tid;
sigevent se;
itimerspec ts;
signal(SIGRTMIN, timefun);//绑定
se.sigev_notify = SIGEV_SIGNAL;//中断触发的时候发出se.sigev_signo信号带动timefun函数
se.sigev_signo = SIGRTMIN;
//se.sigev_value.sival_ptr = &tid;//time out 只会传送SIGRTMIN的编号去信号函数。
flag = timer_create(CLOCK_REALTIME, &se, &tid);
if(flag)
{
printf("create error!%d\n", flag);
return 0;
}
ts.it_interval.tv_sec=1;
ts.it_interval.tv_nsec=0;
ts.it_value.tv_sec=1;
ts.it_value.tv_nsec=0;
flag=timer_settime(tid, 0, &ts, NULL);
if(flag)
{
printf("settime error!%d\n", flag);
return 0;
}
while(1)
{
;
}
timer_delete(tid);
return 0;
}
signal或者sigaction的回调函数为SIG_IGN,表示信号忽略
现在一般不用signal来时间信号和事件了。而用sigaction.
下面所指的signal都是指以前的older signal函数,现在大多系统都用sigaction重新实现了signal函数
1、signal在调用handler之前先把信号的handler指针恢复;sigaction调用之后不会恢复handler指针,直到再次调用sigaction修改handler指针。
:这样,(1)signal就会丢失信号,而且不能处理重复的信号,而sigaction就可以。因为signal在得到信号和调用 handler之间有个时间把handler恢复了,这样再次接收到此信号就会执行默认的handler。(虽然有些调用,在handler的以开头再次置handler,这样只能保证丢信号的概率降低,但是不能保证所有的信号都能正确处理)
2、signal在调用过程不支持信号block;sigaction调用后在handler调用之前会把屏蔽信号(屏蔽信号中自动默认包含传送的该信号)加入信号中,handler调用后会自动恢复信号到原先的值。
(2)signal处理过程中就不能提供阻塞某些信号的功能,sigaction就可以阻指定的信号和本身处理的信号,直到handler处理结束。这样就可以阻塞本身处理的信号,到handler结束就可以再次接受重复的信号。(如果系统同时发出来2个信号,sigaction都可以处理到,而signal不能)
3、sigaction提供了比signal多的多的功能,可以参考man
struct sigaction {
union{
void (*sa_handler)(int);//回调函数,共同体
void (*sa_sigaction)(int, siginfo_t *, void *);//回调函数,共同体
}
sigset_t sa_mask;//sa_mask 用来设置在处理该信号时暂时将sa_mask 指定的信号集搁置。(这里并没有屏蔽信号集,不像sigprocmask函数信号集被屏蔽(可以查询),在处理信号函数的时候等待函数掉完成。我用sigfillset后用sigaction还是可以进中断函数。)
int sa_flags;//默认用sa_handler,=SA_SIGINFO调用sa_sigaction
void (*sa_restorer)(void);//不用
}
xxx.sa_flags=SA_SIGINFO;//表示使用sa_sigaction指示的函数,处理完恢复默认,不阻塞处理过程中到达下在被处理的信号
///
int sigaction(int signum,const struct sigaction *act ,struct sigaction *oldact);
sigemptyset(&act. sa_mask); //清空此信号集。说明不会阻塞信号
请注意sa_mask指定的信号屏蔽的前提条件,是在由sigaction()安装信号的处理函数执行过程中由sa_mask指定的信号才被屏蔽。
void (*sa_sigaction)(int, siginfo_t *, void *);//回调函数,共同体
我们在设计定时器的时候:sigevent te.sigev_value.sival_ptr = timerID;,可以在回调函数使用si_value的内容查看ID。
typedef struct siginfo_t{
int si_signo;//信号编号
int si_errno;//如果为非零值则错误代码与之关联
int si_code;//说明进程如何接收信号以及从何处收到
pid_t si_pid;//适用于SIGCHLD,代表被终止进程的PID
pid_t si_uid;//适用于SIGCHLD,代表被终止进程所拥有进程的UID
int si_status;//适用于SIGCHLD,代表被终止进程的状态
clock_t si_utime;//适用于SIGCHLD,代表被终止进程所消耗的用户时间
clock_t si_stime;//适用于SIGCHLD,代表被终止进程所消耗系统的时间
sigval_t si_value;//siginfo_t->si_value.sival_ptr用来查询我们设置的sival_ptr
int si_int;
void * si_ptr;
void* si_addr;
int si_band;
int si_fd;
};
sigaction用法:
void SignalHandlerRr(__sighandler_t sighandler, int signum)
{
struct sigaction action;//定义结构体
action.sa_handler = sighandler;//中断函数
sigemptyset(&action.sa_mask);//没有阻塞信号集
action.sa_flags = 0;//0表示使用void (*sa_handler)(int);,而默认用sa_handler,=SA_SIGINFO调用sa_sigaction
sigaction(signum, &action, NULL);//将信号和事件绑定(他们叫安装?)
}
int SetTimer(timer_t *timerID, int expireMS, bool periodic)
{
if (( NULL==timerID ) || (*timerID != 0))
return -1;
struct sigevent te;
struct itimerspec its;
struct sigaction sa;
int sigNo = SIGRTMIN;
/* Set up signal handler. */
sa.sa_flags = SA_SIGINFO;
sa.sa_sigaction = timerHandler;
sigemptyset(&sa.sa_mask);
if (sigaction(sigNo, &sa, NULL) == -1)//当中断发生时,主函数有些阻塞函数会被退出。暂不解
return -1;
/* Set and enable alarm */
te.sigev_notify = SIGEV_SIGNAL;
te.sigev_signo = sigNo;
te.sigev_value.sival_ptr = timerID;//在信号触发函数内使用siginfo_t->si_value.sival_ptr查看ID
if(timer_create(CLOCK_REALTIME, &te, timerID) == -1)
return -1;
if (periodic) {
its.it_interval.tv_sec = expireMS;
} else {
its.it_interval.tv_sec = 0;
}
its.it_interval.tv_nsec = 0;//intervalMS * 1000000;
its.it_value.tv_sec = expireMS;//0;
its.it_value.tv_nsec = 0;//expireMS * 1000000;
timer_settime(*timerID, 0, &its, NULL);
return(0);
}
sigaction不能捕获SIGKILL和SIGSTOP
关于信号集:http://blog.sina.com.cn/s/blog_8b745a5f010145pb.html
1. 信号掩码——被阻塞的信号集
每个进程都有一个用来描述哪些信号传送来将被阻塞的信号集,如果某种信号在某个进程的阻塞信号集中,则传送到该进程的此种信号将会被阻塞。当前被进程阻塞的信号集也叫信号掩码,类型为sigset_t。每个进程都有自己的信号掩码,且创建子进程时,子进程会继承父进程的信号掩码。
2. 信号阻塞和忽略的区别
阻塞的概念与忽略信号是不同的:操作系统在信号被进程解除阻塞之前不会将信号传递出去,被阻塞的信号也不会影响进程的行为,信号只是暂时被阻止传递;当进程忽略一个信号时,信号会被传递出去,但进程将信号丢弃。
3. 信号集的操作
信号集可以由以下几个函数操作:
int sigemptyset(sigset_t *set); //清空信号集
int sigfillset(sigset_t *set); //将所有信号填充进set中
int sigaddset(sigset_t *set, int signum); //往set中添加信号signum
int sigdelset(sigset_t *set, int signum); //从set中移除信号signum
int sigismember(const sigset_t *set, int signum);//判断signnum是不是包含在set中,在返回1,不在返回0,查询功能
初始化往往可以用sigemptyset()将信号集清空,再用sigaddset()向信号集中添加信号;或者可以使用sigfillset()将所有信号添加到信号集,再用sigdelset()将某信号从中删除掉。
4. sigprocmask()介绍
可以使用函数sigprocmask()来检查或者修改进程的信号掩码。函数信息如下:
#include
int sigprocmask ( int how, const sigset_t * set, sigset_t * oset );//oset不为空提取原有的集合,
参数how 是一个整数,说明信号掩码的修改方式:
SIG_BLOCK --- 将set指向的信号集中的信号添加到当前阻塞信号集中;即将set和原有信号集合合并
SIG_UNBLOCK --- 从当前阻塞信号集中移除set指向的信号集中的信号;将set集合从原有集合去除。
SIG_SETMASK --- 指定set所指向的信号集为当前阻塞信号集。set替换原有的集合
此外,如果参数set 为NULL, 说明不需要修改,如果old不为NULL,sigprocmask会将修改之前的信号集放在*old 之中返回;oset==NULL,提取以前集合。
EFAULT 参数set,oldset指针地址无法存取。
EINTR 此调用被中断
5. int sigpending(sigset_t *set)sigpending用来获取被sigprocmask阻塞的信号。
struct sigaction {
union{
//sa_flags的SA_SIGINFO位=SA_SIGINFO,用sa_sigaction,不然用sa_handler
void (*sa_handler)(int);//回调函数,共同体
void (*sa_sigaction)(int, siginfo_t *, void *);//回调函数,共同体
}
sigset_t sa_mask;//sa_mask 用来设置在处理该信号时暂时将sa_mask 指定的信号集搁置。(这里并没有屏蔽信号集,不像sigprocmask函数信号集被屏蔽(可以查询),在处理信号函数的时候等待函数掉完成。我用sigfillset后用sigaction还是可以进中断函数。)
//SA_RESTART:触发中断函数时,如果主函数使用getchar时,没标志SA_RESTART,getchar将立即返回错误-1;标志该位getchar不会返回。
//(1)SA_RESETHAND---处理完毕要捕捉的信号后,将自动撤消信号处理函数的注册,即必须再重新注册信号处理函数,才能继续处理接下来产生的信号。
//(2)SA_NODEFER---在处理信号时,如果又发生了其它的信号,则立即进入其它信号的处理,等其它信号处理完毕后,再继续处理当前的信号,即递规地处理。如果sa_flags包含了该掩码,则结构体sigaction的sa_mask将无效;
//(3)SA_RESTART---如果在发生信号时,程序正阻塞在某个系统调用,例如调用read()函数,则在处理完毕信号后,接着从阻塞的系统返回。该掩码符合普通的程序处理流程,所以一般来说,应该设置该掩码,否则信号处理完后,阻塞的系统调用将会返回失败;
//(4)SA_SIGINFO---指示结构体的信号处理函数指针是哪个有效,如果sa_flags包含该掩码,则sa_sigactiion指针有效,否则是sa_handler指针有效。
int sa_flags;//默认用sa_handler,=SA_SIGINFO调用sa_sigaction
void (*sa_restorer)(void);//不用
}
需要注意的是:
函数sigprocmask是全程阻塞,在sigprocmask中设置了阻塞集合后,被阻塞的信号将不能再被信号处理函数捕捉,直到重新设置阻塞信号集合。而在sigaction()注册信号处理函数时,只是在处理捕捉的信号时阻塞信号集,处理完成后又可以接受信号集了。我用sigfillset后用sigaction还是可以进中断函数。