Nginx源码阅读 EPOLL多路复用机制的实现

概述

Nginx源码中,实现IO多路复用的机制的的文件主要在ngx_epoll_module.c中,本文也主要分析epoll模块的实现。主要从创建、初始化和管理epoll实例入手分析。

实现逻辑分析

  • 初始化:ngx_epoll_init 负责创建epoll实例,同时对实例进行初始化
  • 事件注册:ngx_epoll_add_event,主要负责将要关心的事件以及文件描述符添加到epoll实例中
  • 事件等待与处理:事件循环过程中,调用ngx_epoll_process_event 等待事件发生,同事并处理这些事件
  • 事件删除:ngx_epoll_del_event 负责从epoll实例中删除不需要监控的事件内容
  • 清理:通过ngx_epoll_done关闭epoll实例,同时释放对应内存资源

 ngx_epoll_init

初始化epoll实例,创建epoll的文件描述符

  • 使用epoll_create创建爱一个epoll实例,设置其大小,同时检测其是否创建成功
static ngx_int_t ngx_epoll_init(ngx_cycle_t *cycle, ngx_msec_t timer) {
    ep = epoll_create(cycle->connection_n / 2);
    if (ep == -1) {
        ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno, "epoll_create() failed");
        return NGX_ERROR;
    }
    
    // 其他初始化代码

    return NGX_OK;
}

 ngx_epoll_done

关闭epoll实例,同时释放对应资源

static void ngx_epoll_done(ngx_cycle_t *cycle) {
    if (close(ep) == -1) {
        ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, "epoll close() failed");
    }

    // 其他清理代码
}

ngx_epoll_add_event

将事件添加到epoll事例中,然后检查是否添加成功

static ngx_int_t ngx_epoll_add_event(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags) {
    struct epoll_event ee;
    ee.events = event;
    ee.data.ptr = ev;

    if (epoll_ctl(ep, EPOLL_CTL_ADD, ev->fd, &ee) == -1) {
        ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, "epoll_ctl(EPOLL_CTL_ADD) failed");
        return NGX_ERROR;
    }

    return NGX_OK;
}

 ngx_epoll_del_event

从epoll实例中删除关心的事件

static ngx_int_t ngx_epoll_del_event(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags) {
    struct epoll_event ee;
    ee.events = 0;
    ee.data.ptr = NULL;

    if (epoll_ctl(ep, EPOLL_CTL_DEL, ev->fd, &ee) == -1) {
        ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, "epoll_ctl(EPOLL_CTL_DEL) failed");
        return NGX_ERROR;
    }

    return NGX_OK;
}

nagx_epoll_process_events

等待并处理epoll事件,将关心事件中已经发生的事件存储到event_list中,同时遍历每个事件

static ngx_int_t ngx_epoll_process_events(ngx_cycle_t *cycle, ngx_msec_t timer, ngx_uint_t flags) {
    int events;
    struct epoll_event event_list[NGX_MAX_EVENTS];

    events = epoll_wait(ep, event_list, NGX_MAX_EVENTS, timer);

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

    for (int i = 0; i < events; i++) {
        ngx_event_t *ev = event_list[i].data.ptr;
        // 处理事件
    }

    return NGX_OK;
}

处理事件流程分析

  • 首先从event_list[i].data.ptr中获取发生事件的ngx_event_t结构
  • 然后从nagx_event_t 结构中获取其关联的ngx_connection_t 结构
  • 最后根据事件的不同类型,调用ngx_event_t 中对应的处理函数即可
static ngx_int_t ngx_epoll_process_events(ngx_cycle_t *cycle, ngx_msec_t timer, ngx_uint_t flags) {
    int events;
    struct epoll_event event_list[NGX_MAX_EVENTS];

    // 调用 epoll_wait 等待事件发生
    events = epoll_wait(ep, event_list, NGX_MAX_EVENTS, timer);

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

    for (int i = 0; i < events; i++) {
        ngx_event_t *ev;
        ngx_connection_t *c;

        // 获取发生事件的 ngx_event_t 结构
        ev = event_list[i].data.ptr;
        c = ev->data;

        if (event_list[i].events & (EPOLLERR|EPOLLHUP)) {
            ngx_log_error(NGX_LOG_ALERT, cycle->log, 0, "epoll_wait() error on fd:%d", c->fd);
        }

        if (event_list[i].events & EPOLLIN) {
            ev->ready = 1;
            // 调用事件处理函数
            ev->handler(ev);
        }

        if (event_list[i].events & EPOLLOUT) {
            ev->write = 1;
            ev->handler(ev);
        }
    }

    return NGX_OK;
}

 Nginx处理读写事件流程分析(以HTTP请求为例)

处理写事件函数,通常是HTTP模块中的ngx_http_request_handler

void ngx_http_request_handler(ngx_event_t *ev) {
    ngx_connection_t *c;
    ngx_http_request_t *r;
    ssize_t n;
    ngx_buf_t *b;
    
    // 从事件中获取连接
    c = ev->data;
    r = c->data;

    // 检查是否有数据需要发送
    b = r->out;
    if (b != NULL) {
        // 发送响应数据
        n = c->send(c, b->pos, b->last - b->pos);

        // 根据发送结果处理
        if (n > 0) {
            b->pos += n;
            if (b->pos == b->last) {
                // 数据发送完毕,清理缓冲区
                ngx_http_finalize_request(r, NGX_HTTP_OK);
            }
        } else if (n == 0 || n == NGX_ERROR) {
            ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
        }
    }
}

处理读事件,ngx_http_wait_request_handler

void ngx_http_wait_request_handler(ngx_event_t *ev) {
    ngx_connection_t *c;
    ngx_http_request_t *r;
    ngx_http_connection_t *hc;
    ssize_t n;
    ngx_buf_t *b;
    ngx_http_core_srv_conf_t *cscf;
    
    // 从事件中获取连接
    c = ev->data;
    r = c->data;

    // 读取请求数据
    n = c->recv(c, b->last, b->end - b->last);

    // 根据读取结果处理
    if (n > 0) {
        b->last += n;
        // 处理请求数据,例如解析HTTP请求头
        ngx_http_process_request_line(r);
    } else if (n == 0 || n == NGX_ERROR) {
        ngx_http_close_request(r, NGX_HTTP_CLIENT_CLOSED_REQUEST);
    }
}

 Nginx下其他多路复用机制了解

  •  ngx_epoll_module.c :epoll事件的处理模块,Linux使用,处理大量并发连接
  • ngx_select_module.c :select事件处理模块,性能不然epoll
  • ngx_poll_module.c:poll处理模块,介于select和epoll之间
  • ngx_kqueue_module.c:基于kqueue的事件处理模块,主要是macOS系统使用
  • ngx_eventport_module.c:Solaris特有的多路复用机制,提供了和epoll和kqueue类似的功能
  • ngx_devpoll_module.c:Solaris特有的多路复用机制,改进poll
  • 13
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值