timerfd API使用总结

21 篇文章 0 订阅

timerfd 介绍

timerfd 是在Linux内核2.6.25版本中添加的接口,其是Linux为用户提供的一个定时器接口。这个接口基于文件描述符,所以可以被用于select/poll/epoll的场景。当使用timerfd API创建多个定时器任务并置于poll中进行事件监听,当没有可响应的事件,则程序阻塞在poll中,当有事件发生,通过poll的这个事件入口,对产生的事件进行响应,从而构成了一个事件轮训程序。

timerfd 相关函数

#include <time.h>
int clock_gettime(clockid_t clockid, struct timespec *tp);

clock_gettime函数主要用于获取系统时间,精确到纳秒级别。在编译时需要添加-lrt库,clockid_t clockid指定用何种模式获取时间,struct timespec *tp用于存储获取到的时间。其中clockid主要有如下常用的参数:
CLOCK_REALTIME:系统实时时间,随系统实时时间改变而改变,即从UTC1970-1-1 0:0:0开始计时,中间时刻如果系统时间被用户改成其他,则对应的时间相应改变
CLOCK_MONOTONIC:从系统启动这一刻起开始计时,不受系统时间被用户改变的影响
  CLOCK_PROCESS_CPUTIME_ID:本进程到当前代码系统CPU花费的时间
  CLOCK_THREAD_CPUTIME_ID:本线程到当前代码系统CPU花费的时间

#include <sys/timerfd.h>
int timerfd_create(int clockid, int flags);
int timerfd_settime(int fd, int flags, const struct itimerspec *new_value,struct itimerspec *old_value);
int timerfd_gettime(int fd, struct itimerspec *curr_value);
  • timerfd_create函数主要用于生成一个定时器对象,返回与之关联的文件描述符,clockid可以设置CLOCK_REALTIME和CLOCK_MONOTONIC,flags可以设置为TFD_NONBLOCK(非阻塞),TFD_CLOEXEC(同O_CLOEXEC)
  • timerfd_settime用于启动和停止定时器,fd为timerfd_create获得的定时器文件描述符,flags为0表示是相对定时器,为TFD_TIMER_ABSTIME表示是绝对定时器。const struct itimerspec *new_value表示设置超时的时间。
    其数据结构:
  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 */
  };

需要注意的是itimerspec 结构成员表示的意义:
it_value是首次超时时间,需要填写从clock_gettime获取的时间,并加上要超时的时间。 it_interval是后续周期性超时时间,是多少时间就填写多少。
it_interval不为0则表示是周期性定时器。
it_value和it_interval都为0表示停止定时器。

  • timerfd_gettime此函数用于获得定时器距离下次超时还剩下的时间。如果调用时定时器已经到期,并且该定时器处于循环模式(设置超时时间时struct itimerspec::it_interval不为0),那么调用此函数之后定时器重新开始计时。

参考示例

示例一


int tu_set_timer(tu_timer_t * timer, uint64_t milliseconds, bool continious, timer_handler_cb_t timer_handler_cb, void * timer_handler_arg)
{
    int fd;
    struct itimerspec its;
    //创建的定时器    
    fd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK);
    if (fd == -1)
    {
        LOG_PRINT("Error creating timer");
        return -1;
    }
    //设置超时时间
    its.it_value.tv_sec = (milliseconds * 1000000) / 1000000000;
    its.it_value.tv_nsec = (milliseconds * 1000000) % 1000000000;
    //如果是周期定时器,则设置it_interval,如果不是则为0
    its.it_interval.tv_sec = continious ? its.it_value.tv_sec : 0;
    its.it_interval.tv_nsec = continious ? its.it_value.tv_nsec : 0;
    //设置定时到达后的响应函数及其函数参数
    timer->timer_handler_cb = timer_handler_cb;
    timer->timer_handler_arg = timer_handler_arg;
    timer->continious = continious;//标记是否为循环周期定时器
    //启动定时器,并将文件描述符添加到poll中监听
    if ((timerfd_settime(fd, 0, &its, NULL) == 0) 
        && ((timer->fd_index = polling_define_poll_fd(fd, POLLIN, tu_timer_handler, timer)) != -1))
    {
        timer->in_use = true;
        return 0;
    }
    close(fd);
    return -1;
}

示例二


