关闭

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

451人阅读 评论(0) 收藏 举报

这里谈谈源码的具体实现


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

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);

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


0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:153639次
    • 积分:2265
    • 等级:
    • 排名:第16695名
    • 原创:72篇
    • 转载:41篇
    • 译文:1篇
    • 评论:16条
    最新评论