Linux之利用文件描述符進行通知的定時器:timerfd API

目錄

 

提問:

系統調用:

1、創建

2、設置

3、獲取定時器時間間隔和剩餘時間

4、timerfd與fork()及exec()之間的交互

5、通過read()從timefd文件描述符讀取到期信息

測試代碼:

何時使用?

參考資料:


提問:

  1. 如何使用timerfd API?
  2. 什麼時候需要使用?

系統調用:

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 爲底層的打開文件描述設置爲非阻塞。

 

  1. 定時器使用完畢後,應調用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) 秒
};

//秒 毫秒 微秒 納秒
  1.       如果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;
}

 

何時使用?

  1. 進程可以使用設置定時器,以便在經歷指定的一段實際時間後收到信號通知。
  2. 爲系統調用的阻塞設定時間上限。
  3. 應用程序如需暫停執行一段特定間隔的實際時間,可以使用各種合適的休眠函數。然後通過 定時器 來喚醒。

參考資料:

  1. Linux/UNIX系統編程手冊(上冊)

 

 

 

 

 

 

 

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章