timerfd是Linux爲用戶程序提供的一個定時器接口。這個接口基於文件描述符,通過文件描述符的可讀事件進行超時通知,所以能夠被用於select/poll的應用場景。
#include <sys/timerfd.h>
// 返回值:創建定時器文件描述符
int timerfd_create(int clockid, int flags);
clockid
CLOCK_REALTIME :系統範圍內的實時時鐘
CLOCK_MONOTONIC
flags
0
O_CLOEXEC
O_NONBLOCK
// 用來啓動或關閉有fd指定的定時器
int timerfd_settime(int fd, int flags, const struct itimerspec *new_value, struct itimerspec *old_value);
第二個參數:struct itimerspec
struct timespec {
time_t tv_sec; /* Seconds */
long tv_nsec; /* Nanoseconds */
};
struct itimerspec {
struct timespec it_interval; // 之後每隔多長時間超時
struct timespec it_value; // 第一次超時時間
};
// 獲得定時器距離下次超時還剩下的事件
int timerfd_gettime(int fd, struct itimerspec *curr_value);
定時器超時事件的到來時,定時器文件描述符的值將會增加,與eventfd一樣,可以用read函數讀取定時器文件描述符。
最常用的做法是:將定時器文件描述符添加到epoll樹中,由epoll監控該描述符事件,當事件到來時,去處理它(具體的代碼見下)
#include <stdio.h>
#include <stdlib.h>
#include <sys/timerfd.h>
#include <time.h>
#include<sys/time.h>
#include <sys/epoll.h>
#include <unistd.h>
#include <assert.h>
const int MAXNUM = 20;
int main(int argc, char *argv[])
{
uint64_t exp;
ssize_t s;
int i;
// 1.構建了一個定時器
int timefd = timerfd_create(CLOCK_REALTIME, TFD_NONBLOCK);
assert(timefd != -1);
// 第二個參數(new_value)的初始化
struct itimerspec new_value;
// 第一次到期的時間
struct timespec now;
int ret = clock_gettime(CLOCK_REALTIME, &now);//獲取時鐘時間
new_value.it_value.tv_sec = 5;
new_value.it_value.tv_nsec = now.tv_nsec;
// 之後每次到期的時間間隔
new_value.it_interval.tv_sec = 1;
new_value.it_interval.tv_nsec = 0;
// 2.啓動定時器
ret = timerfd_settime(timefd, 0, &new_value, NULL);
assert(ret != -1);
printf("timer started\n"); // 定時器開啓啦!
// 3.創建epoll樹
int epollfd = epoll_create(1);
struct epoll_event events[MAXNUM];
// 4.timefd定時器事件
struct epoll_event ev;
ev.data.fd = timefd;
ev.events = EPOLLIN | EPOLLET;
// 5.將定時器事件添加到epoll樹 && 監控
epoll_ctl(epollfd, EPOLL_CTL_ADD, timefd, &ev);
// 6.死循環,等待定時器事件的到來
for (; ;) {
/* 當定時器到時時,定時器文件描述符將會可讀,epoll_wait將會返回*/
int num = epoll_wait(epollfd, events, MAXNUM, 0);
assert(num >= 0);
// 處理返回的epoll事件
for (i = 0; i < num; i++) {
if (events[i].events & EPOLLIN) {
//....處理其他事件
// 處理定時器到時事件
if (events[i].data.fd == timefd) {
// 讀取
s = read(events[i].data.fd, &exp, sizeof(uint64_t)); //需要讀出uint64_t大小, 不然會發生錯誤
assert(s == sizeof(uint64_t));
printf("here is timer: timefd = %d\n", events[i].data.fd);
}
}
}
}
close(timefd);
close(epollfd);
return 0;
}
代碼運行結果
[g@ ~/test]# gcc time.c -o time -lrt
[g@ ~/test]# ./time
timer started
here is timer: s = 3
here is timer: s = 3
here is timer: s = 3
here is timer: s = 3
here is timer: s = 3
here is timer: s = 3
here is timer: s = 3
here is timer: s = 3
here is timer: s = 3
here is timer: s = 3
here is timer: s = 3