linux下的timerfd机制

timerfd是Linux为用户程序提供的一个定时器接口。这个接口基于文件描述符,通过文件描述符的可读事件进行超时通知,所以能够被用于select/poll的应用场景。timerfd是linux内核2.6.25版本中加入的借口。
timerfd、eventfd、signalfd配合epoll使用,可以构造出一个零轮询的程序,但程序没有处理的事件时,程序是被阻塞的。这样的话在某些移动设备上程序更省电。

clock_gettime函数可以获取系统时钟,精确到纳秒。需要在编译时指定库:-lrt。可以获取两种类型事件:
CLOCK_REALTIME:相对时间,从1970.1.1到目前的时间。更改系统时间会更改获取的值。也就是,它以系统时间为坐标。
CLOCK_MONOTONIC:与CLOCK_REALTIME相反,它是以绝对时间为准,获取的时间为系统重启到现在的时间,更改系统时间对齐没有影响。

timerfd_create:
生成一个定时器对象,返回与之关联的文件描述符。接收两个入参,一个是clockid,填写CLOCK_REALTIME或者CLOCK_MONOTONIC,参数意义同上。第二个可以传递控制标志:TFD_NONBLOCK(非阻塞),TFD_CLOEXEC(同O_CLOEXEC)

注:timerfd的进度要比usleep要高。

timerfd_settime:能够启动和停止定时器;可以设置第二个参数:flags,0表示是相对定时器,TFD_TIMER_ABSTIME表示是绝对定时器。
第三个参数设置超时时间,如果为0则表示停止定时器。定时器设置超时方法:
1、设置超时时间是需要调用clock_gettime获取当前时间,如果是绝对定时器,那么需要获取CLOCK_REALTIME,在加上要超时的时间。如果是相对定时器,要获取CLOCK_MONOTONIC时间。
2、数据结构: 
   struct timespec {
               time_t tv_sec;                /* Seconds */
               long   tv_nsec;               /* Nanoseconds */
           };

           struct itimerspec {
               struct timespec it_interval;  /* Interval for periodic timer */
               struct timespec it_value;     /* Initial expiration */
           };
      it_value是首次超时时间,需要填写从clock_gettime获取的时间,并加上要超时的时间。 it_interval是后续周期性超时时间,是多少时间就填写多少。
     注意一个容易犯错的地方:tv_nsec加上去后一定要判断是否超出1000000000(如果超过要秒加一),否则会设置失败。
     
     it_interval不为0则表示是周期性定时器。
     it_value和it_interval都为0表示停止定时器。

注:timerfd_create第一个参数和clock_gettime的第一个参数都是CLOCK_REALTIME或者CLOCK_MONOTONIC,timerfd_settime的第二个参数为0(相对定时器)或者TFD_TIMER_ABSTIME,三者的关系:
1、如果timerfd_settime设置为TFD_TIMER_ABSTIME(决定时间),则后面的时间必须用clock_gettime来获取,获取时设置CLOCK_REALTIME还是CLOCK_MONOTONIC取决于timerfd_create设置的值。
2、如果timerfd_settime设置为0(相对定时器),则后面的时间必须用相对时间,就是:
    new_value.it_value.tv_nsec = 500000000;
    new_value.it_value.tv_sec = 3;
    new_value.it_interval.tv_sec = 0;
    new_value.it_interval.tv_nsec = 10000000;

read函数可以读timerfd,读的内容为uint_64,表示超时次数。

timerfd简单的性能测试:
申请1000个定时器,超时间定位1s,每秒超时一次,发现cpu占用率在3.0G的cpu上大概为1%,10000个定时器的话再7%左右,而且不会出现同时超时两个的情况,如果有printf到前台,则一般会出现定时器超时多次(3-5)才回调。

 

关于timerfd的一个简单测试程序:

 

(1)timerfd_create()函数

复制代码

#include <sys/timerfd.h>

int timerfd_create(int clockid, int flags);
/* 
timerfd_create()函数创建一个定时器对象,同时返回一个与之关联的文件描述符。
clockid:clockid标识指定的时钟计数器,可选值(CLOCK_REALTIME、CLOCK_MONOTONIC。。。)
CLOCK_REALTIME:系统实时时间,随系统实时时间改变而改变,即从UTC1970-1-1 0:0:0开始计时,中间时刻如果系统时间被用户改成其他,则对应的时间相应改变
CLOCK_MONOTONIC:从系统启动这一刻起开始计时,不受系统时间被用户改变的影响
flags:参数flags(TFD_NONBLOCK(非阻塞模式)/TFD_CLOEXEC(表示当程序执行exec函数时本fd将被系统自动关闭,表示不传递)
*/

