Linux之利用文件描述符进行通知的定时器:timerfd API

目录

 

提问:

系统调用:

1、创建

2、设置

3、获取定时器时间间隔和剩余时间

4、timerfd与fork()及exec()之间的交互

5、通过read()从timefd文件描述符读取到期信息

测试代码:

何时使用?

参考资料:


提问:

  1. 如何使用timerfd API?
  2. 什么时候需要使用?

系统调用:

1、创建

#include <sys/timerfd.h>

int timerfd_create(int clockid, int flags)

成功返回文件描述符,失败返回-1 。
参数clockid
1、设置为CLOCK_REALTIME  可设定的系统级实时时钟,用于度量真实时间。
2、设置为CLOCK_MONOTONIC 不可设定的恒定态时钟,始于“未予规范的过去某一时间点”,系统启动后就不会改变。该时钟适用于那些无法容忍系统时钟发生跳跃性变化(如手工改变了系统时间)的应用程序。
参数flags  一般设置为0,Linux内核2.6.27版本后支持下面两种flags标志
1、TFD_CLOEXEC  为新文件描述符设置运行时关闭标志
2、TFD_NONBLOCK 为底层的打开文件描述设置为非阻塞。

 

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

2、设置

#include <sys/timerfd.h>

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

参数new_value 为定时器指定新设置。
参数old_value 可用来返回定时器的前一设置
参数flags
1、可以为0,将new_value.it_value的值视为相对于调用timerfd_settime()时间点的相对时间。
2、可以为TFD_TIMER_ABSTIME,将其视为一个绝对时间(从时钟值0开始测量)。一旦时钟超过了这一时间,定时器会立即到期。

结构体:

struct itimerspec
{
    struct timespec it_interval;        //定时器的周期性到期时间
    struct timespec it_value;           //第一次到期时间
};

struct timespec
{
    time_t tv_sec;          //秒
    long   tv_nsec;        //纳秒  = 10^(-9) 秒
};

//秒 毫秒 微秒 纳秒
  1.       如果it_interval的下属字段均为0,那么这个定时器将只到期一次。

 

3、获取定时器时间间隔和剩余时间

#include <sys/timerfd.h>

int timerfd_gettime(int fd, struct itimerspec * curr_value);

成功返回0,失败返回-1 。

 

4、timerfd与fork()及exec()之间的交互

  • 调用fork()期间,子进程会继承timerfd_create()所创建文件描述符的拷贝。这些描述符与父进程的对应描述符均指代相同的定时器对象,任一进程都可读取定时器的到期信息。
  • timerfd_create()创建的文件描述符能够跨越exec()得以保存(除非将文件描述符设置为运行时关闭),已配备的定时器在exec()之后会继续生成到期通知。

5、通过read()从timefd文件描述符读取到期信息

  • 一旦用timerfd_create()启动了定时器,就可以从相应文件描述符调用read()来读取定时器到期信息。出于这一目的,传给read()的缓冲区必须足以容纳一个无符号8字节整形数。
  • 在上次使用timerfd_settime()修改设置以后,或是最后一次执行read()后,如果发生了一起到多起定时器到期事件,那么read()会立即返回,且返回的缓冲区包含了已经发生的到期次数。如果无定时器到期,read()会一直阻塞直至产生下一个到期。可以就将文件描述符设置为O_NONBLOCK标志,这时读操作是非阻塞式的,且如果没有定时器到期,则返回错误,并将errno值置为EAGAIN。
  • 可以利用select()、poll()、epoll()对timefd文件描述符进行监控。如果定时器到期,会将对应的文件描述符标记为可读。

 

测试代码:

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

int main()
{
    struct itimerspec ts;
    struct timespec start, now;
    int secs, nanosecs;

    memset(&ts, 0, sizeof(struct itimerspec));

    ts.it_value.tv_sec = 0;
    ts.it_value.tv_nsec = 10000000;    //10ms一次
    ts.it_interval.tv_sec = 0;
    ts.it_interval.tv_nsec = 5000000;   //5ms一次

    int fd = timerfd_create(CLOCK_REALTIME, TFD_NONBLOCK);    //设置为非阻塞
    if(-1 == fd)
        perror("timerfd_create");
  
    if(timerfd_settime(fd, 0, &ts, NULL) == -1)
        perror("timefd_settime");
    
    if(clock_gettime(CLOCK_MONOTONIC, &start) == -1)
        perror("clock_gettime");

    ssize_t s;
    uint64_t numExp, totalExp, maxExp = 10;

    for(totalExp = 0; totalExp < maxExp;)
    {
        s = read(fd, &numExp, sizeof(uint64_t));
        if(s != sizeof(uint64_t))
            perror("read");

        
        totalExp += numExp;

        if(clock_gettime(CLOCK_MONOTONIC, &now) == -1)
            perror("clock_gettime");

        secs = now.tv_sec - start.tv_sec;
        nanosecs = now.tv_nsec - start.tv_nsec;
        if(nanosecs < 0)
        {
            secs--;
            nanosecs += 1000000000;
        }
        
        printf("%d.%03d: expirations read: %lu; total=%lu\n",
               secs, (nanosecs + 500000)/1000000,(unsigned long)numExp, 
               (unsigned long)totalExp);
    }
    return 0;
}

 

何时使用?

  1. 进程可以使用设置定时器,以便在经历指定的一段实际时间后收到信号通知。
  2. 为系统调用的阻塞设定时间上限。
  3. 应用程序如需暂停执行一段特定间隔的实际时间,可以使用各种合适的休眠函数。然后通过 定时器 来唤醒。

参考资料:

  1. Linux/UNIX系统编程手册(上册)

 

 

 

 

 

 

 

 

 

 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值