timerfd

始于版本2.6.25,Linux内核提供了另一种创建定时器的API。Linux特有的timefd API,可从文件描述符中读取其所创建定时器的到期通知。因为可以使用select()、poll()和epoll()将这种文件描述符会同其他描述符一同进行监控,所以非常实用。

       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);

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

这组API中的3个新系统调用第一个系统调用是timerfd_create(),它会创建一个新的定时器对象,并返回一个指代对象的文件描述符。参数clockid可以设置为CLOCK_REALTIME或CLOCK_MONOTONIC。

CLOCK_REALTIME:可设定的系统级实时时钟。用于度量真实时间。

CLOCK_MONOTONIC:不可设定的恒定态时钟。对时间的度量始于“未予规范的古曲某一时间点”,系统启动后就不会发生改变。该时钟适用于那些无法忍受系统时钟发生跳跃性变化的应用程序。LInux上,这种时钟对时间的测量始于系统启动。

对上述的两个类型做个简单测试:

#include <stdio.h>
#include <time.h>

int main()
{
    struct timespec realtime_time;
    struct timespec monotonic_time;

    clock_gettime(CLOCK_REALTIME, &realtime_time);
    clock_gettime(CLOCK_MONOTONIC, &monotonic_time);

    printf("%d\n", time(NULL));
    printf("realtime s:%d ns:%ld\n", realtime_time.tv_sec, realtime_time.tv_nsec);
    printf("monotonic s:%d ns:%ld\n", monotonic_time.tv_sec, monotonic_time.tv_nsec);
}

加-lrt编译执行,执行./clock ; cat /proc/uptime

1604536414
realtime s:1604536414 ns:127853247
monotonic s:6039260 ns:752312307
6039260.75 360242290.69

可以看到,使用CLOCK_REALTIME输出的时间为从Epoch以来的秒数;CLOCK_MONOTONIC输出的时间为系统启动时间。

timerfd_create()的flags参数从Linux内核2.6.27版本开始支持设置为两种flags:TFD_CLOEXEC和TFD_NONBLOCK。TFD_CLOEXEC就是设置close-on-exec,TFD_NONBLOCK设置为非阻塞的。close-on-exec可以参看之前的博客close-on-exec 。

timefd_create()创建的定时器使用完毕后,应调用close()关闭相应的文件描述符,以便于内核能够释放与定时器相关的资源。

系统调用timefd_settime()可以配备(启动)或解除(停止)由文件描述符fd所指代的定时器。参数new_value为定时器指定新设置。参数old_value可用来返回定时器的前一设置。如果不关心定时器的前一设置,可将old_value置为NULL。参数flags可以是0,此时将new_value.it_value的值视为相对于调用timerfd_settime()时间点的相对时间,也可以设为TFD_TIMER_ABSTIME,将其视为一个绝对时间。

itimerspec:it_value指定了定时器首次到期的时间。如果it_interval在任一子字段非0,那么这就是一个周期性定时器,在经历了由it_value指定的初次到期后,会按这些子字段指定的频率周期性到期。如果it_interval的下属字段均为0,那么这个定时器将只到期一次。

系统调用timerfd_gettime()返回文件描述符fd所标识定时器的间隔及剩余时间。间隔以及距离下次到期的时间均返回curr_value指向的结构itimerspec中。即使是以TFD_TIMER_ABSTIME标志创建的绝对时间定时器,curr_value.it_value字段中返回的意义也会保持不变。如果返回的结构curr_value.it_value中所有字段值均为0,那么该定时器已经被解除。如果返回的结构curr_value.it_interval中两字段值均为0,那么定时器只会到期一次,到期时间在curr_value.it_value中给出。

一旦以timerfd_settime()启动了定时器,就可以从相应文件描述符中调用read()来读取定时器的到期信息,出于这一目的,传给read()的缓冲区必须足以容纳一个无符号8字节整形(uint64_t)数。在上次使用timerfd_settime()修改设置以后,或是最后一次执行read()后,如果发生了一起到多起定时器到期时间,那么read()会立即返回,且返回的缓冲区中包含了已发生的到期次数。

一个非常简单的例子:

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

int main()
{
    struct itimerspec ts;
    ts.it_value.tv_sec = 1;
    ts.it_value.tv_nsec = 0;
    ts.it_interval.tv_sec = 1;
    ts.it_interval.tv_nsec = 0;

    int fd = timerfd_create(CLOCK_MONOTONIC, 0);
    if (fd == -1)
    {
        perror("timerfd_create");
        exit(EXIT_FAILURE);
    }

    if (timerfd_settime(fd, 0, &ts, NULL) == -1)
    {
        perror("timerfd_settime");
        exit(EXIT_FAILURE);
    }

    for (int i = 0; i < 100; i++)
    {
        uint64_t numExp;
        ssize_t s = read(fd, &numExp, sizeof(uint64_t));
        if (s != sizeof(uint64_t))
        {
            perror("read");
            exit(EXIT_FAILURE);
        }
        printf("hello world\n");
    }

    exit(EXIT_SUCCESS);
}

该程序每隔1s打印一次hello world,打印100次后退出。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值