Nginx源码研究二:NGINX的网络IO

  NGINX作为服务端的应用程序,在客户端发出数据后,服务端在做着这样一些处理,数据先会经过网卡,网卡会和操作系统做交互,经过操作系统的协议栈处理,再和不同的应用程序交互。

  在这里面涉及两个概念,一个是用户态,一个是内核态。应用程序通过系统调用函数进入内核空间,内核运行进行数据准备和数据拷贝等工作。对于NGINX来说,他是作为应用程序和操作系统交互,即是用户态和内核态的之间的交互,NGINX和内核交互方式有很多,例如open(),read() 等都是在和内核交互,而对于网络IO来说,我们知道linux下的网络IO主要有五种:

一是阻塞IO,应用程序调用内核函数,阻塞到内核完成数据准备和数据拷贝的全过程。

二是非阻塞IO,应用程序调用内核函数,不断的查问内核数据是否准备好,直到内核数据准备好,再阻塞到内核完成数据拷贝。

第三种是I/O复用,应用程序调用内核参数,告知内核关心的事件,内核在收到该事件准备好的数据后,通知应用程序,应用程序再阻塞到内核的数据拷贝完成,一般web服务器都采用这样的IO模型,例如Apache采用的select/poll,当然nginx也支持select/poll,但是在linux2.6后,NGINX一般选择epoll。

第四种是信号,应用程序安置一个信号处理函数,运行过程不阻塞,操作系统在将数据准备好后,会发送一个信号给应用程序,应用程序的信号处理函数可以做IO处理。

第五种的异步,应用程序在调用操作系统提供的异步IO函数,例如aio_read。

告知操作系统发出的请求无需立即返回,待操作系统做完数据准备和数据拷贝后,再通知应用程序通过系统调用函数指定的信号。

实际上网络I/O模型中,前四种都是同步模型,第五种是异步模型。

我们先看一下NGINX的module里面支持的IO模型。

