timerfd

作者: wayyyy | 来源:发表于2020-12-30 11:35 被阅读0次

    Linux内核于内核2.6之后提供了一种创建定时器的方法,那就是Linux特有的timerfd文件描述符(一切皆文件),这也意味着可以用多路复用来监听并读取。该接口共有3个API。

    • timerfd_create

      #include <sys/timerfd.h>
      int timerfd_create(int clockid, int flags);
      
      成功返回一个指代该对象的文件描述符, 失败返  回-1及errno
      
      • 第一个cokckid 可以设置为:CLOCK_REALTIMECLOCK_MONOTONIC
        • CLOCK_REALTIME 表示可设定的系统级别时钟,更改系统时间会更改获取的值。也就是说,它以系统时间为坐标。
        • CLOCK_MONOTONIC
          不可设定的恒定态时钟,更改系统时间对齐没有影响。
      • 第二个参数flags, 支持TFD_CLOEXECTFD_NONBLOCK0
        • TFD_CLOEXEC 为新的文件描述符设置运行时关闭标志。(待补充)
        • TFD_NONBLOCK 为底层的打开文件描述符设置 O_NONBLOCK 标志, 随后的读操作将是非阻塞的,
    • timerfd_settime

      #include <sys/timerfd.h>
      
      int timerfd_settime(int fd, 
                          int flags, 
                          const struct itimerspec* new_value, 
                          struct itimerspec* old_value);
      
      成功返回0, 失败返回-1和 errno
      

      它的作用是启动或者停止由文件描述符fd指定的定时器。第二个参数和第三个参数都是struct timespec类型。

      struct itimerspec 
      {
          struct timespec it_interval;   //间隔时间
          struct timespec it_value;      //第一次到期时间
      };
      
      struct timespec 
      {
          time_t tv_sec;    //秒
          long tv_nsec;    //纳秒
      };
      
      • new_value 为定时器指定新设置新的超时时间,若 newValue.it_value非 0 则启动定时器,否则关闭定时器。若 newValue.it_interval 为 0 则定时器只定时一次,否则之后每隔设定时间超时一次。
      • old_value 用来返回定时器的前一设置, 如果不关心, 可将其设置为 NULL,不为 NULL 时则返回定时器这次设置之前的超时时间。
    • timerfd_gettime

      #include <sys/timerfd.h>
      int timerfd_gettime(int fd, struct itimerspec *curr_value);
      
      成功返回0, 失败返回-1和errno
      

      返回文件描述符 fd 所标识定时器的间隔及剩余时间。
      间隔和距离下次到期的时间均返回到 curr_value 指向的结构体。
      如果返回的结构中 curr_value.it_value 中所有字段值均为0, 那么该定时器已经解除, 如果返回的结构 curr_value.it_interval 中两字段值均为0, 那么该定时器只会到期一次, 到期时间在 curr_value.it_value 中给出

    配合使用epoll监听定时器
    #include <time.h>
    #include <sys/epoll.h>
    #include <unistd.h>
    #include <assert.h>
    using namespace std;
    
    const int MAXNUM = 20;
    
    int main(int argc, char *argv[])
    {
        struct itimerspec new_value;
        struct timespec now;
        uint64_t exp;
        ssize_t s;
    
        int ret = clock_gettime(CLOCK_REALTIME, &now);//获取时钟时间
        assert(ret != -1);
    
        new_value.it_value.tv_sec = 5; //第一次到期的时间
        new_value.it_value.tv_nsec = now.tv_nsec; 
    
        new_value.it_interval.tv_sec = 1;      //之后每次到期的时间间隔
        new_value.it_interval.tv_nsec = 0;
    
        int timefd = timerfd_create(CLOCK_REALTIME, TFD_NONBLOCK); // 构建了一个定时器
        assert(timefd != -1);
    
        ret = timerfd_settime(timefd, 0, &new_value, NULL);//启动定时器
        assert(ret != -1);
    
        cout << "timer started" << endl; // 定时器开启啦!
    
    
        int epollfd = epoll_create(1);  //创建epoll实例对象
    
        struct epoll_event ev;
        struct epoll_event events[MAXNUM];
        ev.data.fd = timefd;
        ev.events = EPOLLIN | EPOLLET;
        epoll_ctl(epollfd, EPOLL_CTL_ADD, timefd, &ev); //添加到epoll事件集合
    
        for (; ;) 
        {
            int num = epoll_wait(epollfd, events, MAXNUM, 0);
            assert(num >= 0);
    
            for (int i = 0; i < num; i++) 
            {
                if (events[i].events & EPOLLIN) 
                {
                    //....处理其他事件
                    if (events[i].data.fd == timefd) 
                    {
                        s = read(events[i].data.fd, &exp, sizeof(uint64_t)); //需要读出uint64_t大小, 不然会发生错误
                        assert(s == sizeof(uint64_t));
                        cout << "here is timer" << endl;
                    }
                }
            }
        }
    
        close(timefd);
        close(epollfd);
    
        return 0;
    }
    

    参考资料
    1.https://blog.csdn.net/weixin_36888577/article/details/81570793

    相关文章

      网友评论

          本文标题:timerfd

          本文链接:https://www.haomeiwen.com/subject/dfaadhtx.html