复制代码

(2)timerfd_settime()函数

复制代码

 1 #include <sys/timerfd.h>
 2 
 3 struct timespec {
 4     time_t tv_sec;                /* Seconds */
 5     long   tv_nsec;               /* Nanoseconds */
 6 };
 7 
 8 struct itimerspec {
 9     struct timespec it_interval;  /* Interval for periodic timer (定时间隔周期)*/
10     struct timespec it_value;     /* Initial expiration (第一次超时时间)*/
11 };
12 int timerfd_settime(int fd, int flags, const struct itimerspec *new_value, struct itimerspec *old_value);
13 /*
14     timerfd_settime()此函数用于设置新的超时时间,并开始计时,能够启动和停止定时器;
15     fd: 参数fd是timerfd_create函数返回的文件句柄
16     flags:参数flags为1代表设置的是绝对时间(TFD_TIMER_ABSTIME 表示绝对定时器);为0代表相对时间。
17     new_value: 参数new_value指定定时器的超时时间以及超时间隔时间
18     old_value: 如果old_value不为NULL, old_vlaue返回之前定时器设置的超时时间,具体参考timerfd_gettime()函数
19     
20     ** it_interval不为0则表示是周期性定时器。
21        it_value和it_interval都为0表示停止定时器
22 */

复制代码

(3)timerfd_gettime()函数

复制代码

1 int timerfd_gettime(int fd, struct itimerspec *curr_value);
2 /*
3     timerfd_gettime()函数获取距离下次超时剩余的时间
4     curr_value.it_value 字段表示距离下次超时的时间,如果改值为0,表示计时器已经解除
5     改字段表示的值永远是一个相对值,无论TFD_TIMER_ABSTIME是否被设置
6     curr_value.it_interval 定时器间隔时间
7 */

复制代码

1 uint64_t exp = 0;
2 read(fd, &exp, sizeof(uint64_t)); 
3 //可以用read函数读取计时器的超时次数,改值是一个8字节无符号的长整型

(4)下面贴出一个timerfd配合epoll函数的简单例子