|-- event
|   |-- modules
|   |   |-- ngx_aio_module.c
|   |   |-- ngx_devpoll_module.c
|   |   |-- ngx_epoll_module.c
|   |   |-- ngx_eventport_module.c
|   |   |-- ngx_kqueue_module.c
|   |   |-- ngx_poll_module.c
|   |   |-- ngx_rtsig_module.c
|   |   |-- ngx_select_module.c
|   |   `-- ngx_win32_select_module.c

在本章,将重点研究NGINX使用epoll做网络IO。

NGINX做网络IO,涉及到三个module:


module名称


类型


所在文件


ngx_events_module


NGX_CORE_MODULE


ngx_event.c


ngx_event_core_module


NGX_EVENT_MODULE


ngx_event.c


ngx_epoll_module


NGX_EVENT_MODULE


module/ngx_epoll_module.c

  在上一章,提到了module的启动过程,在init_cycle函数,对ngx_events_module的配置信息做了生成,通过分析配置文件,调用ngx_events_commands去对NGX_EVENT_MODULE做了配置信息的生成,分析,初始化。

一、master-work工作模式的处理过程

在解决配置信息的处理后,我们来看看进程的处理过程

1、  我们选择master-work工作模式

ngx_master_process_cycle(ngx_cycle_t *cycle)
{
        ......

    ngx_start_worker_processes(cycle, ccf->worker_processes,
                               NGX_PROCESS_RESPAWN);
    ngx_start_cache_manager_processes(cycle, 0);

    ......
}

2、

static void
ngx_start_worker_processes(ngx_cycle_t *cycle, ngx_int_t n, ngx_int_t type)
{
    ngx_int_t      i;
    ngx_channel_t  ch;

    ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, "start worker processes");

    ch.command = NGX_CMD_OPEN_CHANNEL;

    for (i = 0; i < n; i++) {

        ngx_spawn_process(cycle, ngx_worker_process_cycle,
                          (void *) (intptr_t) i, "worker process", type);

        ch.pid = ngx_processes[ngx_process_slot].pid; //主进程的情况
        ch.slot = ngx_process_slot;
        ch.fd = ngx_processes[ngx_process_slot].channel[0];

        ngx_pass_open_channel(cycle, &ch);
    }
}

3、

//进程生成
ngx_pid_t
ngx_spawn_process(ngx_cycle_t *cycle, ngx_spawn_proc_pt proc, void *data,
    char *name, ngx_int_t respawn)
{
   ……

    pid = fork();

    switch (pid) {

    case -1:
        ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
                      "fork() failed while spawning \"%s\"", name);
        ngx_close_channel(ngx_processes[s].channel, cycle->log);
        return NGX_INVALID_PID;

    case 0: //子进程进入到proc
        ngx_pid = ngx_getpid();
        proc(cycle, data);
        break;

    default: //父进程继续
        break;
    }

    ……

    return pid;
}

4、

static void
ngx_worker_process_cycle(ngx_cycle_t *cycle, void *data)
{
……

    ngx_worker_process_init(cycle, worker);

    ngx_setproctitle("worker process");

……

    for ( ;; ) {
……
        ngx_process_events_and_timers(cycle);
        ……
    }
}

5、

static void
ngx_worker_process_init(ngx_cycle_t *cycle, ngx_int_t worker)
{
    ……

    for (i = 0; ngx_modules[i]; i++) {
        if (ngx_modules[i]->init_process) {
            if (ngx_modules[i]->init_process(cycle) == NGX_ERROR) {
                /* fatal */
                exit(2);
            }
        }
    }

 ……

    if (ngx_add_channel_event(cycle, ngx_channel, NGX_READ_EVENT,
                              ngx_channel_handler)
        == NGX_ERROR)
    {
        /* fatal */
        exit(2);
    }
}

6、

static ngx_int_t
ngx_event_process_init(ngx_cycle_t *cycle)
{
    ......

    //初始化module的action

    ......
    cycle->connections =
        ngx_alloc(sizeof(ngx_connection_t) * cycle->connection_n, cycle->log);
    if (cycle->connections == NULL) {
        return NGX_ERROR;
    }

    c = cycle->connections;

    cycle->read_events = ngx_alloc(sizeof(ngx_event_t) * cycle->connection_n,
                                   cycle->log);
    if (cycle->read_events == NULL) {
        return NGX_ERROR;
    }

    rev = cycle->read_events;
    for (i = 0; i < cycle->connection_n; i++) {
        rev[i].closed = 1;
        rev[i].instance = 1;
#if (NGX_THREADS)
        rev[i].lock = &c[i].lock;
        rev[i].own_lock = &c[i].lock;
#endif
    }

    cycle->write_events = ngx_alloc(sizeof(ngx_event_t) * cycle->connection_n,
                                    cycle->log);
    if (cycle->write_events == NULL) {
        return NGX_ERROR;
    }

    wev = cycle->write_events;
    for (i = 0; i < cycle->connection_n; i++) {
        wev[i].closed = 1;
#if (NGX_THREADS)
        wev[i].lock = &c[i].lock;
        wev[i].own_lock = &c[i].lock;
#endif
    }

    i = cycle->connection_n;
    next = NULL;

    do {
        i--;

        c[i].data = next;
        c[i].read = &cycle->read_events[i];
        c[i].write = &cycle->write_events[i];
        c[i].fd = (ngx_socket_t) -1;

        next = &c[i];

#if (NGX_THREADS)
        c[i].lock = 0;
#endif
    } while (i);

    cycle->free_connections = next;
    cycle->free_connection_n = cycle->connection_n;

    /* for each listening socket */

    ls = cycle->listening.elts;
    for (i = 0; i < cycle->listening.nelts; i++) {

        ......

        rev->handler = ngx_event_accept;

        if (ngx_use_accept_mutex) {
            continue;
        }

        if (ngx_event_flags & NGX_USE_RTSIG_EVENT) {
            if (ngx_add_conn(c) == NGX_ERROR) {
                return NGX_ERROR;
            }

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

#endif

    }

    return NGX_OK;
}
时间: 2024-08-02 07:40:11

Nginx源码研究二:NGINX的网络IO的相关文章

Nginx源码研究二:NGINX的内存管理

关于nginx的内存使用,我们先看代码,下面是nginx_cycle.c中对全局数据结构cycle的初始化过程 pool = ngx_create_pool(NGX_CYCLE_POOL_SIZE, log); //申请16K的内存池 if (pool == NULL) { return NULL; } pool->log = log; cycle = ngx_pcalloc(pool, sizeof(ngx_cycle_t)); if (cycle == NULL) { ngx_destroy

Chrome自带恐龙小游戏的源码研究(二)

在上一篇<Chrome自带恐龙小游戏的源码研究(一)>中实现了地面的绘制和运动,这一篇主要研究云朵的绘制. 云朵的绘制通过Cloud构造函数完成.Cloud实现代码如下: 1 Cloud.config = { 2 HEIGHT:14, //云朵sprite的高度 3 MAX_CLOUD_GAP:400, //两朵云之间的最大间隙 4 MAX_SKY_LEVEL:30, //云朵的最大高度 5 MIN_CLOUD_GAP:100, //两朵云之间的最小间隙 6 MIN_SKY_LEVEL:71,

mqtt协议-broker之moqutte源码研究二之Connect报文处理

先上一个图,大概说明一下moquette 的类之间的关系 一.ProtocolProcessor类该类是moquette里面的最终要的类,负责所有报文的处理,持有所有各模块功能的实现对象的引用, 下面详细介绍 protected ConnectionDescriptorStore connectionDescriptors;//所有的连接描述符文存储,即clientId与通道之间的映射集合 protected ConcurrentMap<RunningSubscription, Subscrip

Android Launcher源码研究(二) 加载app流程1

今天主要分析Android Launcher源码中的一些重要类之间的关系,基本的加载流程.先来看一个类图 Launcher.java 是主Activity 在onCreate方法里面初始化了LauncherMode实例. LauncherApplication app = ((LauncherApplication)getApplication()); mModel = app.setLauncher(this); 直接进入LauncherApplication.java的方法 Launcher

Nginx源码研究十四:ngx_http_core_run_phases

if (ngx_http_init_phases(cf, cmcf) != NGX_OK) { return NGX_CONF_ERROR; } if (ngx_http_init_headers_in_hash(cf, cmcf) != NGX_OK) { return NGX_CONF_ERROR; } for (m = 0; ngx_modules[m]; m++) { if (ngx_modules[m]->type != NGX_HTTP_MODULE) { continue; } m

Nginx源码研究七:ngx_http_core_main_conf_t

typedef struct { ngx_array_t servers; /* ngx_http_core_srv_conf_t */ ngx_http_phase_engine_t phase_engine; ngx_hash_t headers_in_hash; ngx_hash_t variables_hash; ngx_array_t variables; /* ngx_http_variable_t */ ngx_uint_t ncaptures; ngx_uint_t server

mqtt协议-broker之moqutte源码研究二之SUBSCRIBE报文处理

这一篇开始讲解moqutte对SUBSCRIBE报文的处理 代码不复杂public void processSubscribe(Channel channel, MqttSubscribeMessage msg) {String clientID = NettyUtils.clientID(channel);//从channel里面获取clientId,具体原理看下文int messageID = messageId(msg);LOG.info("Processing SUBSCRIBE mes

菜鸟nginx源码剖析数据结构篇(二) 双向链表ngx_queue_t

nginx源码剖析数据结构篇(二) 双向链表ngx_queue_t Author:Echo Chen(陈斌) Email:[email protected] Blog:Blog.csdn.net/chen19870707 Date:October 20h, 2014 1.ngx_queue优势和特点 ngx_queue作为顺序容器链表,它优势在于其可以高效地执行插入.删除.合并操作,在插入删除的过程中,只需要修改指针指向,而不需要拷贝数据,因此,对于频繁修改的容器很适合.此外,相对于STL li

nginx源码分析之网络初始化

nginx作为一个高性能的HTTP服务器,网络的处理是其核心,了解网络的初始化有助于加深对nginx网络处理的了解,本文主要通过nginx的源代码来分析其网络初始化. 从配置文件中读取初始化信息 与网络有关的配置命令主要有两个:listen和sever_name.首先先了解这两个命令的用法. listen listen命令设置nginx监听地址,nginx从这里接受请求.对于IP协议,这个地址就是address和port:对于UNIX域套接字协议,这个地址就是path. 一条listen指令只能