Nginx源代码分析之I/O模型细节(七)

原创 2015年07月10日 20:16:36

这里谈谈源码的具体实现


作为统一框架,每个模型都有统一的处理接口包括,这里定义在

typedef struct {
    ngx_int_t  (*add)(ngx_event_t *ev, int event, u_int flags);
    ngx_int_t  (*del)(ngx_event_t *ev, int event, u_int flags);


    ngx_int_t  (*enable)(ngx_event_t *ev, int event, u_int flags);
    ngx_int_t  (*disable)(ngx_event_t *ev, int event, u_int flags);


    ngx_int_t  (*add_conn)(ngx_connection_t *c);
    ngx_int_t  (*del_conn)(ngx_connection_t *c, u_int flags);


    ngx_int_t  (*process_changes)(ngx_cycle_t *cycle, ngx_uint_t nowait);
    ngx_int_t  (*process_events)(ngx_cycle_t *cycle, ngx_msec_t timer,
                   ngx_uint_t flags);


    ngx_int_t  (*init)(ngx_cycle_t *cycle, ngx_msec_t timer);
    void       (*done)(ngx_cycle_t *cycle);
} ngx_event_actions_t;


可以看到,主要有add,del,process_events,init,done,等主要方法。有点类似OOB的多态,定义了统一的抽象接口。


于是每个模型都有自己的具体实现:


比如iocp模型下,分别实现了ngx_iocp_add_event,ngx_iocp_process_events,ngx_iocp_init

epoll模型下,则实现了ngx_epoll_add_event,ngx_epoll_del_event,ngx_epoll_process_events,ngx_epoll_init

select则与epoll类似。



我们知道,ngx_single_process_cycle是I/O处理的线程,其中,process_events 方法是在这个线程中被调用。

    for ( ;; ) {
        ngx_log_debug0(NGX_LOG_DEBUG_EVENT, cycle->log, 0, "worker cycle");


        ngx_process_events_and_timers(cycle);


        if (ngx_terminate || ngx_quit) {


            for (i = 0; ngx_modules[i]; i++) {
                if (ngx_modules[i]->exit_process) {
                    ngx_modules[i]->exit_process(cycle);
                }
            }


            ngx_master_process_exit(cycle);
        }


        if (ngx_reconfigure) {
            ngx_reconfigure = 0;
            ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, "reconfiguring");


            cycle = ngx_init_cycle(cycle);
            if (cycle == NULL) {
                cycle = (ngx_cycle_t *) ngx_cycle;
                continue;
            }


            ngx_cycle = cycle;
        }


        if (ngx_reopen) {
            ngx_reopen = 0;
            ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, "reopening logs");
            ngx_reopen_files(cycle, (ngx_uid_t) -1);
        }


实际上是在ngx_process_events_and_timers函数中调用处理方法,ngx_process_events_and_timers函数涉及到多个重要问题。比如accept的互斥管理的问题,超时的管理问题,不同I/O模型的ngx_process_events的处理,ngx_posted_accept_events和ngx_posted_events队列的处理。

这里只谈谈ngx_process_events。 ngx_process_events主要用于异步模型中当时未完成,但延迟完成的情况。


对于每个模型来说,无非是通过各模型的API,读取I/O的状态,来判断是否有I/O数据到达并就绪。每个模型的代码细节就不谈了,谈谈代码上的一些关键点以及一切差别。


关于timer,每个模型各有特点,但都会设置这个参数。比如GetQueuedCompletionStatus的最后一个参数用来设置timer。


就绪I/O的处理方法各有不同,对于select,会检查work_write_fd_set和work_read_fd_set的每个一句柄,然后将可读可写的socket对应的ev放入accept队列或者普通队列中。

代码如下:

    for (i = 0; i < nevents; i++) {
        ev = event_index[i];
        c = ev->data;
        found = 0;


        if (ev->write) {
            if (FD_ISSET(c->fd, &work_write_fd_set)) {
                found = 1;
                ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
                               "select write %d", c->fd);
            }


        } else {
            if (FD_ISSET(c->fd, &work_read_fd_set)) {
                found = 1;
                ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
                               "select read %d", c->fd);
            }
        }


        if (found) {
            ev->ready = 1;


            queue = ev->accept ? &ngx_posted_accept_events
                               : &ngx_posted_events;


            ngx_post_event(ev, queue);


            nready++;
        }
    }


