当前位置:   article > 正文

timerfd_timerfd_settime

timerfd_settime

timerfd函数


传统网络库的reactor通过控制select和poll,epoll的等待时间(epoll_wait函数的参数)来实现定时。而Linux内核在2.6版本后,新增了timerfd,可以更精确的定时。把这个fd当成普通网络IO事件相同的方式来处理定时。

timerfd是Linux为用户程序提供的一个定时器接口。这个接口基于文件描述符,通过文件描述符的可读事件进行超时通知,替代 setitimer(2) 或 timer_create(2),其优点是文件描述符可以通过 select(2)、poll(2) 和 epoll(7) 进行监控。

用统一的方式来处理IO事件、超时事件。这也是Reactor模式的特点。但传统Reactor模式使用select/poll/epoll 的timeout参数实现定时功能,但其精度只有毫秒(注意区分表示精度和实际精度,表示精度可能为微妙和纳秒)。另外,select/poll/epoll的定时器也有一个缺陷,那就是只能针对的是所有监听的文件描述符fd,而非绑定某个fd。timerfd可以解决这个问题,单独为某个fd指定定时器。

timerfd接口

#include <sys/timerfd.h>

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 (第一次超时时间)*/
};

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);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

timerfd_create()函数创建一个定时器对象,同时返回一个与之关联的文件描述符。
clockid:clockid标识指定的时钟计数器,可选值(CLOCK_REALTIME、CLOCK_MONOTONIC。。。)
CLOCK_REALTIME:系统实时时间,随系统实时时间改变而改变,即从UTC1970-1-1 0:0:0开始计时,中间时刻如果系统时间被用户改成其他,则对应的时间相应改变
CLOCK_MONOTONIC:从系统启动这一刻起开始计时,不受系统时间被用户改变的影响
flags:参数flags(TFD_NONBLOCK(非阻塞模式)/TFD_CLOEXEC(表示当程序执行exec函数时本fd将被系统自动关闭,表示不传递)

timerfd_settime()此函数用于设置新的超时时间,并开始计时,能够启动和停止定时器;
fd: 参数fd是timerfd_create函数返回的文件句柄
flags:参数flags为1代表设置的是绝对时间(TFD_TIMER_ABSTIME 表示绝对定时器);为0代表相对时间。
new_value: 参数new_value指定定时器的超时时间以及超时间隔时间
old_value: 如果old_value不为NULL, old_vlaue返回之前定时器设置的超时时间,具体参考timerfd_gettime()函数
毫秒设置:msec
it_value.tv_sec = msec / 1000;
it_value.tv_nsec = (msec % 1000) * 1000000;

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

可以用read函数读取计时器的超时次数,改值是一个8字节无符号的长整型
eg:
uint64_t exp = 0;
read(fd, &exp, sizeof(uint64_t));

程序例子:

//
// Created by HG10 on 2022/12/23.
//
/********************************************************
* Filename: timerfd.c
* Author: zhangwj
* Desprition: a sample program of timerfd
* Date: 2017-04-17
* Warnning:
********************************************************/
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <stdlib.h>
#include <pthread.h>
#include <errno.h>
#include <sys/epoll.h>
#include <sys/timerfd.h>
#include <unistd.h>

#if 0
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 */
};
#endif

#define EPOLL_LISTEN_CNT        256
#define EPOLL_LISTEN_TIMEOUT    500

#define LOG_DEBUG_ON 1