int tu_set_timer_realtime(tu_timer_t * timer, uint64_t milliseconds, bool continious, timer_handler_cb_t timer_handler_cb, void * timer_handler_arg)
{
    int fd;
    struct itimerspec its;
    struct timespec now;
    time_t tv_sec;
    long tv_nsec;
    //获取绝对时间
    if(clock_gettime(CLOCK_REALTIME,&now) == -1)
    {
        LOG_PRINT("Error clock_gettime timer\n");
        return -1;
    }
    //创建定时器,非阻塞方式   
    fd = timerfd_create(CLOCK_REALTIME, TFD_NONBLOCK);
    if (fd == -1)
    {
        LOG_PRINT("Error creating timer\n");
        return -1;
    }
    //计算时间
    tv_sec = (milliseconds * 1000000) / 1000000000;
    tv_nsec = (milliseconds * 1000000) % 1000000000;

    //设置到期时间
    its.it_value.tv_sec  = now.tv_sec + tv_sec;
    its.it_value.tv_nsec = now.tv_nsec + tv_nsec;

    //如果使用循环模式,设置循环间隔
    its.it_interval.tv_sec = continious ? tv_sec : 0;
    its.it_interval.tv_nsec = continious ? tv_nsec : 0;
    //设置定时到达后的响应函数及其函数参数
    timer->timer_handler_cb = timer_handler_cb;
    timer->timer_handler_arg = timer_handler_arg;
    timer->continious = continious;
    //启动定时器,并将文件描述符添加到poll中监听
    if ((timerfd_settime(fd,TFD_TIMER_ABSTIME, &its, NULL) == 0) && ((timer->fd_index = polling_define_poll_fd(fd, POLLIN, tu_timer_handler, timer)) != -1))
    {
        timer->in_use = true;
        LOG_PRINT("tu_set_timer_realtime--\n");
        return 0;
    }

    LOG_PRINT("Error setting timer\n");
    close(fd);
    return -1;
}

poll中的回调函数

void tu_timer_handler(void * arg)
{
    tu_timer_t * timer = arg;
    uint64_t exp;
    if (timer->continious)//重复定时器
    {
        if (read(polling_fds[timer->fd_index].fd, &exp, sizeof(uint64_t)) != sizeof(uint64_t))
        {
            LOG_PRINT("%p ERROR timer read. Killing timer.\n", timer);
            tu_kill_timer(timer);
        }
    }
    else
    {
        tu_kill_timer(timer);//关闭定时器
    }
    //调用定时器处理函数
    timer->timer_handler_cb(timer->timer_handler_arg);
}
  • 2
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
timerfd_settime函数是一个Linux系统调用函数,用于创建并设置一个用于定时器的文件描述符。该函数需要传入timerfd文件描述符、设置定时器的参数(包括定时器到期的时间和定时器触发的时间间隔)以及flags参数。 使用该函数的步骤如下: 1. 创建一个timerfd文件描述符,可以使用系统调用函数timerfd_create来创建。 2. 定义一个struct itimerspec结构体,用于设置定时器的参数。该结构体包含两个成员:it_interval和it_value。it_interval表示定时器触发的时间间隔,it_value表示定时器到期的时间。 3. 调用timerfd_settime函数,将定时器参数设置到timerfd文件描述符中。 4. 读取timerfd文件描述符中的数据,当定时器到期时,该文件描述符会变为可读状态,可以使用read函数从中读取数据。 下面是一个简单的例子,演示如何使用timerfd_settime函数: ```c #include <sys/timerfd.h> #include <unistd.h> #include <stdlib.h> #include <stdio.h> #include <stdint.h> int main() { int timer_fd; uint64_t exp; // 创建一个timerfd文件描述符 timer_fd = timerfd_create(CLOCK_MONOTONIC, 0); if (timer_fd == -1) { perror("timerfd_create"); exit(EXIT_FAILURE); } // 设置定时器参数 struct itimerspec timer_value; timer_value.it_interval.tv_sec = 1; // 定时器触发间隔1秒 timer_value.it_interval.tv_nsec = 0; timer_value.it_value.tv_sec = 5; // 定时器5秒后到期 timer_value.it_value.tv_nsec = 0; // 设置定时器参数到timerfd文件描述符中 if (timerfd_settime(timer_fd, 0, &timer_value, NULL) == -1) { perror("timerfd_settime"); exit(EXIT_FAILURE); } // 读取timerfd文件描述符中的数据 ssize_t s = read(timer_fd, &exp, sizeof(uint64_t)); if (s != sizeof(uint64_t)) { perror("read"); exit(EXIT_FAILURE); } printf("Timer expired\n"); close(timer_fd); exit(EXIT_SUCCESS); } ``` 在上述代码中,我们创建了一个定时器,设置为5秒后到期,每隔1秒触发一次。当定时器到期时,程序会打印一条消息,然后退出。 注意,在这个例子中,我们只读取了一次timerfd文件描述符中的数据,也就是说,程序只会等待定时器到期一次。如果需要等待多次定时器到期,需要在读取完数据后重新设置定时器参数。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值