复制代码

  1 /********************************************************
  2 * Filename: timerfd.c
  3 * Author: zhangwj
  4 * Desprition: a sample program of timerfd
  5 * Date: 2017-04-17
  6 * Warnning:
  7 ********************************************************/
  8 #include <stdio.h>
  9 #include <stdint.h>
 10 #include <string.h>
 11 #include <stdlib.h>
 12 #include <pthread.h>
 13 #include <errno.h>
 14 #include <sys/epoll.h>
 15 #include <sys/timerfd.h>
 16 
 17 #if 0
 18 struct timespec {
 19     time_t tv_sec;                /* Seconds */
 20     long   tv_nsec;               /* Nanoseconds */
 21 };
 22 
 23 struct itimerspec {
 24     struct timespec it_interval;  /* Interval for periodic timer */
 25     struct timespec it_value;     /* Initial expiration */
 26 };
 27 #endif
 28 
 29 #define EPOLL_LISTEN_CNT        256
 30 #define EPOLL_LISTEN_TIMEOUT    500
 31 
 32 #define LOG_DEBUG_ON 1
 33 
 34 #ifdef LOG_DEBUG_ON 
 35 #define LOG_DEBUG(fmt, args...) \
 36     do {  \
 37         printf("[DEBUG]:");\
 38         printf(fmt "\n", ##args); \
 39     } while(0);
 40 #define LOG_INFO(fmt, args...) \
 41     do { \
 42         printf("[INFO]:");\
 43         printf(fmt "\n", ##args); \
 44     } while(0);
 45 #define LOG_WARNING(fmt, args...) \
 46     do { \
 47         printf("[WARNING]:");\
 48         printf(fmt "\n", ##args); \
 49     } while(0);
 50 #else
 51 #define LOG_DEBUG(fmt, args...) 
 52 #define LOG_INFO(fmt, args...) 
 53 #define LOG_WARNING(fmt, args...) 
 54 #endif
 55 #define LOG_ERROR(fmt, args...) \
 56     do{ \
 57         printf("[ERROR]:");\
 58         printf(fmt "\n", ##args);\
 59     }while(0);
 60 
 61 #define handle_error(msg) \
 62         do { perror(msg); exit(EXIT_FAILURE); } while (0)
 63 
 64 static int g_epollfd = -1;
 65 static int g_timerfd = -1;
 66 uint64_t tot_exp = 0;
 67 
 68 static void help(void)
 69 {
 70     exit(0);
 71 }
 72 
 73 static void print_elapsed_time(void)
 74 {
 75     static struct timespec start;
 76     struct timespec curr;
 77     static int first_call = 1;
 78     int secs, nsecs;
 79     
 80     if (first_call) {
 81         first_call = 0;
 82         if (clock_gettime(CLOCK_MONOTONIC, &start) == -1) 
 83             handle_error("clock_gettime");
 84     }   
 85     
 86     if (clock_gettime(CLOCK_MONOTONIC, &curr) == -1) 
 87         handle_error("clock_gettime");
 88     
 89     secs = curr.tv_sec - start.tv_sec;
 90     nsecs = curr.tv_nsec - start.tv_nsec;
 91     if (nsecs < 0) {
 92         secs--;
 93         nsecs += 1000000000;
 94     }   
 95     printf("%d.%03d: ", secs, (nsecs + 500000) / 1000000);
 96 }
 97 
 98 void timerfd_handler(int fd)
 99 {
100     uint64_t exp = 0;
101     
102     read(fd, &exp, sizeof(uint64_t)); 
103     tot_exp += exp;
104     print_elapsed_time();
105     printf("read: %llu, total: %llu\n", (unsigned long long)exp, (unsigned long long)tot_exp);
106 
107     return;
108 }
109 
110 void epoll_event_handle(void)
111 {
112     int i = 0;
113     int fd_cnt = 0;
114     int sfd;
115     struct epoll_event events[EPOLL_LISTEN_CNT];    
116 
117     memset(events, 0, sizeof(events));
118     while(1) 
119     {   
120         /* wait epoll event */
121         fd_cnt = epoll_wait(g_epollfd, events, EPOLL_LISTEN_CNT, EPOLL_LISTEN_TIMEOUT); 
122         for(i = 0; i < fd_cnt; i++) 
123         {   
124             sfd = events[i].data.fd;
125             if(events[i].events & EPOLLIN) 
126             {   
127                 if (sfd == g_timerfd) 
128                 {
129                     timerfd_handler(sfd); 
130                 }   
131             }   
132         } 
133     }   
134 }
135 
136 int epoll_add_fd(int fd)
137 {
138     int ret;
139     struct epoll_event event;
140 
141     memset(&event, 0, sizeof(event));
142     event.data.fd = fd;
143     event.events = EPOLLIN | EPOLLET;
144 
145     ret = epoll_ctl(g_epollfd, EPOLL_CTL_ADD, fd, &event);
146     if(ret < 0) {
147         LOG_ERROR("epoll_ctl Add fd:%d error, Error:[%d:%s]", fd, errno, strerror(errno));
148         return -1;
149     }
150 
151     LOG_DEBUG("epoll add fd:%d--->%d success", fd, g_epollfd);
152     return 0;    
153 }
154 
155 int epollfd_init()
156 {
157     int epfd;
158 
159     /* create epoll fd */
160     epfd = epoll_create(EPOLL_LISTEN_CNT); 
161     if (epfd < 0) {
162         LOG_ERROR("epoll_create error, Error:[%d:%s]", errno, strerror(errno));
163         return -1;
164     }
165     g_epollfd = epfd;
166     LOG_DEBUG("epoll fd:%d create success", epfd);
167 
168     return epfd;
169 }
170 
171 int timerfd_init()
172 {
173     int tmfd;
174     int ret;
175     struct itimerspec new_value;
176 
177     new_value.it_value.tv_sec = 2;
178     new_value.it_value.tv_nsec = 0;
179     new_value.it_interval.tv_sec = 1;
180     new_value.it_interval.tv_nsec = 0;
181     
182     tmfd = timerfd_create(CLOCK_MONOTONIC, 0);
183     if (tmfd < 0) {
184         LOG_ERROR("timerfd_create error, Error:[%d:%s]", errno, strerror(errno));
185         return -1;
186     }
187 
188     ret = timerfd_settime(tmfd, 0, &new_value, NULL);
189     if (ret < 0) {
190         LOG_ERROR("timerfd_settime error, Error:[%d:%s]", errno, strerror(errno));
191         close(tmfd);
192         return -1;
193     }
194 
195     if (epoll_add_fd(tmfd)) {
196         close(tmfd);
197         return -1;
198     }
199     g_timerfd = tmfd;
200 
201     return 0;
202 }
203 
204 int main(int argc, char **argv)
205 {
206     if (epollfd_init() < 0) {
207         return -1;
208     }
209 
210     if (timerfd_init()) {
211         return -1;
212     }
213 
214     /* event handle */
215     epoll_event_handle();
216 
217     return 0;
218 }


原文链接:https://blog.csdn.net/chgaowei/article/details/21295811

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值