leevis.com icon indicating copy to clipboard operation
leevis.com copied to clipboard

nginx处理epoll_wait EPOLLRDHUP错误

Open vislee opened this issue 8 years ago • 0 comments

EPOLLRDHUP是什么意思

epoll_wait 返回的epoll_eventeventsEPOLLRDHUP被置位是什么意思呢,表示对端调用close()关闭socket连接或调用shutdown(SHUT_WR)关闭对端的写。

nginx是如何判断内核支持该错误的

这个参数应该是个新东西,也就是说不是所有的内核都支持。nginx又是怎么判断内核是否支持的呢?

在auto/os/linux 有这样一段代码,调用auto/feature 把ngx_feature_test的内容调用gcc编译一下,如果编译(且运行)成功则该系统支持该参数。


    ngx_feature="EPOLLRDHUP"
    ngx_feature_name="NGX_HAVE_EPOLLRDHUP"
    ngx_feature_run=no
    ngx_feature_incs="#include <sys/epoll.h>"
    ngx_feature_path=
    ngx_feature_libs=
    ngx_feature_test="int efd = 0, fd = 0;
                      struct epoll_event ee;
                      ee.events = EPOLLIN|EPOLLRDHUP|EPOLLET;
                      ee.data.ptr = NULL;
                      epoll_ctl(efd, EPOLL_CTL_ADD, fd, &ee)"
    . auto/feature

nginx是如何处理该参数

在调用模块的init_process方法,调用到事件模块的ngx_event_process_init方法时,该方法又调用事件模块的module->actions.init,epoll是调用ngx_epoll_init方法,该方法中调用epoll_create创建完epoll文件句柄后,如果定义了宏NGX_HAVE_EPOLLRDHUP,也就是通过feature脚本判断支持EPOLLRDHUP错误时,会调用ngx_epoll_test_rdhup判断是否支持,该函数会建立一个socket的无名管道,epoll检查一端,关闭另一断端,查看检查的端是否会返回置位EPOLLRDHUP的读事件。再多啰嗦点吧,别看这不到100行代码,注意的东西还挺多,因为是单进程单线程模拟一个socket双方通信,所以只能是先调用closeepoll_wait,都close完了再wait还能wait到事件吗?能,epoll 事件是可靠的。


ngx_epoll_test_rdhup(ngx_cycle_t *cycle)
{
    int                 s[2], events;
    struct epoll_event  ee;

    if (socketpair(AF_UNIX, SOCK_STREAM, 0, s) == -1) {
        ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
                      "socketpair() failed");
        return;
    }

    ee.events = EPOLLET|EPOLLIN|EPOLLRDHUP;

    if (epoll_ctl(ep, EPOLL_CTL_ADD, s[0], &ee) == -1) {
        ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
                      "epoll_ctl() failed");
        goto failed;
    }

    if (close(s[1]) == -1) {
        ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
                      "close() failed");
        s[1] = -1;
        goto failed;
    }

    s[1] = -1;

    events = epoll_wait(ep, &ee, 1, 5000);

    if (events == -1) {
        ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
                      "epoll_wait() failed");
        goto failed;
    }

    if (events) {
        ngx_use_epoll_rdhup = ee.events & EPOLLRDHUP;

    } else {
        ngx_log_error(NGX_LOG_ALERT, cycle->log, NGX_ETIMEDOUT,
                      "epoll_wait() timed out");
    }

    ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
                   "testing the EPOLLRDHUP flag: %s",
                   ngx_use_epoll_rdhup ? "success" : "fail");

failed:

    if (s[1] != -1 && close(s[1]) == -1) {
        ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
                      "close() failed");
    }

    if (close(s[0]) == -1) {
        ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
                      "close() failed");
    }
}

vislee avatar Jan 12 '17 08:01 vislee