Linux定時器timerfd用法
timerfd特點
timerfd的特點是將時間變成一個檔案描述符,定時器超時時,檔案可讀。這樣就能很容易融入select(2)/poll(2)/epoll(7)的框架中,用統一的方式來處理IO事件、超時事件。這也是Reactor模式的特點。
timerfd定時器與傳統Reactor模式定時器
傳統Reactor模式使用select/poll/epoll 的timeout引數實現定時功能,但其精度只有毫秒(注意區分表示精度和實際精度,表示精度可能為微妙和納秒)。
另外,select/poll/epoll的定時器也有一個缺陷,那就是隻能針對的是所有監聽的檔案描述符fd,而非繫結某個fd。
timerfd可以解決這個問題,單獨為某個fd指定定時器。
timerfd介面
timerfd包含3個介面:timerfd_create,timerfd_settime,timerfd_gettime。
#include <sys/timerfd.h> int timerfd_create(int clockid, int flags); int timerfd_settime(int fd, int flags, const struct itimerspec *new_value, struct itimerspec *old_value); int timerfd_gettime(int fd, struct itimerspec *curr_value);
1)timerfd_create 用於建立定時器物件,返回一個指向該定時器的fd。
引數
clockid 用於建立定時器的過程,只能是CLOCK_REALTIME或CLOCK_MONOTONIC。CLOCK_REALTIME表示建立一個可設定的系統範圍的時鐘(system-wide clock)。CLOCK_MONOTONIC表示建立一個不可設定的時鐘,不受系統時鐘中的非連續改變的影響(例如,手動改變系統時間)
flags 選項,值能按位或,可用於改變timerfd_create()行為。
- TFD_NONBLOCK 為新開啟的fd設定O_NONBLOCK選項,可以節約額外呼叫fcntl實現通用結果。
- TFD_CLOEXEC 為新開啟的fd設定close-on-exec(FD_CLOEXEC)選項,在fork + exec後,新程序自動關閉該fd。同樣的,可以節約額外呼叫open指定O_CLOEXEC選項。
返回值
成功,返回一個新的檔案描述符(繫結到一個內部定時器);錯誤,返回-1並且errno被設定。
2)timerfd_settime 用於啟動或停止繫結到fd的定時器。
引數
fd 由timerfd_create的定時器對應檔案描述符
flags 選項
- 0,啟動一個相對定時器,基於當前時間 + new_value.it_value指定的相對定時值。
- TFD_TIMER_ABSTIME,啟動一個絕對定時器(由new_value.it_value指定定時值)。
new_value 定時器新的定時值,根據flags不同有不同含義。
old_value 定時器舊的定時值。可以為NULL,如果非NULL,說明之前設定過。
返回值
成功,返回0;錯誤,返回-1,且errno被設定。
3)timerfd_gettime 用於獲取fd對應定時器的當前時間值
引數
fd 由timerfd_create的定時器對應檔案描述符。
curr_value 儲存定時器的當前定時值。
返回值
成功,返回0;錯誤,返回-1,且errno被設定。
4)操作定時器fd
- read(2) 定時器超時時,fd可讀,read讀取fd返回1個無符號8byte整型(uint64_t,主機位元組序,存放當buf中),表示超時的次數。如果沒有超時,read將會阻塞到下一次定時器超時,或者失敗(errno設定為EAGAIN,fd設定為非阻塞)。另外,如果提供的buffer大小 < 8byte,read將返回EINVAL。read成功時,返回值應為8。
- poll(2)/select(2)/epoll (7) 監聽定時器fd,fd可讀時,會收到就緒通知。
- close(2) 關閉fd對應定時器。如果不需要該定時器,可呼叫close關閉之。
timerfd使用示例
timerfd + poll + read
#include <iostream>
#include <sys/timerfd.h>
#include <poll.h>
#include <unistd.h>
#include <assert.h>
using namespace std;
int main() {
struct itimerspec timebuf;
int timerfd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK | TFD_CLOEXEC);
timebuf.it_interval = {1, 0}; // period timeout value = 1s
timebuf.it_value = {5, 100}; // initial timeout value = 5s100ns
timerfd_settime(timerfd, 0, &timebuf, NULL);
struct pollfd fds[1];
int len = sizeof(fds) / sizeof(fds[0]);
fds[0].fd = timerfd;
fds[0].events = POLLIN | POLLERR | POLLHUP;
while (true)
{
int n = poll(fds, len, -1);
for (int i = 0; i < len && n-- > 0; ++i) {
if (fds[i].revents & POLLIN)
{
uint64_t val;
int ret = read(timerfd, &val, sizeof(val));
if (ret != sizeof(val)) // ret should be 8
{
cerr << "read " << ret << "bytes instead of 8 frome timerfd" << endl;
break;
}
cout << "timerfd = " << timerfd << " timeout!" << endl;
}
}
}
close(timerfd);
return 0;
}