timerfd 介紹
timerfd 是在Linux內核2.6.25版本中添加的接口,其是Linux為用戶提供的一個定時器接口。這個接口基于文件描述符,所以可以被用于select/poll/epoll的場景。當使用timerfd API創建多個定時器任務并置于poll中進行事件監聽,當沒有可響應的事件,則程序阻塞在poll中,當有事件發生,通過poll的這個事件入口,對產生的事件進行響應,從而構成了一個事件輪訓程序。
timerfd 相關函數
#include <time.h>
int clock_gettime(clockid_t clockid, struct timespec *tp);
- 1
- 2
- 3
clock_gettime函數主要用于獲取系統時間,精確到納秒級別。在編譯時需要添加-lrt庫,clockid_t clockid指定用何種模式獲取時間,struct timespec *tp用于存儲獲取到的時間。其中clockid主要有如下常用的參數:?
CLOCK_REALTIME:系統實時時間,隨系統實時時間改變而改變,即從UTC1970-1-1 0:0:0開始計時,中間時刻如果系統時間被用戶改成其他,則對應的時間相應改變?
CLOCK_MONOTONIC:從系統啟動這一刻起開始計時,不受系統時間被用戶改變的影響?
CLOCK_PROCESS_CPUTIME_ID:本進程到當前代碼系統CPU花費的時間?
CLOCK_THREAD_CPUTIME_ID:本線程到當前代碼系統CPU花費的時間
#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
- 2
- 3
- 4
- 5
- timerfd_create函數主要用于生成一個定時器對象,返回與之關聯的文件描述符,clockid可以設置CLOCK_REALTIME和CLOCK_MONOTONIC,flags可以設置為TFD_NONBLOCK(非阻塞),TFD_CLOEXEC(同O_CLOEXEC)
- timerfd_settime用于啟動和停止定時器,fd為timerfd_create獲得的定時器文件描述符,flags為0表示是相對定時器,為TFD_TIMER_ABSTIME表示是絕對定時器。const struct itimerspec *new_value表示設置超時的時間。?
其數據結構:
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 */};
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
需要注意的是itimerspec 結構成員表示的意義:?
it_value是首次超時時間,需要填寫從clock_gettime獲取的時間,并加上要超時的時間。 it_interval是后續周期性超時時間,是多少時間就填寫多少。?
it_interval不為0則表示是周期性定時器。?
it_value和it_interval都為0表示停止定時器。
- timerfd_gettime此函數用于獲得定時器距離下次超時還剩下的時間。如果調用時定時器已經到期,并且該定時器處于循環模式(設置超時時間時struct itimerspec::it_interval不為0),那么調用此函數之后定時器重新開始計時。
參考示例
示例一
int tu_set_timer(tu_timer_t * timer, uint64_t milliseconds, bool continious, timer_handler_cb_t timer_handler_cb, void * timer_handler_arg)
{int fd;struct itimerspec its;//創建的定時器 fd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK);if (fd == -1){LOG_PRINT("Error creating timer");return -1;}//設置超時時間its.it_value.tv_sec = (milliseconds * 1000000) / 1000000000;its.it_value.tv_nsec = (milliseconds * 1000000) % 1000000000;//如果是周期定時器,則設置it_interval,如果不是則為0its.it_interval.tv_sec = continious ? its.it_value.tv_sec : 0;its.it_interval.tv_nsec = continious ? its.it_value.tv_nsec : 0;//設置定時到達后的響應函數及其函數參數timer->timer_handler_cb = timer_handler_cb;timer->timer_handler_arg = timer_handler_arg;timer->continious = continious;//標記是否為循環周期定時器//啟動定時器,并將文件描述符添加到poll中監聽if ((timerfd_settime(fd, 0, &its, NULL) == 0) && ((timer->fd_index = polling_define_poll_fd(fd, POLLIN, tu_timer_handler, timer)) != -1)){timer->in_use = true;return 0;}close(fd);return -1;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
示例二
int tu_set_timer_realtime(tu_timer_t * timer, uint64_t milliseconds, bool continious, timer_handler_cb_t timer_handler_cb, void * timer_handler_arg)
{int fd;struct itimerspec its;struct timespec now;time_t tv_sec;long tv_nsec;//獲取絕對時間if(clock_gettime(CLOCK_REALTIME,&now) == -1){LOG_PRINT("Error clock_gettime timer\n");return -1;}//創建定時器,非阻塞方式 fd = timerfd_create(CLOCK_REALTIME, TFD_NONBLOCK);if (fd == -1){LOG_PRINT("Error creating timer\n");return -1;}//計算時間tv_sec = (milliseconds * 1000000) / 1000000000;tv_nsec = (milliseconds * 1000000) % 1000000000;//設置到期時間its.it_value.tv_sec = now.tv_sec + tv_sec;its.it_value.tv_nsec = now.tv_nsec + tv_nsec;//如果使用循環模式,設置循環間隔its.it_interval.tv_sec = continious ? tv_sec : 0;its.it_interval.tv_nsec = continious ? tv_nsec : 0;//設置定時到達后的響應函數及其函數參數timer->timer_handler_cb = timer_handler_cb;timer->timer_handler_arg = timer_handler_arg;timer->continious = continious;//啟動定時器,并將文件描述符添加到poll中監聽if ((timerfd_settime(fd,TFD_TIMER_ABSTIME, &its, NULL) == 0) && ((timer->fd_index = polling_define_poll_fd(fd, POLLIN, tu_timer_handler, timer)) != -1)){timer->in_use = true;LOG_PRINT("tu_set_timer_realtime--\n");return 0;}LOG_PRINT("Error setting timer\n");close(fd);return -1;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
poll中的回調函數
void tu_timer_handler(void * arg)
{tu_timer_t * timer = arg;uint64_t exp;if (timer->continious)//重復定時器{if (read(polling_fds[timer->fd_index].fd, &exp, sizeof(uint64_t)) != sizeof(uint64_t)){LOG_PRINT("%p ERROR timer read. Killing timer.\n", timer);tu_kill_timer(timer);}}else{tu_kill_timer(timer);//關閉定時器}//調用定時器處理函數timer->timer_handler_cb(timer->timer_handler_arg);
}