【问题标题】:epoll with edge triggered event带有边缘触发事件的 epoll
【发布时间】:2012-10-15 09:00:39
【问题描述】:

epoll 的手册页有一个边缘触发的示例代码,如下所示:

for (;;) {
    nfds = epoll_wait(epollfd, events, MAX_EVENTS, -1);
    if (nfds == -1) {
        perror("epoll_pwait");
        exit(EXIT_FAILURE);
    }

    for (n = 0; n < nfds; ++n) {
        if (events[n].data.fd == listen_sock) {
            conn_sock = accept(listen_sock,
                        (struct sockaddr *) &local, &addrlen);
            if (conn_sock == -1) {
                perror("accept");
                exit(EXIT_FAILURE);
            }
            setnonblocking(conn_sock);
            ev.events = EPOLLIN | EPOLLET;
            ev.data.fd = conn_sock;
            if (epoll_ctl(epollfd, EPOLL_CTL_ADD, conn_sock,
                    &ev) == -1) {
                perror("epoll_ctl: conn_sock");
                exit(EXIT_FAILURE);
            }
        } else {
            do_use_fd(events[n].data.fd);
        }
    }
}

在函数 do_use_fd 中,我在 while 循环中调用 nonblocked recv 直到 EAGAIN,示例代码工作正常。

我有一个关于这个示例代码的问题,假设现在我有 50 个 socket 客户端连接,突然有 10 个客户端同时写入数据,所以 epoll_wait() 将返回 10 然后进入 for 循环:

for (n = 0; n < nfds; ++n)

它将为这 10 个客户端调用 do_use_fd(events[n].data.fd);,假设 n=5 已完成,而 n=6 尚未完成,突然事件 n=3 的文件描述在所有这 10 个事件之后收到了新数据完成并返回 epoll_wait ,我会收到事件通知我有一个客户端有新数据要读取吗?否则我会错过它,因为当事件发生时,代码不在 epoll_wait 中!

【问题讨论】:

标签: c linux sockets epoll


【解决方案1】:

只要您阅读直到收到EAGAIN 错误,您就会在下次调用epoll_wait 时收到该事件。

只有在空和非空(或者EPOLLOUT 的满和非满)之间发生变化时才会触发该事件,但该状态会一直保持到通过epoll_wait 传递事件为止。

有一点相关的说明:如果您注册EPOLLINEPOLLOUT 事件并假设您从未填满发送缓冲区,您仍然会在每次由epoll_wait 返回的事件中设置EPOLLOUT 标志EPOLLIN 被触发 - 请参阅 https://lkml.org/lkml/2011/11/17/234 以获得更详细的说明。

最后,边缘触发模式的确切行为实际上取决于所使用的套接字类型,并且在任何地方都没有真正记录。我前段时间做了一些测试,并在此处记录了我的发现:http://cmeerw.org/blog/753.html#753 - 简而言之,对于数据报套接字,您可能会收到比预期更多的事件。

【讨论】:

  • ...对于eventfds 和timerfds,你得到的比你预期的要少。
  • 谢谢cmeerw,帮了大忙!!
【解决方案2】:

在epoll下使用Edge Triggered的时候,读一些东西,可以这样

int n = -1;
while (1)
{
    n = recv(fd, iobuf, init_buff_size, MSG_DONTWAIT);
    if (n > 0)
    {
        LOG(glogfd, LOG_TRACE, "fd[%d] recv len %d\n", fd, n);
        mybuff_setdata(&(curcon->recv_buff), iobuf, n); // this is my func
        if (n == init_buff_size)
        {
            LOG(glogfd, LOG_DEBUG, "fd[%d] need recv nextloop %d\n", fd, n);
            continue;
        }
        break;
    }
    if (n == 0)
    {
        LOG(glogfd, LOG_ERROR, "fd[%d] close %s:%d!\n", fd, ID, LN);
        return do_close(fd);
    }
    if (errno == EINTR)
    {
        LOG(glogfd, LOG_TRACE, "fd[%d] need recv again!\n", fd);
        continue;
    }
    else if (errno == EAGAIN)
    {
        LOG(glogfd, LOG_TRACE, "fd[%d] need recv next!\n", fd);
        modify_fd_event(fd, EPOLLIN);   // this is the KEY, add read again
        break;
    }
    else
    {
        LOG(glogfd, LOG_ERROR, "fd[%d] close %s:%d!\n", fd, ID, LN);
        return do_close(fd);
    }
}

【讨论】:

  • >>> modify_fd_event(fd, EPOLLIN);您实际上不需要重新注册吗?
猜你喜欢
  • 2013-01-25
  • 2016-07-08
  • 2014-10-14
  • 2012-02-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-02-02
  • 2010-12-30
相关资源
最近更新 更多