【问题标题】:Always print EAGAIN when calling accept after epoll_wait在 epoll_wait 之后调用 accept 时总是打印 EAGAIN
【发布时间】:2013-05-19 09:54:55
【问题描述】:

我使用epoll来监听listen fd事件,在EPOLLIN事件发生后我调用accept来处理, 但总是 EAGAIN 错误。任何人都可以给我一些建议吗?谢谢!

[log]一直打印下面的信息

INFO Jan 01 00:02:08:924 [385] poll_loop: epoll 有 1 个事件

INFO Jan 01 00:02:08:925 [385] poll_loop:事件 fd 0,事件类型:1

INFO Jan 01 00:02:08:925 [385] handle_connect: fd 0,

ERROR Jan 01 00:02:08:925 [385] handle_connect:accept 返回错误 11 资源暂时不可用...重试,监听 fd:0。

[代码]

    ................

        listenfd = listen_sock (port, &addrlen);
        socket_nonblocking(listenfd);
        g_epollFd = epoll_create(MAX_EVENTS);  
        register_read(listenfd);

    .................

        while (!config.quit)
        {

            int fds = epoll_wait(g_epollFd, g_Events, MAX_EVENTS, -1);  

            if (fds < 0)
            {
                log_message(LOG_CRIT, "epoll wait error %d %s, continue", errno, strerror(errno));
                continue;
            }

            log_message(LOG_INFO, "epoll has %d event", fds);
            for (i = 0; i < fds; ++i)
             {

                log_message(LOG_INFO, "event fd %d, event type:%d", g_Events[i].data.fd, g_Events[i].events);
                 if ((g_Events[i].events & EPOLLERR) 
                    || (g_Events[i].events & EPOLLHUP)
                    /*|| (g_Events[i].events & POLLNVAL) compile err*/)
                {
                    log_message(LOG_INFO, "A disconnect occurs, [fd:%d]", g_Events[i].data.fd);
                    handle_disconnect(g_Events[i].data.fd);      
                    continue;
                }
                if (g_Events[i].events & EPOLLIN)
                {
                     if (listenfd == g_Events[i].data.fd)
                    {
                        handle_connect(listenfd, ptr);
                    }
                    else if (g_dnsfd == g_Events[i].data.fd)
                    {
                        dns_poll(g_dns);
                    }
                    else
                    {
                        handle_input(g_Events[i].data.fd);
                    }
                }
    ...................

    void handle_connect(int fd, struct child_s *ptr)
    {
        int connfd;
        struct conn_s *connptr = 0;
        char peer_ipaddr[IP_LENGTH]; 
        socklen_t clilen = sizeof(struct sockaddr_in);
        struct sockaddr_in cliaddr ;

        log_message(LOG_INFO, "fd %d, ");
        connfd = accept (fd, (struct sockaddr*)(&cliaddr), &clilen);

        if (connfd < 0) 
        {
            log_message (LOG_ERR, "accept returned an error %d %s ... retrying, listen fd:%d", errno, strerror (errno), fd);
            return;
        }

    ......................

#define REGISTER_EPOLL_EVENT(_fd, evt, op) \
do\
{\
    struct epoll_event epv = {0, {0}};  \
    epv.data.fd = _fd;  \
    epv.events  = evt;  /*EPOLLLT default*/\
\
    if(epoll_ctl(g_epollFd, op, _fd, &epv) < 0)  \
    {\
        /*if ((errno != EEXIST) && (errno != ENOENT))*/\
        {\
            log_message(LOG_ERR, "epool ctl set failed, fd %d, errno %d: %s", _fd, errno, strerror(errno));  \
            return ;\
        }\
    }\
    log_message(LOG_INFO, "epool ctl set sucess, fd %d", _fd);\
}while(0)


void register_read(int fd, int* evt)
{
    if (0 == evt)
    {
        REGISTER_EPOLL_EVENT(fd, EPOLLIN, EPOLL_CTL_ADD);
    }
    else if (0 == *evt)
    {
        *evt |= EPOLLIN;

        REGISTER_EPOLL_EVENT(fd, (*evt), EPOLL_CTL_ADD);
    }
    else if (!(*evt & EPOLLIN))
    {
        *evt |= EPOLLIN;
        REGISTER_EPOLL_EVENT(fd, (*evt), EPOLL_CTL_MOD);
    }
    else
    {
        log_message("event %d for fd %d is already there", *evt, fd);
    }

}

【问题讨论】:

  • 您实际上是如何处理断开连接的?我没有看到任何方法可以调整您的投票内容。
  • 你调用 epoll_wait 两次有什么原因吗?
  • 您调用的大量代码没有向我们展示,这可能会导致难以提供帮助。 register_read handle_disconnect 也许其他人会帮助能够看到
  • 两次调用 epoll_wait 看起来很傻,但不会导致错误。
  • 您实际上是在调用 epoll_wait() 两次 吗?顺便说一句:它是边缘触发的吗?

标签: c epoll


【解决方案1】:

这一行说明了一切:

INFO Jan 01 00:02:08:925 [385] poll_loop: event fd 0, event type:1

fd 0 是标准输入,而不是您的监听套接字。显然,您将“0”添加到 epoll 集中而不是您的侦听套接字。

您没有显示调用 epoll_ctl 以将 listenFd 添加到 epoll 集 (g_epollFd) 的位置。据推测,这就是上面的 register_read() 调用的全部内容。我希望它看起来像这样:

int result;
epoll_event readEvent = {};

readEvent.data.fd = listenFd;
readEvent.events = EPOLLIN;
result = epoll_ctl(g_epollFd, EPOLL_CTL_ADD, listenFd, &readEvent);

请注意,listenFd 被指定为 epoll_ctl 的第三个参数以及第四个参数 (readEvent.data.fd) 上的成员 var。

【讨论】:

  • 内核版本是linux 2.6.21,好像和dev.openwrt.org/ticket/1815一样的问题,但我不确定。
  • 在 Linux ubuntu 3.2.0-40-generic-pae 上运行良好,但在 Linux ralink 2.6.21 #4057 上出现问题。
  • 我确定现在与 dev.openwrt.org/ticket/1815 有同样的问题,但我无法直接升级内核,还有其他建议吗?
  • 切换到调用 poll() 而不是 epoll 来进行轮询机制。 linux.die.net/man/2/poll
  • 如果您最终确认了错误,请自行发布答案并将其标记为已接受,以便其他人受益。
【解决方案2】:

EAGAIN 并不是真正的错误。

请看: ERROR on accept: Resource temporarily unavailable

另外,如果您有兴趣进一步阅读,我强烈推荐这本书,以很好地概述使用 epoll 进行网络编程。

http://man7.org/tlpi/

【讨论】:

  • 是我的错,实际只调用了一次epoll_wait,而且我使用的是EPOLLLT模式,handle_disconnect()没有调用
  • 我知道 EAGAIN 并不是一个真正的错误,它只是告诉我们什么都不应该处理,但是 epoll_wait 告诉在 listenfd 上发生了一些事情。这是一个死循环。我在 Ralink mips board 上遇到了这个问题,但是在我的 vmware ubuntu 上,同样的代码是可以的,运行得很好。
  • epoll_ctl在调用register_read(listernfd)时以EPOLLLT模式调用
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-06-21
  • 2012-11-14
  • 1970-01-01
  • 1970-01-01
  • 2021-12-20
  • 2017-02-26
相关资源
最近更新 更多