在LINUX中经常要使用计时器,而在LINUX环境下使用计时器不像WINDOWS环境下那样一个SETTIMER()方便,主要有三种方式:使用SLEEP/USLEEP+单独线程;SETITMER加处理信号SIGALRM,或者是RTC机制。这里我讲到的是使用RTC机制实现计时器类。这种方法最为优越,它与传统意义上的SLEEP和SIGALRM信号是分离的,它的运行不受SLEEP的影响,而像SETITMER等都会受到SLEEP的影响,因为它们使用的是同一时钟。
以前用select实现的计时器类(http://hi.baidu.com/susdisk/blog/item/03f70d35e8e2e182a61e1288.html)其实并不是真正的计时器,它是一个循环,只是在处理完一次ONTIMER()事件后停下了一秒,然后再接着一次ONTIMER(),这其实并不是真正的计时器。真正的计时器应该是不管是否在处理ONTIMER()事件,它都会触发。
RTC(real-time clock)。现在可以使用LINUX下的RTC机制来编写计时器类,这个类是完全意义上的计时器,经过测试,也基本不占用cpu时间,因为它采用的是底层的硬件时钟,rtc的文档中说的很明白,它与系统时钟最大的区别在于即使它在机器耗能非常低的情况下,也会触发此时钟信号。它也与SLEEP、SETITIMER等函数是完全独立的,就是说,使用这个计时器类,你依然可以使用各种SLEEP函数等,互不影响,这一点我觉得是最重要的。
实现如下:
- CTimer.h:
- /*
- * CTimer.h
- *
- * Created on: 2009-7-13
- * Author: DEAN
- */
- //
- // This class provide a timer to finish some works.
- // Call StartTimer() to enable it and call StopTimer() to stop
- // it. The work you want to do should be written on OnTimer()
- // function. Call SetInterval(x) to set every x second to call
- // OnTimer() once.
- //
- #ifndef CTIMER_H_
- #define CTIMER_H_
- #include <sys/time.h>
- #include <linux/rtc.h>
- #include <sys/ioctl.h>
- #include <pthread.h>
- class CTimer
- {
- private:
- static CTimer *g_singleinstance;
- long m_second, m_counter;
- unsigned long m_data;
- int m_rtcfd;
- pthread_t thread_timer;
- static void *thread_stub(void *p)
- {
- (static_cast<CTimer*>(p))->thread_proc();
- }
- void *thread_proc();
- void OnTimer();
- protected:
- CTimer();
- public:
- virtual ~CTimer();
- static CTimer *Instance();
- void SetInterval(long second);
- void StartTimer();
- void StopTimer();
- };
- #endif /* CTIMER_H_ */
- CTimer.cpp:
- /*
- * CTimer.cpp
- *
- * Created on: 2009-7-13
- * Author: dean
- */
- #include "Timer.h"
- #include <iostream>
- #include <sys/time.h>
- #include <linux/rtc.h>
- #include <sys/ioctl.h>
- #include <sys/types.h>
- #include <sys/stat.h>
- #include <fcntl.h>
- #include <pthread.h>
- using namespace std;
- CTimer *CTimer::g_singleinstance = 0;
- CTimer::CTimer():
- m_second(2), m_counter(0)
- {
- //Init the rtc
- m_rtcfd = open("/dev/rtc", O_RDONLY);
- if(m_rtcfd < 0)
- {
- cout<<"TimerWarning: open /dev/rtc error..."<<endl;
- return;
- }
- if(ioctl(m_rtcfd, RTC_IRQP_SET, 2) < 0)
- {
- cout<<"TimerWarning: Set rtc request failed..."<<endl;
- close(m_rtcfd);
- return;
- }
- pthread_create(&thread_timer, NULL, thread_stub, this);
- }
- //private methods//
- void *CTimer::thread_proc()
- {
- int nfds;
- while(true)
- {
- read(m_rtcfd,&m_data,sizeof(unsigned long));
- ++m_counter;
- if (m_counter >= m_second)
- {
- OnTimer();
- m_counter = 0;
- }
- pthread_testcancel();
- }
- }
- void CTimer::OnTimer()
- {
- cout<<"Timer...."<<endl;
- }
- //public methods//
- CTimer::~CTimer()
- {
- pthread_cancel(thread_timer);
- pthread_join(thread_timer, NULL);
- close(m_rtcfd);
- }
- CTimer *CTimer::Instance()
- {
- if (g_singleinstance == 0)
- g_singleinstance = new CTimer;
- return g_singleinstance;
- }
- void CTimer::SetInterval(long second)
- {
- m_second = second * 2;
- }
- void CTimer::StartTimer()
- {
- if (!(m_rtcfd > 0))
- {
- cout<<"TimerWarning: rtcfd < 0...Start failed..."<<endl;
- return;
- }
- if(ioctl(m_rtcfd, RTC_PIE_ON, 0) < 0)
- {
- cout<<"TimerWarning: ioctl(RTC_PIE_ON) failed..."<<endl;
- close(m_rtcfd);
- return;
- }
- m_counter = 0;
- }
- void CTimer::StopTimer()
- {
- if (!(m_rtcfd > 0))
- {
- cout<<"TimerWarning: rtcfd < 0...Stop failed..."<<endl;
- return;
- }
- if(ioctl(m_rtcfd, RTC_PIE_OFF, 0) < 0)
- {
- cout<<"TimerWarning: ioctl(RTC_PIE_ON) failed..."<<endl;
- close(m_rtcfd);
- return;
- }
- }
- linux 下 timer机制 标准实现,一般是用 sigalarm + setitimer() 来实现的,但这样就与 select/epoll 等逻辑有所冲突,我希望所有 event 的通知逻辑都从 select/epoll 中触发。(FreeBSD 中 kqueue 默认就有 FILTER_TIMER,多好)
- ps. /dev/rtc 只能被 open() 一次,因此上面希望与 epoll 合并的想法基本不可能了~
- 下面是通过 /dev/rtc (real-time clock) 硬件时钟实现的 timer机制。:-)
- 其中 ioctl(fd, RTC_IRQP_SET, 4) 的第三个参数只能是 2, 4, 8, 16, 32 之一,表示 xx Hz。
- -------------------------------------------------
- #include <linux/rtc.h>
- #include <sys/ioctl.h>
- #include <sys/time.h>
- #include <sys/types.h>
- #include <fcntl.h>
- #include <stdio.h>
- #include <unistd.h>
- #include <errno.h>
- #include <time.h>
- #include <err.h>
- int main(void)
- {
- unsigned long i = 0;
- unsigned long data = 0;
- int fd = open("/dev/rtc", O_RDONLY);
- if ( fd < 0 )
- errx(1, "open() fail");
- /* set the freq as 4Hz */
- if ( ioctl(fd, RTC_IRQP_SET, 4) < 0 )
- errx(1, "ioctl(RTC_IRQP_SET) fail");
- /* enable periodic interrupts */
- if ( ioctl(fd, RTC_PIE_ON, 0) < 0 )
- errx(1, "ioctl(RTC_PIE_ON)");
- for ( i = 0; i < 100; i++ )
- {
- if ( read(fd, &data, sizeof(data)) < 0 )
- errx(1, "read() error");
- printf("timer %d/n", time(NULL));
- }
- /* enable periodic interrupts */
- if ( ioctl(fd, RTC_PIE_OFF, 0) < 0 )
- errx(1, "ioctl(RTC_PIE_OFF)");
- close(fd);
- return 0;
- }