1. 程式人生 > 其它 >Linux定時器timerfd用法

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;
}