timer设计

调函数模式

#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   sigev_value;//signal value,回调函数的参数。

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和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阻塞的信号。

6.

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还是可以进中断函数。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值