相关文章推荐

Nginx事件驱动是根据操作系统以及操作系统内核设计了很多不同的驱动方式,epoll事件驱动机制是Unix系统的事件驱动模式。本文重点介绍ngx_epoll_module是如何基于Linux内核实现epoll事件驱动模型,实理解Nginx在几十万并发连接下高效利用服务器资源的基本原理。
epoll在处理用户请求时不同于select或者poll的模式(采用轮询来处理的,轮询的fd数目越多,耗时越多)将用户请求作为一个整体处理,epoll在Linux系统中申请一个简易的文件系统,将用户请求分为三个阶段:第一个阶段调用epoll_create建立一个epoll对象(分配文件系统句柄资源),然后调用epoll_ctl向epoll对象中添加连接套接字,最后调用epoll_wait收集发生事件的连接。实现了在进程启动时一次创建,重复使用的目标。

1.epoll_create创建文件系统句柄

创建epoll句柄,实质就是从内核申请一个内存空间,用来存放socket事件(具体参考Linux句柄的使用)。当epoll句柄创建完成后,就会占用一个fd值,在使用完后需要调用close方法释放fd句柄,否则句柄一直处于被占用状态。具体使用参考ngx_epoll_init方法

int epoll_create(int size);
static ngx_int_t
ngx_epoll_init(ngx_cycle_t *cycle, ngx_msec_t timer)
    ngx_epoll_conf_t  *epcf;
    epcf = ngx_event_get_conf(cycle->conf_ctx, ngx_epoll_module);
    if (ep == -1) {
        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;
#if (NGX_HAVE_EVENTFD)
        if (ngx_epoll_notify_init(cycle->log) != NGX_OK) {
            ngx_epoll_module_ctx.actions.notify = NULL;
#endif
#if (NGX_HAVE_FILE_AIO)
        ngx_epoll_aio_init(cycle, epcf);
#endif
#if (NGX_HAVE_EPOLLRDHUP)
        ngx_epoll_test_rdhup(cycle);
#endif
    if (nevents < epcf->events) {
        if (event_list) {
            ngx_free(event_list);
        event_list = ngx_alloc(sizeof(struct epoll_event) * epcf->events,
                               cycle->log);
        if (event_list == NULL) {
            return NGX_ERROR;
    nevents = epcf->events;
    ngx_io = ngx_os_io;
    ngx_event_actions = ngx_epoll_module_ctx.actions;
#if (NGX_HAVE_CLEAR_EVENT)
    ngx_event_flags = NGX_USE_CLEAR_EVENT
#else
    ngx_event_flags = NGX_USE_LEVEL_EVENT
#endif
                      |NGX_USE_GREEDY_EVENT
                      |NGX_USE_EPOLL_EVENT;
    return NGX_OK;

2.epoll_ctl对监听事件管理
        主要是将被监听的事件添加到epoll句柄(epoll_create中创建的fd句柄,)、删除或者修改。

*epfd :epoll_create中创建的句柄EPOLL_CTL_ADD 注册、EPOLL_CTL_MOD 修 改、EPOLL_CTL_DEL 删除 *op:操作类型,例如:事件注册(EPOLL_CTL_ADD), 修改(EPOLL_CTL_MOD),删除(EPOLL_CTL_DEL)。 *fd:关联的文件描述符 *event:epoll_event指针(文件可读EPOLLIN,文件可写EPOLLOUT,紧急数据EPOLLPRI,文件错误EPOLLERR,文件挂断 EPOLLHUP) int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event); #if (NGX_HAVE_EVENTFD) static ngx_int_t ngx_epoll_notify_init(ngx_log_t *log) struct epoll_event ee; #if (NGX_HAVE_SYS_EVENTFD_H) notify_fd = eventfd(0, 0); #else notify_fd = syscall(SYS_eventfd, 0); #endif if (notify_fd == -1) { ngx_log_error(NGX_LOG_EMERG, log, ngx_errno, "eventfd() failed"); return NGX_ERROR; ngx_log_debug1(NGX_LOG_DEBUG_EVENT, log, 0, "notify eventfd: %d", notify_fd); notify_event.handler = ngx_epoll_notify_handler; notify_event.log = log; notify_event.active = 1; notify_conn.fd = notify_fd; notify_conn.read = &notify_event; notify_conn.log = log; ee.events = EPOLLIN|EPOLLET; ee.data.ptr = &notify_conn; if (epoll_ctl(ep, EPOLL_CTL_ADD, notify_fd, &ee) == -1) { ngx_log_error(NGX_LOG_EMERG, log, ngx_errno, "epoll_ctl(EPOLL_CTL_ADD, eventfd) failed"); if (close(notify_fd) == -1) { ngx_log_error(NGX_LOG_ALERT, log, ngx_errno, "eventfd close() failed"); return NGX_ERROR; return NGX_OK;

3.epoll_wait等待事件触发
        轮询epfd上的io事件(epoll_ctl注册的事件都是文件事件),如果事件发生则将socket句柄和事件类型放入event数组中,如果等待事件超过time_out,则超时处理。
        int epoll_wait(int epfd, struct epoll_event * events, intmaxevents, int timeout);
       事件轮询模式中最重要的功能就是事件的收集,分发。ngx_epoll_process_events实现了process_events接口,通过轮询epoll_wait返回的事件数组,将其分发给对于的handler处理,具体参考代码。

 static ngx_int_t
ngx_epoll_process_events(ngx_cycle_t *cycle, ngx_msec_t timer, ngx_uint_t flags)
    int                events;
    uint32_t           revents;
    ngx_int_t          instance, i;
    ngx_uint_t         level;
    ngx_err_t          err;
    ngx_event_t       *rev, *wev;
    ngx_queue_t       *queue;
    ngx_connection_t  *c;
    /* NGX_TIMER_INFINITE == INFTIM */
    ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
                   "epoll timer: %M", timer);
    events = epoll_wait(ep, event_list, (int) nevents, timer);
    err = (events == -1) ? ngx_errno : 0;
    if (flags & NGX_UPDATE_TIME || ngx_event_timer_alarm) {
        ngx_time_update();
    if (err) {
        if (err == NGX_EINTR) {
            if (ngx_event_timer_alarm) {
                ngx_event_timer_alarm = 0;
                return NGX_OK;
            level = NGX_LOG_INFO;
        } else {
            level = NGX_LOG_ALERT;
        ngx_log_error(level, cycle->log, err, "epoll_wait() failed");
        return NGX_ERROR;
    if (events == 0) {
        if (timer != NGX_TIMER_INFINITE) {
            return NGX_OK;
        ngx_log_error(NGX_LOG_ALERT, cycle->log, 0,
                      "epoll_wait() returned no events without timeout");
        return NGX_ERROR;
    for (i = 0; i < events; i++) {
        c = event_list[i].data.ptr;
        instance = (uintptr_t) c & 1;
        c = (ngx_connection_t *) ((uintptr_t) c & (uintptr_t) ~1);
        rev = c->read;
        if (c->fd == -1 || rev->instance != instance) {
             * the stale event from a file descriptor
             * that was just closed in this iteration
            ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
                           "epoll: stale event %p", c);
            continue;
        revents = event_list[i].events;
        ngx_log_debug3(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
                       "epoll: fd:%d ev:%04XD d:%p",
                       c->fd, revents, event_list[i].data.ptr);
        if (revents & (EPOLLERR|EPOLLHUP)) {
            ngx_log_debug2(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
                           "epoll_wait() error on fd:%d ev:%04XD",
                           c->fd, revents);
             * if the error events were returned, add EPOLLIN and EPOLLOUT
             * to handle the events at least in one active handler
            revents |= EPOLLIN|EPOLLOUT;
#if 0
        if (revents & ~(EPOLLIN|EPOLLOUT|EPOLLERR|EPOLLHUP)) {
            ngx_log_error(NGX_LOG_ALERT, cycle->log, 0,
                          "strange epoll_wait() events fd:%d ev:%04XD",
                          c->fd, revents);
#endif
        if ((revents & EPOLLIN) && rev->active) {
#if (NGX_HAVE_EPOLLRDHUP)
            if (revents & EPOLLRDHUP) {
                rev->pending_eof = 1;
#endif
            rev->ready = 1;
            rev->available = -1;
            if (flags & NGX_POST_EVENTS) {
                queue = rev->accept ? &ngx_posted_accept_events
                                    : &ngx_posted_events;
                ngx_post_event(rev, queue);
            } else {
                rev->handler(rev);
        wev = c->write;
        if ((revents & EPOLLOUT) && wev->active) {
            if (c->fd == -1 || wev->instance != instance) {
                 * the stale event from a file descriptor
                 * that was just closed in this iteration
                ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
                               "epoll: stale event %p", c);
                continue;
            wev->ready = 1;
#if (NGX_THREADS)
            wev->complete = 1;
#endif
            if (flags & NGX_POST_EVENTS) {
                ngx_post_event(wev, &ngx_posted_events);
            } else {
                wev->handler(wev);
    return NGX_OK;
                    Nginx事件驱动是根据操作系统以及操作系统内核设计了很多不同的驱动方式,epoll事件驱动机制是Unix系统的事件驱动模式。本文重点介绍ngx_epoll_module是如何基于Linux内核实现epoll事件驱动模型,实理解Nginx在几十万并发连接下高效利用服务器资源的基本原理。    epoll在处理用户请求时不同于select或者poll的模式(采用轮询来处理的,...
				
1. epoll 原理 假设有 100 万用户同时与一个进程保持着 TCP 连接,而每一时刻只有几十个或几百个 TCP 连接时活跃的(接收到 TCP 包),也就是说,在每一时刻,进程只需要处理这 100 万连接中的一小部分连接。 select 和 poll 的做法是:进程每次收集事件的连接(其实这 100 万连接中的大部分都是没有事件发生的)都把这 100 万连 接的套接字传给操作系统(这首先就是...
eventpoll的优点就不用说了,网上的资料很多,eventpoll的使用也很广泛,特别是在Web服务器中。因为最近要用到epoll,所以好好地看了一下它的实现,把学到的一些东西做下整理,做个记录。 一、sys_epoll_create()   其源码如下: SYSCALL_DEFINE1(epoll_create, int, size) if (size <= 0)
ngx_errlog_module模块nginx中第二个被执行的模块,它是nginx用来为其它模块提供日志记录功能的基础模块,http,mail,stream等模块都会调用该模块的接口实现日志记录功能。该模块提供了一个指令:error_log,用于指定全局范围的日志配置,指令的定义如下: static ngx_command_t ngx_errlog_commands[] = {
何谓惊群呢?master进程开始监听web端口,fork出的多个worker进程开始监听同一个监听套接字。当有一个新链接到达时,所有worker进程都会尝试的accept得到这个链接,但是只有一个进程可以得到,其他进程在accept失败后有会进入睡眠等待的状态。这种就是惊群。 要想解决惊群必须,同时间只有一个进程,去监听链接。可是如何做到一个进程去accept呢?下面看看nginx咋做的吧。 具体实现如下: ngx_int_t ngx_trylock_accept_mu...
    nginx做为一个异步高效的事件驱动型web服务器,在linux平台中当系统支持epollnginx默认采用epoll来高效的处理事件nginx中使用ngx_event_t结构来表示一个事件,先介绍下ngx_event_t结构体中成员的含义: struct ngx_event_s { void *data;
原来的操作是代码层操作,即 触发EPOLLIN,recv返回0,关闭套接字即可。 现在,使用EPOLLRDHUP状态,当客户端发送数据后立马关闭,(调用close(sockfd)),服务器无法接受数据,直接关闭。 代码如下: if(events[i].events &amp; EPOLLRDHUP || events[i].event...
指令的语法很简单,下面我们翻译一下官方文档: 配置日志。在同一个level中,可以指定多个日志。 第一个参数定义了存储日志信息的文件。不同level的错误日志会操作指定level的日志文件。可以通过指定的配置:以‘syslog:’为前缀,使用syslog机制记录日志。 什么是syslog? Unix/Linux系统中的大部分日志都是通过一种syslog的机制产生和维护的。Syslog是一种
1, 编译时如何选择正确的事件机制? 答:  编译的目标选择最先进的事件机制, 例如如果该机器支持epool就不会使用pool或select等. 实现的方法是写一个编译脚本, 依次尝试, 例如先尝试编译epool, 如果失败再尝试poll等. (待确认) 二: 代码结构
前面讲过ngx_event_core_module模块的init_process函数ngx_event_process_init会为每个监听套接字的读事件注册处理函数ngx_event_accept,本篇就学习一下在ngx_event_accept函数中到底进行了哪些工作。 /* event/ngx_event_accept.c */ /* 监听套接字的读事件处理函数 * param
有的时候初次安装nginx的时候会报这样的错误 sbin/nginx -c conf/nginx.conf 报错内容:sbin/nginx: error while loading shared libraries: libpcre.so.1: cannot open shared object file: No such file or directory 启动时如果报异常 error while loading shared libraries: libpcre.so.1:
nginx-1.19.3【集成nginx-http-flv-module】.zip是一个针对nginx服务器的扩展插件包,其中包含了一个名为nginx-http-flv-module的第三方模块。该模块的作用是支持HTTP-FLV协议的消息传输和流媒体播放。 HTTP-FLV协议是一种基于HTTP协议的视频传输协议,其可以将视频数据进行实时流式传输。这种协议在一些实时视频直播平台和视频点播平台上得到广泛应用,因为它不需要安装任何插件或专用播放器,而且可以在不同的设备和操作系统上进行播放。 集成nginx-http-flv-module插件包可以轻松地让nginx服务器支持HTTP-FLV协议,从而增强了它的多媒体处理功能。使用该插件后,nginx服务器可以直接将FLV格式的视频通过HTTP规范进行传输和播放,而且可以同时支持多个客户端的实时播放请求。 总之,nginx-1.19.3【集成nginx-http-flv-module】.zip是一个非常有用的nginx插件包,它为nginx服务器增加了HTTP-FLV协议的支持,使得nginx服务器在多媒体处理领域有更加广泛的应用。
 
推荐文章