timerfd是linux提供的定時器機制,基于檔案描述符,定時器精度最高可達納秒級别,接口包括定時器建立、啟動定時器、關閉定時器和删除定時器。下面介紹一下timerfd API接口和一個結合epoll使用的定時器demo。
1. 建立定時器
#include <sys/timerfd.h>
/*
* 功能 : 建立定時器
* 傳回值:成功傳回定時器檔案描述符,失敗傳回-1
* 參數: clockid可以是CLOCK_REALTIME(實時時鐘)或者CLOCK_MONOTONIC(遞增時鐘),實時時鐘可以被系統時間改變,後者不會。
* 如果這裡使用實時時鐘,當手動更改系統時間定時器也會受影響,而遞增時鐘則隻受設定的時間值影響。
* flags : 可選項包括TFD_NONBLOCK(非阻塞)和TFD_CLOEXEC,阻塞指的是當定時器未逾時的時候,如果調用read(timerfd)會阻塞直
* 到定時器逾時,如果設定TFD_NONLOCK,則會直接傳回并傳回-1. 這與套接字描述符類似。
*/
int timerfd_create(int clockid, int flags); 如果這裡使用實時時鐘,當手動更改系統時間定時器也會受影響,而遞增時鐘則隻受設定的時間值影響。
* flags : 可選項包括TFD_NONBLOCK(非阻塞)和TFD_CLOEXEC,阻塞指的是當定時器未逾時的時候,如果調用read(timerfd)會阻塞直
* 到定時器逾時,如果設定TFD_NONLOCK,則會直接傳回并傳回-1. 這與套接字描述符類似。
*/
int timerfd_create(int clockid, int flags);
2. 啟動和停止定時器
/*
* 功能 定時器啟動和關閉
* 傳回值 : 成功傳回0,失敗傳回-1,并存儲錯誤碼到errno
* 參數: fd: 定時器描述符
* flags: 0 或者TFD_TIMER_ABSTIME,0代表相對時間,即相對于目前時間多少,後者是絕對時間。
* new_value: 當new_value.it_value非0時,用于設定定時器第一次逾時時間,為0代表停止定時器
new_value.it_interval:表示第一次逾時後下一次逾時的時間,為0代表定時器隻逾時一次
* old_value: 如果不為NULL,則用來存儲目前時間。
int timerfd_settime(int fd, int flags, const struct itimerspec *new_value, struct itimerspec *old_value);
3. 關閉定時器
/*
* 功能: 和普通描述符一樣,用完後使用close釋放
* 參數:timerfd為timerfd_create()建立的定時器描述符
*/
close(timerfd);
4. 時間結構體
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 */
};
下面是一個結合epoll使用的定時器demo,建立一個每隔2s逾時的定時器并加入到epoll監聽隊列中,每當定時器到期逾時時産生一個讀事件,之後可以去執行相應的定時器回調函數。
/*
* Description : linux 應用層程式設計之定時器timerfd的使用
* Date :20180611
* Author :mason
* Mail : [email protected]
*
*/
#include <sys/timerfd.h>
#include <sys/epoll.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdio.h>
#include<errno.h>
#include <time.h>
#define TIME_MAX 2
#define log(fmt, arg...) printf(""fmt, ##arg)
void main() {
int tfd; //定時器描述符
int efd; //epoll描述符
int fds, ret;
uint64_t value;
struct epoll_event ev, *evptr;
struct itimerspec time_intv; //用來存儲時間
tfd = timerfd_create(CLOCK_MONOTONIC, 0); //建立定時器
if(tfd == -1) {
log("create timer fd fail \r\n");
return ;
}
time_intv.it_value.tv_sec = TIME_MAX; //設定2s逾時
time_intv.it_value.tv_nsec = 0;
time_intv.it_interval.tv_sec = time_intv.it_value.tv_sec; //每隔2s逾時
time_intv.it_interval.tv_nsec = time_intv.it_value.tv_nsec;
log("timer start ...\n");
timerfd_settime(tfd, 0, &time_intv, NULL); //啟動定時器
efd = epoll_create1(0); //建立epoll執行個體
if (efd == -1) {
log("create epoll fail \r\n");
close(tfd);
return ;
}
evptr = (struct epoll_event *)calloc(1, sizeof(struct epoll_event));
if (evptr == NULL) {
log("epoll event calloc fail \r\n");
close(tfd);
close(efd);
return ;
}
ev.data.fd = tfd;
ev.events = EPOLLIN; //監聽定時器讀事件,當定時器逾時時,定時器描述符可讀。
epoll_ctl(efd, EPOLL_CTL_ADD, tfd, &ev); //添加到epoll監聽隊列中
while(1) {
fds = epoll_wait(efd, evptr, 1, -1); //阻塞監聽,直到有事件發生
if(evptr[0].events & EPOLLIN){
ret = read(evptr->data.fd, &value, sizeof(uint64_t));
if (ret == -1)
log("read return -1, errno :%d \r\n", errno);
else
log("*** timer up *** \n");
}
}
return ;
}
實驗截圖:
代碼路徑:
[email protected]:FuYuanDe/timerfd.git
參考資料:
https://linux.die.net/man/2/timerfd_create