【问题标题】:timerfd and readtimerfd 和读取
【发布时间】:2012-09-11 13:55:16
【问题描述】:

我有应用程序,它会定期(通过计时器)检查一些数据存储。
像这样:

#include <iostream>
#include <cerrno>
#include <cstring>
#include <cstdlib>
#include <sys/fcntl.h>
#include <unistd.h>

// EPOLL & TIMER
#include <sys/epoll.h>
#include <sys/timerfd.h>

int main(int argc, char **argv)
{
    /* epoll instance */
    int efd = epoll_create1(EPOLL_CLOEXEC);

    if (efd < 0)
    {
        std::cerr << "epoll_create error: " << strerror(errno) << std::endl;
        return EXIT_FAILURE;
    }

    struct epoll_event ev;
    struct epoll_event events[128];

    /* timer instance */
    int tfd = timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC);

    struct timespec ts;
    // first expiration in 3. seconds after program start
    ts.tv_sec = 3;
    ts.tv_nsec = 0;

    struct itimerspec new_timeout;
    struct itimerspec old_timeout;

    bzero(&new_timeout, sizeof(new_timeout));
    bzero(&old_timeout, sizeof(old_timeout));

    // value
    new_timeout.it_value = ts;

    // no interval;
    // timer will be armed in epoll_wait event trigger
    new_timeout.it_interval.tv_sec =
    new_timeout.it_interval.tv_nsec = 0;

    // Add the timer descriptor to epoll.
    if (tfd != -1)
    {
        ev.events = EPOLLIN | EPOLLERR /*| EPOLLET*/;
        ev.data.ptr = &tfd;
        epoll_ctl(efd, EPOLL_CTL_ADD, tfd, &ev);
    }

    int flags = 0;
    if (timerfd_settime(tfd, flags, &new_timeout, &old_timeout) < 0)
    {
       std::cerr << "timerfd_settime error: " << strerror(errno) << std::endl;
    }

    int numEvents = 0;
    int timeout = 0;
    bool checkTimer = false;
    while (1)
    {
        checkTimer = false;
        numEvents = epoll_wait(efd, events, 128, timeout);
        if (numEvents > 0)
        {
            for (int i = 0; i < numEvents; ++i)
            {
                if (events[i].data.ptr == &tfd)
                {
                    std::cout << "timeout" << std::endl;
                    checkTimer = true;
                }
            }
        }
        else if(numEvents == 0)
        {
            continue;
        }
        else
        {
            std::cerr << "An error occured: " << strerror(errno) << std::endl;
        }

        if (checkTimer)
        {
            /* Check data storage */
            uint64_t value;
            ssize_t readBytes;
            //while ( (readBytes = read(tfd, &value, 8)) > 0)
            //{
            //    std::cout << "\tread: '" << value << "'" << std::endl;
            //}
            itimerspec new_timeout;
            itimerspec old_timeout;
            new_timeout.it_value.tv_sec = rand() % 3 + 1;
            new_timeout.it_value.tv_nsec = 0;
            new_timeout.it_interval.tv_sec =
            new_timeout.it_interval.tv_nsec = 0;
            timerfd_settime(tfd, flags, &new_timeout, &old_timeout);
        }
    }

    return EXIT_SUCCESS;
}

这是对我的应用的简单描述。 每次超时后,定时器需要在每次超时后重新设置不同的值。

问题是:

  1. 是否需要将 timerfd 添加到带有 EPOLLET 标志的 epoll (epoll_ctl) 中?
  2. 是否需要在每次超时后读取 timerfd?
  3. 是否需要无限等待epoll_wait(超时=-1)?

【问题讨论】:

    标签: c++ timer timeout epoll


    【解决方案1】:

    问题的答案:

    1. 是否需要将 timerfd 添加到带有 EPOLLET 标志的 epoll (epoll_ctl) 中?

    没有。添加EPOLLET(边缘触发)确实会改变接收事件的行为。如果没有EPOLLET,您将不断收到来自epoll_wait 的与timerfd 相关的事件,直到您收到来自timerfdread()。使用 EPOLLET,您将不会收到除第一个事件之外的其他事件,即使发生新的到期,直到您从 timerfd 收到 read() 并发生新的到期。 p>

    1. 是否需要在每次超时后读取 timerfd?

    是的,以便在发生新的到期时(见上文)继续(仅)接收事件。不使用定期计时器(仅单次到期)时不可以,并且您关闭timerfd 而不读取。

    1. 是否需要无限等待epoll_wait(超时=-1)?

    没有。您可以使用epoll_wait 的超时时间代替timerfd。我个人认为使用timerfd 比继续计算EPOLL 的下一个超时更容易,特别是如果您期望多个超时间隔;当超时发生时,密切关注您的下一个任务是什么,因为它与唤醒的特定事件相关联。

    【讨论】:

      【解决方案2】:

      您可以在两种模式之一(边沿触发或电平触发)中执行此操作。如果您选择边缘触发路由,那么您必须通过EPOLLET,并且不需要在每次唤醒后读取timerfd。您收到来自epoll 的事件这一事实意味着已经触发了一个或多个超时。您也可以选择阅读timerfd,它会返回自您上次阅读后触发的超时次数

      如果您选择水平触发路线,则无需通过EPOLLET,但您必须在每次唤醒后阅读timerfd。如果您不这样做,那么您将立即再次被唤醒,直到您消耗超时。

      您应该将-1 传递给epoll 作为超时或某个正值。如果您通过0,就像您在示例中所做的那样,那么您将永远不会进入睡眠状态,您只会等待超时触发。这几乎可以肯定是不受欢迎的行为。

      【讨论】:

      • 似乎read() 在这两种情况下都是必须的。没有read(),在EPOLLET的情况下,你不会得到下一个事件(直到read()发生),没有EPOLLET,你将不断收到事件(直到read()发生)。跨度>
      猜你喜欢
      • 2018-10-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-02-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多