一个利用信号捕获实现循环定时器代码阅读笔记


看了一份利用捕获信号的方式,实现循环定时器的代码,重新复习了一下linux下timer编程和signal编程。

下面对几个函数进行注释说明,便于后面查看。

首先是创建定时器的函数:


struct itimerspec tim;
timer_t realTimer;
struct sigevent event;

1.timer_create(CLOCK_REALTIME, &event, &realTimer)
这个函数就是创建一个定时器,与系统定时器相关,设置基于CLOCK_REALTIME的定时器,就是和系统真实时间一致。当定时器到时,内核发出设置的信号,我们就捕获这个信号进行处理。

event.sigev_notify = SIGEV_SIGNAL;
event.sigev_signo = SIGRTMAX;
这两个值就是设置了信号的行为和信号值


2.clock_gettime(CLOCK_REALTIME,&(tim.it_value));

这个函数就是获取当前的系统时间值

tim.it_value.tv_sec += 10;

tim.it_interval.tv_sec = 0;
tim.it_interval.tv_nsec = 0;

获取时间后,设置tv_sec的值,+10 就是起一个10s后到期的定时器


3.timer_settime(realTimer,TIMER_ABSTIME,&tim,0)

这个函数是最后一步,设置前面创建的定时器,指定过期时间。


原型是int timer_settime (timer_t timerid,int flags,const struct itimerspec *value,struct itimerspec *ovalue);

struct itimerspec {
struct timespec it_interval; /* next value */
struct timespec it_value; /* current value */
};

it_value上面得知是指定的过期时间,当定时器过期时将用it_interval的值更新it_value,如果it_interval是0,定时器在过期后停止运行。


flags是TIMER_ABSTIME,指value指定的是绝对时间,这里理解成和realtimer的绝对时间,realtimer是固定不变的(程序起来时的时间)

 

这样一个定时器就创建好了,不过这时候只是一个一次性定时器,并且,到期后,是系统捕获的信号进行默认处理的,程序会直接退出,下面讲信号的捕获。


首先,内核处理信号时有三种方式:
一,忽略信号
不采取任何操作。

二,捕获并处理信号
内核会跳到以前注册过的信号处理函数,完成函数调用后,返回捕获信号的地方继续执行

三,执行默认操作
一般是终止程序

 


这里我们需要采用第二种方式,因为定时器到期后我们希望进行处理,并创建新的定时器,实现循环定时器。


希望捕获信号有三种方式

1,用sighandler_t signal (int signo, sighandler_t
handler);函数
注册处理函数,指定需要捕获的信号值

当收到信号时,就会调用处理函数进行处理。


2.int sigaction (int signo,const struct sigaction *act,struct sigaction *oldact);

POSIX定义了sigaction系统调用,它提供更强大的信号管理能力,可以用来阻塞特定的信号接收,防止信号的漏收。

 

3.sigemptyset(),sigaddset(),pthread_sigmask(),sigwait()

event.sigev_notify = SIGEV_SIGNAL;
event.sigev_signo = SIGRTMAX;

if (sigemptyset(&sigmask) == -1 || sigaddset(&sigmask,SIGRTMAX) == -1)
{
printf("set sig error\n");
return 0;
}

pthread_sigmask(SIG_BLOCK,&sigmask,NULL);

for(;;)
{
if (sigwait(&sigmask,&sig) == -1)
{
printf("end\n");
return;
}


.....
//创建新的定时器
tim.it_value.tv_sec += 5;
tim.it_value.tv_nsec = 0;

if (timer_settime(realTimer,TIMER_ABSTIME,&tim,0) == -1)
{
printf("timer set error\n");

return 0;
}
}


第三种方式是利用sigaddset将给的的信号值放入信号集,然后调用pthread_sigmask(SIG_SETMASK,&sigmask,NULL);

 

pthread_sigmask函数的用法是:在多线程的程序里,希望只在主线程中处理信号

pthread_sigmask,第一个参数取值可以是:
SIG_BLOCK: 结果集是当前集合参数集的并集
SIG_UNBLOCK: 结果集是当前集合参数集的差集
SIG_SETMASK: 结果集是由参数集指向的集


sigwait 暂定调用线程, 直到 set 中定义的某个信号递达调用线程.
sigwait 所等待的信号必须在所有线程中阻塞, 而不仅仅是调用线程. 在多线程的程序里,希望只在主线程中处理信号,可以使用


摘录网上一位兄台的笔记
“在多线程代码中,总是使用sigwait或者sigwaitinfo或者sigtimedwait等函数来处理信号。
而不是signal或者sigaction等函数。因为在一个线程中调用signal或者sigaction等函数会改变所有线程中的信号处理函数,而不是仅仅改变调用signal/sigaction的那个线程的信号处理函数。”

“pthread_sigmask函数:
每个线程均有自己的信号屏蔽集(信号掩码),可以使用pthread_sigmask函数来屏蔽某个线程对某些信号的
响应处理,仅留下需要处理该信号的线程来处理指定的信号。实现方式是:利用线程信号屏蔽集的继承关系
(在主进程中对sigmask进行设置后,主进程创建出来的线程将继承主进程的掩码)”


总的来说就是利用pthread_sigmask,阻塞需要捕获的信号,sigwait监听该信号,当信号发生时,进行下面的处理。

因为该代码运行在多线程,所以采用了第三种方式,以前只接触过前两种,这次又学习到了。

 

posted on 2016-02-26 16:56    阅读(703)  评论(0编辑  收藏  举报

导航