目錄
提問:
- 如何使用timerfd API?
- 什麼時候需要使用?
系統調用:
1、創建
#include <sys/timerfd.h>
int timerfd_create(int clockid, int flags)
成功返回文件描述符,失敗返回-1 。
參數clockid
1、設置爲CLOCK_REALTIME 可設定的系統級實時時鐘,用於度量真實時間。
2、設置爲CLOCK_MONOTONIC 不可設定的恆定態時鐘,始於“未予規範的過去某一時間點”,系統啓動後就不會改變。該時鐘適用於那些無法容忍系統時鐘發生跳躍性變化(如手工改變了系統時間)的應用程序。
參數flags 一般設置爲0,Linux內核2.6.27版本後支持下面兩種flags標誌
1、TFD_CLOEXEC 爲新文件描述符設置運行時關閉標誌
2、TFD_NONBLOCK 爲底層的打開文件描述設置爲非阻塞。
- 定時器使用完畢後,應調用close()關閉相應的文件描述符,以便於內核釋放與定時器相關的資源。
2、設置
#include <sys/timerfd.h>
int timerfd_settime(int fd, int flags, const struct itimerspec * new_value,
struct itimerspec * old_value);
參數new_value 爲定時器指定新設置。
參數old_value 可用來返回定時器的前一設置
參數flags
1、可以爲0,將new_value.it_value的值視爲相對於調用timerfd_settime()時間點的相對時間。
2、可以爲TFD_TIMER_ABSTIME,將其視爲一個絕對時間(從時鐘值0開始測量)。一旦時鐘超過了這一時間,定時器會立即到期。
結構體:
struct itimerspec
{
struct timespec it_interval; //定時器的週期性到期時間
struct timespec it_value; //第一次到期時間
};
struct timespec
{
time_t tv_sec; //秒
long tv_nsec; //納秒 = 10^(-9) 秒
};
//秒 毫秒 微秒 納秒
- 如果it_interval的下屬字段均爲0,那麼這個定時器將只到期一次。
3、獲取定時器時間間隔和剩餘時間
#include <sys/timerfd.h>
int timerfd_gettime(int fd, struct itimerspec * curr_value);
成功返回0,失敗返回-1 。
4、timerfd與fork()及exec()之間的交互
- 調用fork()期間,子進程會繼承timerfd_create()所創建文件描述符的拷貝。這些描述符與父進程的對應描述符均指代相同的定時器對象,任一進程都可讀取定時器的到期信息。
- timerfd_create()創建的文件描述符能夠跨越exec()得以保存(除非將文件描述符設置爲運行時關閉),已配備的定時器在exec()之後會繼續生成到期通知。
5、通過read()從timefd文件描述符讀取到期信息
- 一旦用timerfd_create()啓動了定時器,就可以從相應文件描述符調用read()來讀取定時器到期信息。出於這一目的,傳給read()的緩衝區必須足以容納一個無符號8字節整形數。
- 在上次使用timerfd_settime()修改設置以後,或是最後一次執行read()後,如果發生了一起到多起定時器到期事件,那麼read()會立即返回,且返回的緩衝區包含了已經發生的到期次數。如果無定時器到期,read()會一直阻塞直至產生下一個到期。可以就將文件描述符設置爲O_NONBLOCK標誌,這時讀操作是非阻塞式的,且如果沒有定時器到期,則返回錯誤,並將errno值置爲EAGAIN。
- 可以利用select()、poll()、epoll()對timefd文件描述符進行監控。如果定時器到期,會將對應的文件描述符標記爲可讀。
測試代碼:
#include <sys/timerfd.h>
#include <time.h>
#include <unistd.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
int main()
{
struct itimerspec ts;
struct timespec start, now;
int secs, nanosecs;
memset(&ts, 0, sizeof(struct itimerspec));
ts.it_value.tv_sec = 0;
ts.it_value.tv_nsec = 10000000; //10ms一次
ts.it_interval.tv_sec = 0;
ts.it_interval.tv_nsec = 5000000; //5ms一次
int fd = timerfd_create(CLOCK_REALTIME, TFD_NONBLOCK); //設置爲非阻塞
if(-1 == fd)
perror("timerfd_create");
if(timerfd_settime(fd, 0, &ts, NULL) == -1)
perror("timefd_settime");
if(clock_gettime(CLOCK_MONOTONIC, &start) == -1)
perror("clock_gettime");
ssize_t s;
uint64_t numExp, totalExp, maxExp = 10;
for(totalExp = 0; totalExp < maxExp;)
{
s = read(fd, &numExp, sizeof(uint64_t));
if(s != sizeof(uint64_t))
perror("read");
totalExp += numExp;
if(clock_gettime(CLOCK_MONOTONIC, &now) == -1)
perror("clock_gettime");
secs = now.tv_sec - start.tv_sec;
nanosecs = now.tv_nsec - start.tv_nsec;
if(nanosecs < 0)
{
secs--;
nanosecs += 1000000000;
}
printf("%d.%03d: expirations read: %lu; total=%lu\n",
secs, (nanosecs + 500000)/1000000,(unsigned long)numExp,
(unsigned long)totalExp);
}
return 0;
}
何時使用?
- 進程可以使用設置定時器,以便在經歷指定的一段實際時間後收到信號通知。
- 爲系統調用的阻塞設定時間上限。
- 應用程序如需暫停執行一段特定間隔的實際時間,可以使用各種合適的休眠函數。然後通過 定時器 來喚醒。
參考資料:
- Linux/UNIX系統編程手冊(上冊)