欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Linux环境编程 用户层定时器使用一 timerfd的使用

程序员文章站 2022-06-09 11:53:54
...

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. 启动和停止定时器

/*
 * 功能 定时器启动和关闭
 * 参数: 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        : aaa@qq.com
 *
 */

#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的使用

代码路径:

aaa@qq.com:FuYuanDe/timerfd.git

参考资料:

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