然后接着调用ngx_event_process_posted(cycle, &ngx_posted_accept_events);以及ngx_event_process_posted(cycle, &ngx_posted_events);来处理已完成队列。

而iocp则不会使用这2个数据结构,在ngx_process_events_and_timers中,也就是ngx_iocp_process_events函数,iocp会直接调用ev->handler(ev);完成回调处理。

对于epoll模型,如果存在NGX_POST_EVENTS标志,会跟select一样把就绪I/O放入2个对之列中,如果不存在,则直接调用rev->handler(rev);

为什么有这样的差别,以后在详细论述。


解释nginx的epoll的网络I/O模型为什么快的原因

epoll是多路复用IO(I/O Multiplexing)中的一种方式,但是仅用于linux2.6以上内核,在开始讨论这个问题之前,先来解释一下为什么需要多路复用IO. 以一个生活中的例子来解释....
  • xb_0916
  • xb_0916
  • 2013年08月06日 17:14
  • 1141

Nginx源代码分析之I/O细节(十一)

至于每个平台和模型里面具体I/O的细节,我们简单分析一下,先看看发送的具体实现,我们先以iocp模型来进行具体分析。 在Upstream部分,最后提到真正的发送函数是一个send_chain指针,对...
  • namelcx
  • namelcx
  • 2015年07月31日 22:12
  • 567

Nginx源代码分析之I/O模型(六)

....
  • namelcx
  • namelcx
  • 2015年04月25日 13:23
  • 685

i/o——Java I/O底层

转自http://www.importnew.com/14111.html 缓存处理和内核vs用户空间 缓冲与缓冲的处理方式,是所有I/O操作的基础。术语“输入、输出”只对数据移入和移出缓存有...
  • chuntiandejiaobu10
  • chuntiandejiaobu10
  • 2016年09月07日 13:55
  • 122

简述Linux下的5种I/O模型

在说正文之前我觉得有必要解释一下同步(synchronous)IO,异步(asynchronous)IO,阻塞(blocking)IO和非阻塞(non-blocking)IO到底是什么,有什么区别:...
  • Sun_flower77
  • Sun_flower77
  • 2017年05月31日 23:25
  • 321

【重要】Linux I/O模型

socket阻塞与非阻塞,同步与异步、I/O模型 分类: c/c++ socket网络编程2012-04-12 16:35 32926人阅读 评论(22) 收藏 举报 socket...
  • chenchong_219
  • chenchong_219
  • 2014年06月29日 15:10
  • 1639

Linux网络编程---I/O复用模型之epoll

Linux网络编程—I/O复用模型之epoll1. epoll模型简介epoll是Linux多路服用IO接口select/poll的加强版,e对应的英文单词就是enhancement,中文翻译为增强,...
  • men_wen
  • men_wen
  • 2016年12月04日 21:08
  • 876

多路复用I/O模型之select

1 .所谓I/O多路复用是指内核一旦发现进程指定的一个或者多个I/O条件准备读取,它就通知该进程。I/O多路复用适用如下场合:  (1)当客户处理多个描述字时(一般是交互式输入和网络套接口),必须使用...
  • lixiaogang_theanswer
  • lixiaogang_theanswer
  • 2017年06月16日 17:08
  • 201

WinSock三种选择I/O模型

在《套接字socket及C/S通信的基本概念》和《WinSock编程基础》中,我们介绍了套接字的基本概念和WinSock API的基本调用规范。我们讨论了阻塞模式/非阻塞模式和同步I/O和异步I/...
  • liujiayu2
  • liujiayu2
  • 2015年06月16日 14:46
  • 823

Web服务与I/O模型

Web 服务和I/O模型 前言:当前最为流行的Web服务器就属Httpd和Nginx。Web 服务器到底干了什么事?简单点说就是接受用户请求,响应用户请求。这个过程的实现就是完成了跨主机之间的通信,...
  • lirou_
  • lirou_
  • 2017年04月06日 10:53
  • 469
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:Nginx源代码分析之I/O模型细节(七)
举报原因:
原因补充:

(最多只允许输入30个字)