linux環境編程 定時器timerfd的使用

timerfd是linux提供的定時器機制,基於文件描述符,定時器精度最高可達納秒級別,接口包括定時器創建、啓動定時器、關閉定時器和刪除定時器。下面介紹一下timerfd  API接口和一個結合epoll使用的定時器demo。

1. 創建定時器 

#include <sys/timerfd.h>
/*
 * 功能 : 創建定時器
 * 返回值:成功返回定時器文件描述符,失敗返回-1
 * 參數:  clockid可以是CLOCK_REALTIME(實時時鐘)或者CLOCK_MONOTONIC(遞增時鐘),實時時鐘可以被系統時間改變,後者不會。
 * 				 如果這裏使用實時時鐘,當手動更改系統時間定時器也會受影響,而遞增時鐘則只受設置的時間值影響。
 * flags : 可選項包括TFD_NONBLOCK(非阻塞)和TFD_CLOEXEC,阻塞指的是當定時器未超時的時候,如果調用read(timerfd)會阻塞直
 *  				到定時器超時,如果設置TFD_NONLOCK,則會直接返回並返回-1. 這與套接字描述符類似。
 */
int timerfd_create(int clockid, int flags);             
            

2. 啓動和停止定時器

/*
 * 功能 定時器啓動和關閉
 * 參數: fd: 定時器描述符
 *	 flags: 0 或者TFD_TIMER_ABSTIME,0代表相對時間,即相對於當前時間多少,後者是絕對時間。
 * 	 new_value: 當new_value.it_value非0時,用於設置定時器第一次超時時間,爲0代表停止定時器
		    new_value.it_interval:表示第一次超時後下一次超時的時間,爲0代表定時器只超時一次
 *	 old_value: 如果不爲NULL,則用來存儲當前時間。

int timerfd_settime(int fd, int flags, const struct itimerspec *new_value, struct itimerspec *old_value);  

3. 關閉定時器

/*
 * 功能: 和普通描述符一樣,用完後使用close釋放
 * 參數:timerfd爲timerfd_create()創建的定時器描述符
 */
close(timerfd);

4. 時間結構體

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

下面是一個結合epoll使用的定時器demo,創建一個每隔2s超時的定時器並加入到epoll監聽隊列中,每當定時器到期超時時產生一個讀事件,之後可以去執行相應的定時器回調函數。

/*
 *  Description : linux 應用層編程之定時器timerfd的使用
 *  Date        :20180611
 *  Author      :mason
 *  Mail        : [email protected]
 *
 */

#include <sys/timerfd.h>
#include <sys/epoll.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdio.h>
#include<errno.h>
#include <time.h>

#define TIME_MAX 2 
#define log(fmt, arg...) printf(""fmt, ##arg)

void main() {
    int tfd;    //定時器描述符
    int efd;    //epoll描述符
    int fds, ret;
    uint64_t value;
    struct epoll_event ev, *evptr;
    struct itimerspec time_intv; //用來存儲時間

    tfd = timerfd_create(CLOCK_MONOTONIC, 0);   //創建定時器
    if(tfd == -1) {
        log("create timer fd fail \r\n");
        return ;
    }

    time_intv.it_value.tv_sec = TIME_MAX; //設定2s超時
    time_intv.it_value.tv_nsec = 0;
    time_intv.it_interval.tv_sec = time_intv.it_value.tv_sec;   //每隔2s超時
    time_intv.it_interval.tv_nsec = time_intv.it_value.tv_nsec;

    log("timer start ...\n");
    timerfd_settime(tfd, 0, &time_intv, NULL);  //啓動定時器
    
    efd = epoll_create1(0); //創建epoll實例
    if (efd == -1) {
        log("create epoll fail \r\n");
        close(tfd);
        return ;
    }
    
    evptr = (struct epoll_event *)calloc(1, sizeof(struct epoll_event));
    if (evptr == NULL) {
        log("epoll event calloc fail \r\n");
        close(tfd);
        close(efd);
        return ;
    }

    ev.data.fd = tfd; 
    ev.events = EPOLLIN;    //監聽定時器讀事件,當定時器超時時,定時器描述符可讀。
    epoll_ctl(efd, EPOLL_CTL_ADD, tfd, &ev); //添加到epoll監聽隊列中

    while(1) {
        fds = epoll_wait(efd, evptr, 1, -1);    //阻塞監聽,直到有事件發生
        if(evptr[0].events & EPOLLIN){   
                ret = read(evptr->data.fd, &value, sizeof(uint64_t));
                if (ret == -1) 
                    log("read return -1, errno :%d \r\n", errno);
                else
                    log("*** timer up  *** \n");               
       }            
    }

    return ;
}

實驗截圖:


代碼路徑:

[email protected]:FuYuanDe/timerfd.git

參考資料:

https://linux.die.net/man/2/timerfd_create

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