如何正确使用timerfd?

3 c linux timer zeromq

我与 zmq 一起使用timerfd

如何使用计时器timerfd_createtimerfd_set等待一秒钟(https://man7.org/linux/man-pages/man2/timerfd_create.2.html)?

我已经浏览了链接,但我仍然不明白如何初始化一个计时器,该计时器在创建和设置时每滴答等待一秒。这正是我的任务:

我们用 启动一个计时器timerfd_create(),即 1/秒。滴答作响。当使用计时器设置计时器时,_set_(..)计数器会简单地增加,而每次滴答都会减少。当计数器达到 0 时,计时器就到期了。

在这个项目中,我们有一个函数timer _ set _(),其中定时器是通过函数timerfd_create和设置的timerfd_settimer()。我希望你可以帮助我。

这是我的进度(我的代码的一部分):

    struct itimerspec timerValue;

    g_items[n].socket = nullptr; 
    g_items[n].events = ZMQ_POLLIN;

    g_items[n].fd = timerfd_create(CLOCK_REALTIME, 0);
    if(g_items[n].fd == -1 ){
        printf("timerfd_create() failed: errno=%d\n", errno);
        return -1;
    }  

    timerValue.it_value.tv_sec = 1;
    timerValue.it_value.tv_nsec = 0;
    timerValue.it_interval.tv_sec = 1;
    timerValue.it_interval.tv_nsec = 0;

    timerfd_settime(g_items[n].fd,  0, &timerValue, NULL); 
Run Code Online (Sandbox Code Playgroud)

Rob*_*oni 7

出现有关正确设置计时器超时的问题。

随着设置

timerValue.it_value.tv_sec = 1;
timerValue.it_value.tv_nsec = 0;
timerValue.it_interval.tv_sec = 1;
timerValue.it_interval.tv_nsec = 0;
Run Code Online (Sandbox Code Playgroud)

您正确地将初始超时设置为 1 秒(字段timerValue.it_value)。但你也设置了1s的周期间隔,并且你没有提到这样做的意愿。


关于超时

手册的以下段落描述了此行为:

int timerfd_create(int clockid, int flags);

new_value.it_value指定计时器的初始到期时间,以秒和纳秒为单位。将 的任一字段设置new_value.it_value为非零值即可启动计时器。
将 的两个字段设置new_value.it_value为零可解除定时器。

将 的一个或两个字段设置new_value.it_interval为非零值指定初始到期后重复计时器到期的时间段(以秒和纳秒为单位)。如果 的两个字段new_value.it_interval均为零,则计时器仅在 指定的时间到期一次new_value.it_value

最后一段的重点是我的,因为它展示了如何才能拥有单次计时器。


的好处timerrfd。如何检测定时器到期?

提供的主要优点timerfd是计时器与文件描述符相关联,这意味着它

可以通过select(2)poll(2)epoll(7)来监测。

另一个答案中包含的信息read()也是有效的:我们只是说,即使使用诸如 之类的函数select()read()也需要 function 才能使用文件描述符中的数据。


一个完整的例子

在下面的演示程序中,设置了4秒的超时;之后设置 5 秒的周期间隔。

good oldselect()用于等待定时器到期,并read()用于消费数据(即到期的超时次数;我们将忽略它)。

#include <stdio.h>
#include <sys/timerfd.h>
#include <sys/select.h>
#include <time.h>

int main()
{
    int tfd = timerfd_create(CLOCK_REALTIME,  0);
    
    printf("Starting at (%d)...\n", (int)time(NULL));
    
    if(tfd > 0)
    {
        char dummybuf[8];
        struct itimerspec spec =
        {
            { 5, 0 }, // Set to {0, 0} if you need a one-shot timer
            { 4, 0 }
        };
        timerfd_settime(tfd, 0, &spec, NULL);

        /* Wait */
        fd_set rfds;
        int retval;

        /* Watch timefd file descriptor */
        FD_ZERO(&rfds);
        FD_SET(0, &rfds);
        FD_SET(tfd, &rfds);

        /* Let's wait for initial timer expiration */
        retval = select(tfd+1, &rfds, NULL, NULL, NULL); /* Last parameter = NULL --> wait forever */
        printf("Expired at %d! (%d) (%d)\n", (int)time(NULL), retval, read(tfd, dummybuf, 8) );
        
        /* Let's wait (twice) for periodic timer expiration */
        retval = select(tfd+1, &rfds, NULL, NULL, NULL);
        printf("Expired at %d! (%d) (%d)\n", (int)time(NULL), retval, read(tfd, dummybuf, 8) );

        retval = select(tfd+1, &rfds, NULL, NULL, NULL);
        printf("Expired at %d! (%d) (%d)\n", (int)time(NULL), retval, read(tfd, dummybuf, 8) );
    }
    
    return 0;
}

Run Code Online (Sandbox Code Playgroud)

这是输出。每行还包含时间戳,以便可以检查实际经过的时间>

Starting at (1596547762)...
Expired at 1596547766! (1) (8)
Expired at 1596547771! (1) (8)
Expired at 1596547776! (1) (8)
Run Code Online (Sandbox Code Playgroud)

请注意:

  • 我们刚刚进行了 3 次读取,用于测试
  • 间隔为 4s + 5s + 5s(初始超时 + 两次间隔超时)
  • 返回 8 个字节read()。我们忽略了它们,但它们包含了过期超时的数量