Nginx源代码分析之accept细节(十九)

        现在单独说说accept中与一些具体I/O模型相关的细节。

        其实前面已经提过这个问题,这一系列I/O模型中最大差别是iocp,我们前面也说了,linux的几个模型,不管是select,epoll,kqueue,其实都是基于异步通知的,也就是说系统只关心哪个socket有接受和发送完成的消息,然后设置一定的标志,当某个work线程调用select,epoll_wait,kevent这样的函数发现这个标志后,再调用i/o函数来收发数据。

         而iocp不同,他是直接基于异步I/O的,而windows后来增加了多个扩展函数,包括acceptex,connectex,就是为了实现完整的异步功能。并且,异步I/O是按照先来先调用的原则,也就是在同一个socket先投递的I/O请求必然先触发。

         根据以上信息,在nginx中iocp是不会引起群惊问题的,因为即使多个线程向同1个监听端口投递了多个acceptext请求,每次客户端的connect只会触发其中一个acceptex。

         再看看nginx的源码,在epoll模型中有这样一段代码:

        if ((revents & EPOLLIN) && rev->active) {


#if (NGX_HAVE_EPOLLRDHUP)
            if (revents & EPOLLRDHUP) {
                rev->pending_eof = 1;
            }
#endif


            rev->ready = 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);
            }
        }

       前面章节已经提过,在防群惊模式下,会给flags增加NGX_POST_EVENTS标志,执行到这里会根据这个标志把可读请求放到2个缓存队列中,以便尽快释放accept锁。而在iocp的模型中,没有看到这方面的代码,因此我猜测,如果在配置中选择iocp的话,必须关闭群惊锁,,否则整个代码是有一定问题的。


       在来看看在ngx_event_process_init里面,iocp与其他模型在初始化过程中的差异,异步消息通知模型,只需要注册rev->handler = ngx_event_accept,然后调用gnx_add_event即可。而iocp则完全不同,我们看1.97的代码:



        if (ngx_event_flags & NGX_USE_IOCP_EVENT) {
            ngx_iocp_conf_t  *iocpcf;


            rev->handler = ngx_event_acceptex;


            if (ngx_use_accept_mutex) {
                continue;
            }


            if (ngx_add_event(rev, 0, NGX_IOCP_ACCEPT) == NGX_ERROR) {
                return NGX_ERROR;
            }


            ls[i].log.handler = ngx_acceptex_log_error;


            iocpcf = ngx_event_get_conf(cycle->conf_ctx, ngx_iocp_module);
            if (ngx_event_post_acceptex(&ls[i], iocpcf->post_acceptex)
                == NGX_ERROR)
            {
                return NGX_ERROR;
            }


        } else {
            rev->handler = ngx_event_accept;


            if (ngx_use_accept_mutex) {
                continue;
            }


            if (ngx_add_event(rev, NGX_READ_EVENT, 0) == NGX_ERROR) {
                return NGX_ERROR;
            }
        }

      其中对群惊锁的判断其实是没有意义的,抛开这个问题,看看其中的细节,首先仍然是注册ngx_event_acceptex,然后执行ngx_add_event(rev, 0, NGX_IOCP_ACCEPT),将listen socket加入完成端口的通知对象,伺候调用ngx_event_post_acceptex,此函数会根据文件中的配置生成多个client socket,然后调用ngx_acceptex,这就同时发起了多个accept异步调用,每个client socket会生成一个对应ngx_connect,以及相应的rev,wev 事件对象。rev->handler则注册为ngx_event_acceptex,ngx_connect还会记录listen socket,

       这其中有一点跟通常iocp模型有差别,一般来说会首先调用ngx_acceptex,然后在回调函数中再调用ngx_add_event(rev, 0, NGX_IOCP_IO)来把client socket加入完成端口的通知对象集合,但这里却在刚创建client socket之后就将其加入。

        再看ngx_event_acceptex,到这里系统已经生成真正的client socket,因此首先调用setsockopt并设置SO_UPDATE_ACCEPT_CONTEXT以复制listen socket的特性,再调用ngx_getacceptexsockaddrs获取client socket的本地和对端地址然后保存在ngx_connection里面,接着再次调用ngx_event_post_acceptex,但这次数量限定为1,保持整个进程投递的异步accept总量不变,最后再调用listen socket对象的handler,即ngx_http_init_connection。

     再看看ngx_http_init_connection里面的一些细节,到这里首先会注册rev的回调函数ngx_http_wait_request_handler,然后在352行,则有一段专门针对iocp的代码,首先判断rev->ready是否为1,如果是的话则会直接调用rev->handler即ngx_http_wait_request_handler。在ngx_http_wait_request_handler里面,可能已经有附带数据跟accept一起到达,因此需要进行简单的判断:

    b = c->buffer;


    if (b == NULL) {
        b = ngx_create_temp_buf(c->pool, size);
        if (b == NULL) {
            ngx_http_close_connection(c);
            return;
        }


        c->buffer = b;


    } else if (b->start == NULL) {


        b->start = ngx_palloc(c->pool, size);
        if (b->start == NULL) {
            ngx_http_close_connection(c);
            return;
        }


        b->pos = b->start;
        b->last = b->start;
        b->end = b->last + size;
    }

随后调用n = c->recv(c, b->last, size);也就是iocp版本的ngx_overlapped_wsarecv

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值