• 【用示例学习与理解C++系列】timerfd与epoll的使用


    代码示例

    代码运行环境:Android , 开发工具AS,用AS创建一个JIN工程,然后copy示例代码覆盖工程的natvie-lib.cpp文件即可
    代码逻辑说明:创建一个首次超时时间2S,后续每间隔50毫秒超时的定时器文件符实例,超时20次后停止

    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    
    using namespace std;
    
    static char *TAG = "JNI-DEMO";
    static static int MAX_EVENTS_SIZE = 1024;
    
    
    void demo_timerfd() {
        int timerFd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK);
        if (timerFd == -1) {
            __android_log_print(ANDROID_LOG_WARN, TAG, "timerfd_create fail");
            return;
        }
    
        int epollFd = epoll_create(MAX_EVENTS_SIZE);
        if (epollFd == -1) {
            __android_log_print(ANDROID_LOG_WARN, TAG, "epoll_create fail");
            return;
        }
        struct epoll_event ev;
        ev.data.fd = timerFd;
        ev.events = EPOLLIN;
        int ret = epoll_ctl(epollFd, EPOLL_CTL_ADD, timerFd, &ev);
        if (ret == -1) {
            __android_log_print(ANDROID_LOG_WARN, TAG, "epoll_ctl fail");
        }
    
        struct itimerspec spec;
        //定时器周期超时时间
        spec.it_interval.tv_sec = 0;
        //nsec是纳秒,每50ms超时
        spec.it_interval.tv_nsec = 50 * 1000 * 1000; // 1ms = 1000000ns
        //定时器首次超时时间,2S后
        spec.it_value.tv_sec = 2;
        spec.it_value.tv_nsec = 0;
    
        //启动定时间
        if (timerfd_settime(timerFd, 0, &spec, nullptr) == -1) {
            __android_log_print(ANDROID_LOG_WARN, TAG, "start timer fail");
            return;
        }
        __android_log_print(ANDROID_LOG_INFO, TAG, "start timer");
    
        thread threadEpoll([epollFd, timerFd] {
            struct epoll_event evs[MAX_EVENTS_SIZE];
    
            int timeOutCount = 0;
            while (true) {
                int count = epoll_wait(epollFd, evs, MAX_EVENTS_SIZE, -1);
                __android_log_print(ANDROID_LOG_INFO, TAG, "epoll_wait, count:%d", count);
                for (int i = 0; i < count; i++) {
                    if (evs[i].data.fd == timerFd && evs[i].events && EPOLLIN) {
                        uint64_t value = 0;
                        int ret = 0;
                        //!!!注意需要作读取的操作,否侧会不停的触发超时/可读事件,只有做了读取的操作才算消费了超时事件
                        ret = read(timerFd, &value, sizeof(uint64_t));
                        __android_log_print(ANDROID_LOG_INFO, TAG, "value:%u, ret:%d", value, ret);
                        timeOutCount++;
                        if (timeOutCount == 19) {
                            struct itimerspec spec;
                            spec.it_interval.tv_sec = 0;
                            spec.it_interval.tv_nsec = 0;
                            spec.it_value.tv_sec = 0;
                            spec.it_value.tv_nsec = 0;
                            if (timerfd_settime(timerFd, 0, &spec, nullptr) == -1) {
                                __android_log_print(ANDROID_LOG_WARN, TAG, "stop timer fail");
                                return;
                            }
                            __android_log_print(ANDROID_LOG_INFO, TAG, "stop timer, timeOutCount:%d", timeOutCount);
                        }
                    }
                }
            }
        });
        threadEpoll.detach();
    }
    
    extern "C" JNIEXPORT jstring JNICALL
    Java_com_example_myapplication_MainActivity_stringFromJNI(
            JNIEnv *env,
            jobject /* this */) {
        std::string hello = "Hello from C++";
        demo_timerfd();
        return env->NewStringUTF(hello.c_str());
    }
    
    • 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
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93

    运行效果
    在这里插入图片描述

    使用注意点

    epoll_wait到超时事件后,需要从timefd读数据,否侧可读/超时事件会一直触发!!!!
    如上示例代码如果如下读操作的代码删除,会发现定时器似乎不按指定的超时参数工作,一下就epoll_wait到了20次,然后很快执行到了停止定时间的代码

                        ret = read(timerFd, &value, sizeof(uint64_t));
    
    • 1

    运行结果如下:
    在这里插入图片描述

    timerfd的说明

    timerfd简单的一句话描述就是定时器于文件描述符的形式去使用,即定时器触发超时时会住其绑定的文件描述符写数据,然后我们就可以从文件描述读取出数据,就是专门提供给epoll编程模式下来使用了定时器了
    在这里插入图片描述
    相关的接口描述更是简结明了:通过文件描述符通知超时事件的定时器!

    eventfd的头文件

    #include
    主要是创建timerfd实例,以及设备定时器的启动参数,获取定时器当前的配置参数等

    #if __ANDROID_API__ >= 19
    int timerfd_create(clockid_t __clock, int __flags) __INTRODUCED_IN(19);
    #endif /* __ANDROID_API__ >= 19 */
    
    
    /** The timerfd_settime() flag to use absolute rather than relative times. */
    #define TFD_TIMER_ABSTIME (1 << 0)
    /** The timerfd_settime() flag to cancel an absolute timer if the realtime clock changes. */
    #define TFD_TIMER_CANCEL_ON_SET (1 << 1)
    
    /**
     * [timerfd_settime(2)](http://man7.org/linux/man-pages/man2/timerfd_settime.2.html) starts or
     * stops a timer.
     *
     * Returns 0 on success, and returns -1 and sets `errno` on failure.
     *
     * Available since API level 19.
     */
    
    #if __ANDROID_API__ >= 19
    int timerfd_settime(int __fd, int __flags, const struct itimerspec* __new_value, struct itimerspec* __old_value) __INTRODUCED_IN(19);
    
    /**
     * [timerfd_gettime(2)](http://man7.org/linux/man-pages/man2/timerfd_gettime.2.html) queries the
     * current timer settings.
     *
     * Returns 0 on success, and returns -1 and sets `errno` on failure.
     *
     * Available since API level 19.
     */
    int timerfd_gettime(int __fd, struct itimerspec* __current_value) __INTRODUCED_IN(19);
    #endif /* __ANDROID_API__ >= 19 */
    
    • 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

    启动与停止定时器

    通过timerfd_settime接口启动与停止定时器,设备所有时间参数为零,即可暂停定时器。注意时间参数是秒 + 纳秒,即定时间的精度可以到纳秒!

        struct itimerspec spec;
        //定时器周期超时时间
        spec.it_interval.tv_sec = 0;
        //nsec是纳秒,每50ms超时
        spec.it_interval.tv_nsec = 50 * 1000 * 1000; // 1ms = 1000000ns
        //定时器首次超时时间,2S后
        spec.it_value.tv_sec = 2;
        spec.it_value.tv_nsec = 0;
    
        //启动定时器
        if (timerfd_settime(timerFd, 0, &spec, nullptr) == -1) {
            __android_log_print(ANDROID_LOG_WARN, TAG, "start timer fail");
            return;
        }
    
    //暂停定时器
      struct itimerspec spec;
      spec.it_interval.tv_sec = 0;
      spec.it_interval.tv_nsec = 0;
      spec.it_value.tv_sec = 0;
      spec.it_value.tv_nsec = 0;
      if (timerfd_settime(timerFd, 0, &spec, nullptr) == -1) {
          __android_log_print(ANDROID_LOG_WARN, TAG, "stop timer fail");
          return;
       }
    
    • 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

    参考文档

  • 相关阅读:
    操作系统——管程、死锁及其处理策略
    python实现中缀表达式转后缀表达式
    Git【入门】从安装到会用(千字总结●超详细)
    计算机网络学习笔记(Ⅱ):物理层
    【漏洞复现】广联达OA漏洞合集(信息泄露+SQL注入+文件上传)
    Hadoop面试题
    Monkey二次开发 -- Monkey jar包构建
    干货分享 | GBase 8c 分布式核心技术初揭秘—CDC数据同步
    单点登录常用协议原理和流程
    NCO IP Core
  • 原文地址:https://blog.csdn.net/SCHOLAR_II/article/details/127445837