• 用timerfd_*系列函数代替alarm作为定时器选项


    陈硕在《Linux多线程服务端编程:使用muduo C++网络库》中说到(4.10多线程和signal):多线程程序中,使用signal的第一原则就是不要使用signal。原因是信号处理函数的复杂性和限制性,以及中断处理的困难,可能导致正确程序编写的困难,在这里陈硕举例signal handler函数中修改全局变量但是由于编译器优化导致修改不能被立刻看到的情况,以及多线程中掩码的考虑和signal hander中不能通过codition variable来通知其他线程,不能再信号处理函数中创建其他线程等。

    但是在之前的定时函数的实现中,我都是用alarm+信号处理函数+统一事件源,来处理定时任务。

    所以需要了解一下timerfd_*系列系统调用,以及gettimeofday()函数。

    虽然网上文章不少,但是笔者认为吸收别人的意见并用自己的话写下来和只看别人的文章是不一样的,加上每个人的侧重点也不同,所以有必要写下来。

    参考文章:
    linux timerfd系列函数总结
    Linux的timerfd分析

    一、timerfd_*

    当定时器超时时候,定时器对象代表的文件描述符可读,也就是可以用read函数读取,而且要注意read读取的是uint64_t类型的值,如果不是read可能会读取失败。

    uint64_t exp = 0;
    int ret = read(tmfd, &(exp), sizeof(uint64_t));
    
    • 1
    • 2

    头文件#include

    1.1 int timerfd_create(int clockid, int flags);

    timerfd_create创建一个定时器对象,并返回对应的文件描述符

    • clockid:clockid标识指定的时钟计数器,可选值:
      • CLOCK_REALTIME:系统实时时间,随系统实时时间改变而改变,即从UTC1970-1-1 0:0:0开始计时,中间时刻如果系统时间被用户改成其他,则对应的时间相应改变
      • CLOCK_MONOTONIC:从系统启动这一刻起开始计时,不受系统时间被用户改变的影响
      • 如果不想系统时间的修改导致计时器计时时间的改变,或者不知道选什么的时候,可能CLOCK_MONOTONIC会是一个更好的选择。
    • flags:可选下边两个的与值,timerfd默认是阻塞模式,
      • TFD_NONBLOCK(非阻塞模式)
      • TFD_CLOEXEC(表示当程序执行exec函数时本fd将被系统自动关闭,表示不传递)

    1.2 timerfd_settime()函数

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

    //设置定时器
    struct itimerspec {
        struct timespec it_interval;  /* Interval for periodic timer (定时间隔周期)*/
        struct timespec it_value;     /* Initial expiration (第一次超时时间)*/
    };
    //
    struct timespec {
        time_t tv_sec;                /* Seconds */
        long   tv_nsec;               /* Nanoseconds */
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • timerfd_settime()此函数用于设置新的超时时间,并开始计时,能够启动和停止定时器,停止定时器也就是把时间间隔改为0。
    • fd: 参数fd是timerfd_create函数返回的文件句柄
    • flags:参数flags为1代表设置的是绝对时间(TFD_TIMER_ABSTIME 表示绝对定时器);为0代表相对时间。
    • new_value: 参数new_value指定定时器的超时时间以及超时间隔时间
    • old_value: 如果old_value不为NULL, old_vlaue返回之前定时器设置的超时时间,具体参考timerfd_gettime()函数

    it_interval不为0则表示是周期性定时器,it_value和it_interval都为0表示停止定时器

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

    • timerfd_gettime()函数获取距离下次超时剩余的时间。
    • curr_value.it_value 字段表示距离下次超时的时间,若值为0,表示计时器已经解除,其表示的值永远是一个相对值,无论TFD_TIMER_ABSTIME是否被设置
    • curr_value.it_interval 定时器间隔时间

    例子:

    从第一秒开始,每一秒触发一次。

    #include 
    #include 
    #include "fcntl.h"
    #include "unistd.h"
    #include 
    #include 
    #include 
    
    
    using namespace std;
    
    int main(){
       int tmfd;
       int ret;
       struct itimerspec new_value;
       struct timeval tv;
       gettimeofday(&tv,NULL);
       new_value.it_value.tv_sec = 1;
       new_value.it_value.tv_nsec = 0;
       new_value.it_interval.tv_sec = 1;
       new_value.it_interval.tv_nsec = 0;
       //创建定时器
       tmfd = timerfd_create(CLOCK_REALTIME, TFD_CLOEXEC);
       if (tmfd == -1)
       {
           printf("timerfd_create fail\n");
           return 0;
       }
       //用相对时间设置定时器
       timerfd_settime(tmfd, 0, &new_value, NULL);
    
       for(int i = 0; i != 10; ++i){
           uint64_t exp = 0;
           //由于设置为阻塞,所以read返回的时候定时器一定是被触发了
           int ret = read(tmfd, &(exp), sizeof(uint64_t));
           printf("%d sec, ret = %d, exp = %u\n", i, ret, exp);
       }
       close(tmfd);
       return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
  • 相关阅读:
    nginx服务和uwsgi服务如何设置开机自启动
    SSM整合-异常处理器和项目异常处理方案
    【矩阵】- “之”字形打印
    Docker 部署Harbor 443端口冲突
    [SpringBoot笔记] SpringBoot-01-快速入门案例
    解决线上概率性异常 TransactionTooLargeException
    基于Spring Boot应用Java的Stream流API
    Java版工程项目管理系统平台+企业工程系统源码+助力工程企业实现数字化管理
    微信小程序 java 早茶点餐订餐预定系统 python php
    ES6中扩展运算符的作用
  • 原文地址:https://blog.csdn.net/qq_42370809/article/details/126391200