天天看點

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);                             				 如果這裡使用實時時鐘,當手動更改系統時間定時器也會受影響,而遞增時鐘則隻受設定的時間值影響。
 * flags : 可選項包括TFD_NONBLOCK(非阻塞)和TFD_CLOEXEC,阻塞指的是當定時器未逾時的時候,如果調用read(timerfd)會阻塞直
 *  				到定時器逾時,如果設定TFD_NONLOCK,則會直接傳回并傳回-1. 這與套接字描述符類似。
 */
int timerfd_create(int clockid, int flags);                   

2. 啟動和停止定時器

/*
 * 功能 定時器啟動和關閉
 * 傳回值 : 成功傳回0,失敗傳回-1,并存儲錯誤碼到errno
 * 參數: 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 ;
}
           

實驗截圖:

Linux環境程式設計 使用者層定時器使用一 timerfd的使用

代碼路徑:

[email protected]:FuYuanDe/timerfd.git

參考資料:

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

繼續閱讀