#ifdef LOG_DEBUG_ON
#define LOG_DEBUG(fmt, args...) \
    do {  \
        printf("[DEBUG]:");\
        printf(fmt "\n", ##args); \
    } while(0);
#define LOG_INFO(fmt, args...) \
    do { \
        printf("[INFO]:");\
        printf(fmt "\n", ##args); \
    } while(0);
#define LOG_WARNING(fmt, args...) \
    do { \
        printf("[WARNING]:");\
        printf(fmt "\n", ##args); \
    } while(0);
#else
#define LOG_DEBUG(fmt, args...)
#define LOG_INFO(fmt, args...)
#define LOG_WARNING(fmt, args...)
#endif
#define LOG_ERROR(fmt, args...) \
    do{ \
        printf("[ERROR]:");\
        printf(fmt "\n", ##args);\
    }while(0);

#define handle_error(msg) \
        do { perror(msg); exit(EXIT_FAILURE); } while (0)

static int g_epollfd = -1;
static int g_timerfd = -1;
uint64_t tot_exp = 0;

static void help(void)
{
    exit(0);
}

static void print_elapsed_time(void)
{
    static struct timespec start;
    struct timespec curr;
    static int first_call = 1;
    int secs, nsecs;

    if (first_call) {
        first_call = 0;
        if (clock_gettime(CLOCK_MONOTONIC, &start) == -1)
            handle_error("clock_gettime");
    }

    if (clock_gettime(CLOCK_MONOTONIC, &curr) == -1)
        handle_error("clock_gettime");

    secs = curr.tv_sec - start.tv_sec;
    nsecs = curr.tv_nsec - start.tv_nsec;
    if (nsecs < 0) {
        secs--;
        nsecs += 1000000000;
    }
    printf("%d.%03d: ", secs, (nsecs + 500000) / 1000000);
}

void timerfd_handler(int fd)
{
    uint64_t exp = 0;

    read(fd, &exp, sizeof(uint64_t));
    tot_exp += exp;
    print_elapsed_time();
    printf("read: %llu, total: %llu\n", (unsigned long long)exp, (unsigned long long)tot_exp);

    return;
}

void epoll_event_handle(void)
{
    int i = 0;
    int fd_cnt = 0;
    int sfd;
    struct epoll_event events[EPOLL_LISTEN_CNT];

    memset(events, 0, sizeof(events));
    while(1)
    {
        /* wait epoll event */
        fd_cnt = epoll_wait(g_epollfd, events, EPOLL_LISTEN_CNT, EPOLL_LISTEN_TIMEOUT);
        for(i = 0; i < fd_cnt; i++)
        {
            sfd = events[i].data.fd;
            if(events[i].events & EPOLLIN)
            {
                if (sfd == g_timerfd)
                {
                    timerfd_handler(sfd);
                }
            }
        }
    }
}

int epoll_add_fd(int fd)
{
    int ret;
    struct epoll_event event;

    memset(&event, 0, sizeof(event));
    event.data.fd = fd;
    event.events = EPOLLIN | EPOLLET;

    ret = epoll_ctl(g_epollfd, EPOLL_CTL_ADD, fd, &event);
    if(ret < 0) {
        LOG_ERROR("epoll_ctl Add fd:%d error, Error:[%d:%s]", fd, errno, strerror(errno));
        return -1;
    }

    LOG_DEBUG("epoll add fd:%d--->%d success", fd, g_epollfd);
    return 0;
}

int epollfd_init()
{
    int epfd;
    /* create epoll fd */
    epfd = epoll_create(EPOLL_LISTEN_CNT);
    if (epfd < 0) {
        LOG_ERROR("epoll_create error, Error:[%d:%s]", errno, strerror(errno));
        return -1;
    }
    g_epollfd = epfd;
    LOG_DEBUG("epoll fd:%d create success", epfd);

    return epfd;
}

int timerfd_init()
{
    int tmfd;
    int ret;
    struct itimerspec new_value;

    new_value.it_value.tv_sec = 2;
    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_MONOTONIC, 0);
    if (tmfd < 0) {
        LOG_ERROR("timerfd_create error, Error:[%d:%s]", errno, strerror(errno));
        return -1;
    }

    ret = timerfd_settime(tmfd, 0, &new_value, NULL);
    if (ret < 0) {
        LOG_ERROR("timerfd_settime error, Error:[%d:%s]", errno, strerror(errno));
        close(tmfd);
        return -1;
    }

    if (epoll_add_fd(tmfd)) {
        close(tmfd);
        return -1;
    }
    g_timerfd = tmfd;
    return 0;
}

int main(int argc, char **argv)
{
    if (epollfd_init() < 0) {
        return -1;
    }

    if (timerfd_init()) {
        return -1;
    }

    /* event handle */
    epoll_event_handle();

    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
  • 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
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188
  • 189
  • 190
  • 191
  • 192
  • 193
  • 194
  • 195
  • 196
  • 197
  • 198
  • 199
  • 200
  • 201
  • 202
  • 203
  • 204
  • 205
  • 206
  • 207
  • 208
  • 209
  • 210
  • 211
  • 212
  • 213
  • 214
  • 215
  • 216
  • 217
  • 218
  • 219
  • 220

参考:
Linux下 timerfd创建定时器并使用 epoll 监听
基于timerfd epoll开发的io定时器 [上]
epoll定时器实现系列文章:linux timerfd系列函数总结,有firecat注释
timerfd_create(2) — Linux 手册页
Linux定时器timerfd用法

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小小林熬夜学编程/article/detail/470984
推荐阅读
相关标签
  

闽ICP备14008679号