timerfd是Linux爲用戶程序提供的一個定時器接口。這個接口基於文件描述符,經過文件描述符的可讀事件進行超時通知,因此可以被用於select/poll的應用場景。html
#include <sys/timerfd.h> linux
int timerfd_create(int clockid, int flags);less
int timerfd_settime(int fd, int flags, const struct itimerspec *new_value, struct itimerspec *old_value); ide
int timerfd_gettime(int fd, struct itimerspec *curr_value);函數
int timerfd_create(int clockid, int flags);ui
它是用來建立一個定時器描述符timerfdthis
第一個參數:clockid指定時間類型,有兩個值:spa
CLOCK_REALTIME :Systemwide realtime clock. 系統範圍內的實時時鐘3d
CLOCK_MONOTONIC:以固定的速率運行,從不進行調整和復位 ,它不受任何系統time-of-day時鐘修改的影響code
第二個參數:flags能夠是0或者O_CLOEXEC/O_NONBLOCK。
返回值:timerfd(文件描述符)
在講解該函數前,先理解兩個相關結構體:
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 */ };
第二個結構體itimerspec就是timerfd要設置的超時結構體,它的成員it_value表示定時器第一次超時時間,it_interval表示以後的超時時間即每隔多長時間超時
int timerfd_settime(int fd, int flags, const struct itimerspec *new_value, struct itimerspec *old_value);
做用:用來啓動或關閉有fd指定的定時器
參數:
fd:timerfd,有timerfd_create函數返回
fnew_value:指定新的超時時間,設定new_value.it_value非零則啓動定時器,不然關閉定時器,若是new_value.it_interval爲0,則定時器只定時一次,即初始那次,不然以後每隔設定時間超時一次
old_value:不爲null,則返回定時器此次設置以前的超時時間
flags:1表明設置的是絕對時間;爲0表明相對時間。
int timerfd_gettime(int fd, struct itimerspec *curr_value);
此函數用於得到定時器距離下次超時還剩下的時間。若是調用時定時器已經到期,而且該定時器處於循環模式(設置超時時間時struct itimerspec::it_interval不爲0),那麼調用此函數以後定時器從新開始計時。
The it_value field returns the amount of time until the timer will next expire. If both fields of this structure are zero, then the timer is currently disarmed. This field always contains a relative value, regardless of whether the TFD_TIMER_ABSTIME flag was specified when setting the timer.
The it_interval field returns the interval of the timer. If both fields of this structure are zero, then the timer is set to expire just once, at the time specified by curr_value.it_value.
read(2) If the timer has already expired one or more times since its settings were last modified using timerfd_settime(), or since the last successful read(2), then the buffer given to read(2) returns an unsigned 8-byte integer (uint64_t) containing the number of expirations that have occurred. (The returned value is in host byte order, i.e., the native byte order for integers on the host machine.)
If no timer expirations have occurred at the time of the read(2), then the call either blocks until the next timer expiration, or fails with the error EAGAIN if the file descriptor has been made nonblocking (via the use of the fcntl(2) F_SETFL operation to set the O_NONBLOCK flag).
A read(2) will fail with the error EINVAL if the size of the supplied buffer is less than 8 bytes.
當定時器超時,read讀事件發生便可讀,返回超時次數(從上次調用timerfd_settime()啓動開始或上次read成功讀取開始),它是一個8字節的unit64_t類型整數,若是定時器沒有發生超時事件,則read將阻塞若timerfd爲阻塞模式,不然返回EAGAIN 錯誤(O_NONBLOCK模式),若是read時提供的緩衝區小於8字節將以EINVAL錯誤返回。
man手冊中示例:
The following program creates a timer and then monitors its progress. The program accepts up to three command-line arguments. The first argument specifies the number of seconds for the initial expiration of the timer. The second argument specifies the interval for the timer, in seconds. The third argument specifies the number of times the program should allow the timer to expire before terminating. The second and third command-line arguments are optional.
#include <sys/timerfd.h> #include <time.h> #include <unistd.h> #include <stdlib.h> #include <stdio.h> #include <stdint.h> /* Definition of uint64_t */ #define handle_error(msg) \ do { perror(msg); exit(EXIT_FAILURE); } while (0) static void print_elapsed_time(void) { static struct timespec start; struct timespec curr; static int first_call = 1; int secs, nsecs; if (first_call) { first_call = 0; if (clock_gettime(CLOCK_MONOTONIC, &start) == -1) handle_error("clock_gettime"); } if (clock_gettime(CLOCK_MONOTONIC, &curr) == -1) handle_error("clock_gettime"); secs = curr.tv_sec - start.tv_sec; nsecs = curr.tv_nsec - start.tv_nsec; if (nsecs < 0) { secs--; nsecs += 1000000000; } printf("%d.%03d: ", secs, (nsecs + 500000) / 1000000); } int main(int argc, char *argv[]) { struct itimerspec new_value; int max_exp, fd; struct timespec now; uint64_t exp, tot_exp; ssize_t s; if ((argc != 2) && (argc != 4)) { fprintf(stderr, "%s init-secs [interval-secs max-exp]\n", argv[0]); exit(EXIT_FAILURE); } if (clock_gettime(CLOCK_REALTIME, &now) == -1) handle_error("clock_gettime"); /* Create a CLOCK_REALTIME absolute timer with initial expiration and interval as specified in command line */ new_value.it_value.tv_sec = now.tv_sec + atoi(argv[1]); new_value.it_value.tv_nsec = now.tv_nsec; if (argc == 2) { new_value.it_interval.tv_sec = 0; max_exp = 1; } else { new_value.it_interval.tv_sec = atoi(argv[2]); max_exp = atoi(argv[3]); } new_value.it_interval.tv_nsec = 0; fd = timerfd_create(CLOCK_REALTIME, 0); if (fd == -1) handle_error("timerfd_create"); if (timerfd_settime(fd, TFD_TIMER_ABSTIME, &new_value, NULL) == -1) handle_error("timerfd_settime"); print_elapsed_time(); printf("timer started\n"); for (tot_exp = 0; tot_exp < max_exp;) { s = read(fd, &exp, sizeof(uint64_t)); if (s != sizeof(uint64_t)) handle_error("read"); tot_exp += exp; print_elapsed_time(); printf("read: %llu; total=%llu\n", (unsigned long long) exp, (unsigned long long) tot_exp); } exit(EXIT_SUCCESS); }
運行結果: