nginx启动初始化代码执行流程
字数 1177 2025-08-15 21:33:32

Nginx启动初始化及代码执行流程详解

1. Nginx事件模块初始化

Nginx的事件模块初始化主要在ngx_event_process_init函数中完成:

static ngx_int_t ngx_event_process_init(ngx_cycle_t *cycle) {
    // 分配连接和事件内存
    cycle->connections = ngx_alloc(sizeof(ngx_connection_t) * cycle->connection_n, cycle->log);
    cycle->read_events = ngx_alloc(sizeof(ngx_event_t) * cycle->connection_n, cycle->log);
    
    // 初始化监听套接字
    ls = cycle->listening.elts;
    for (i = 0; i < cycle->listening.nelts; i++) {
        c = ngx_get_connection(ls[i].fd, cycle->log);
        c->listening = &ls[i];
        rev = c->read;
        
        // 设置accept回调函数
        rev->handler = ngx_http_accept;
        ngx_add_event(rev, NGX_READ_EVENT, 0);
    }
}

关键点:

  • 为所有可能的连接预分配内存
  • 遍历所有监听套接字,为每个监听套接字设置读事件
  • 设置读事件的回调函数为ngx_event_accept
  • 将读事件添加到事件监听机制中

2. 连接接受处理流程

当有新连接到达时,ngx_event_accept函数被调用:

void ngx_event_accept(ngx_event_t *ev) {
    lc = ev->data;
    ls = lc->listening;
    
    // 接受新连接
    s = accept(lc->fd, (struct sockaddr *) sa, &socklen);
    
    // 获取连接对象
    c = ngx_get_connection(s, ev->log);
    
    // 添加连接事件
    ngx_add_conn(c);
    
    // 调用监听器的handler处理新连接
    ls->handler(c);
}

关键点:

  • 调用系统accept接受新连接
  • 通过ngx_get_connection获取连接对象
  • 调用监听器配置的handler处理新连接

3. HTTP模块初始化流程

HTTP模块的初始化从ngx_http_block开始:

static char * ngx_http_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) {
    // 分配main/srv/loc配置数组
    ctx->main_conf = ngx_pcalloc(cf->pool, sizeof(void *) * ngx_http_max_module);
    ctx->srv_conf = ngx_pcalloc(cf->pool, sizeof(void *) * ngx_http_max_module);
    ctx->loc_conf = ngx_pcalloc(cf->pool, sizeof(void *) * ngx_http_max_module);
    
    // 调用各模块的create配置函数
    for (m = 0; ngx_modules[m]; m++) {
        if (ngx_modules[m]->type != NGX_HTTP_MODULE) {
            continue;
        }
        
        module = ngx_modules[m]->ctx;
        mi = ngx_modules[m]->ctx_index;
        
        if (module->create_main_conf) {
            ctx->main_conf[mi] = module->create_main_conf(cf);
        }
        if (module->create_srv_conf) {
            ctx->srv_conf[mi] = module->create_srv_conf(cf);
        }
        if (module->create_loc_conf) {
            ctx->loc_conf[mi] = module->create_loc_conf(cf);
        }
    }
    
    // 解析http块内配置
    cf->module_type = NGX_HTTP_MODULE;
    cf->cmd_type = NGX_HTTP_MAIN_CONF;
    rv = ngx_conf_parse(cf, NULL);
    
    // 优化服务器配置
    if (ngx_http_optimize_servers(cf, cmcf, cmcf->ports) != NGX_OK) {
        return NGX_CONF_ERROR;
    }
}

关键点:

  • 为所有HTTP模块创建配置存储空间
  • 调用各模块的create配置函数初始化配置
  • 解析http块内的配置指令
  • 最后优化服务器配置

4. 服务器配置初始化

服务器配置初始化流程:

static char * ngx_http_core_server(ngx_conf_t *cf, ngx_command_t *cmd, void *dummy) {
    // 创建server配置上下文
    ctx = ngx_pcalloc(cf->pool, sizeof(ngx_http_conf_ctx_t));
    ctx->main_conf = http_ctx->main_conf;
    ctx->srv_conf = ngx_pcalloc(cf->pool, sizeof(void *) * ngx_http_max_module);
    ctx->loc_conf = ngx_pcalloc(cf->pool, sizeof(void *) * ngx_http_max_module);
    
    // 解析server块内配置
    cf->ctx = ctx;
    cf->cmd_type = NGX_HTTP_SRV_CONF;
    rv = ngx_conf_parse(cf, NULL);
}

static char * ngx_http_core_listen(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) {
    // 解析监听地址
    cscf->listen = 1;
    value = cf->args->elts;
    ngx_memzero(&u, sizeof(ngx_url_t));
    u.url = value[1];
    ngx_parse_url(cf->pool, &u);
    
    // 添加监听配置
    if (ngx_http_add_listen(cf, cscf, &lsopt) == NGX_OK) {
        return NGX_CONF_OK;
    }
}

关键点:

  • 为server块创建配置上下文
  • 解析server块内的配置指令
  • 解析listen指令并添加到监听配置中

5. 监听配置优化

监听配置优化流程:

static ngx_int_t ngx_http_optimize_servers(ngx_conf_t *cf, ngx_http_core_main_conf_t *cmcf, ngx_array_t *ports) {
    port = ports->elts;
    for (p = 0; p < ports->nelts; p++) {
        addr = port[p].addrs.elts;
        
        // 处理服务器名称
        for (a = 0; a < port[p].addrs.nelts; a++) {
            if (ngx_http_server_names(cf, cmcf, &addr[a]) != NGX_OK) {
                return NGX_ERROR;
            }
        }
        
        // 初始化监听
        if (ngx_http_init_listening(cf, &port[p]) != NGX_OK) {
            return NGX_ERROR;
        }
    }
}

static ngx_int_t ngx_http_server_names(ngx_conf_t *cf, ngx_http_core_main_conf_t *cmcf, ngx_http_conf_addr_t *addr) {
    // 初始化哈希表
    ha.pool = cf->pool;
    
    // 添加服务器名称到哈希表
    cscfp = addr->servers.elts;
    for (s = 0; s < addr->servers.nelts; s++) {
        name = cscfp[s]->server_names.elts;
        for (n = 0; n < cscfp[s]->server_names.nelts; n++) {
            rc = ngx_hash_add_key(&ha, &name[n].name, name[n].server, NGX_HASH_WILDCARD_KEY);
        }
    }
    
    // 初始化哈希
    hash.hash = &addr->hash;
    ngx_hash_init(&hash, ha.keys.elts, ha.keys.nelts);
}

static ngx_listening_t * ngx_http_add_listening(ngx_conf_t *cf, ngx_http_conf_addr_t *addr) {
    // 创建监听套接字
    ls = ngx_create_listening(cf, &addr->opt.u.sockaddr, addr->opt.socklen);
    
    // 设置连接处理函数
    ls->handler = ngx_http_init_connection;
}

关键点:

  • 为每个端口和地址处理服务器名称
  • 创建服务器名称哈希表用于快速查找
  • 创建实际的监听套接字
  • 设置连接处理函数为ngx_http_init_connection

6. 连接管理机制

Nginx的连接管理机制:

ngx_connection_t * ngx_get_connection(ngx_socket_t s, ngx_log_t *log) {
    // 从空闲连接池获取连接
    c = ngx_cycle->free_connections;
    ngx_cycle->free_connections = c->data;
    ngx_cycle->free_connection_n--;
    
    // 初始化连接
    rev = c->read;
    wev = c->write;
    ngx_memzero(c, sizeof(ngx_connection_t));
    c->read = rev;
    c->write = wev;
    c->fd = s;
    
    // 设置instance标志
    rev->instance = !instance;
    wev->instance = !instance;
    rev->data = c;
    wev->data = c;
}

void ngx_free_connection(ngx_connection_t *c) {
    // 将连接放回空闲池
    c->data = ngx_cycle->free_connections;
    ngx_cycle->free_connections = c;
    ngx_cycle->free_connection_n++;
    
    // 清除文件描述符引用
    if (ngx_cycle->files) {
        ngx_cycle->files[c->fd] = NULL;
    }
}

关键点:

  • 使用连接池管理连接对象
  • 通过instance标志防止事件混淆
  • 连接重用减少内存分配开销

7. 事件处理机制

Nginx的事件处理机制:

static ngx_int_t ngx_epoll_add_event(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags) {
    // 设置事件数据,包含instance标志
    ee.data.ptr = (void *) ((uintptr_t) c | ev->instance);
}

static ngx_int_t ngx_epoll_process_events(ngx_cycle_t *cycle, ngx_msec_t timer, ngx_uint_t flags) {
    // 处理事件
    c = event_list[i].data.ptr;
    instance = (uintptr_t) c & 1;
    
    // 检查instance是否匹配
    if (c->fd == -1 || rev->instance != instance) {
        // 不匹配则忽略该事件
    }
}

关键点:

  • 将instance标志嵌入事件数据中
  • 处理事件时检查instance是否匹配
  • 防止处理已关闭连接的事件

8. 第三方模块开发示例

一个简单的输出模块示例:

// 模块指令定义
static ngx_command_t ngx_http_shuchu_commands[] = {
    { ngx_string("shuchu"),
      NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
      ngx_http_shuchu,
      NGX_HTTP_LOC_CONF_OFFSET,
      offsetof(ngx_http_shuchu_loc_conf_t, content),
      NULL },
    ngx_null_command
};

// 模块上下文
static ngx_http_module_t ngx_http_shuchu_ctx = {
    NULL,                          /* preconfiguration */
    ngx_http_shuchu_init,          /* postconfiguration */
    NULL,                          /* create main configuration */
    NULL,                          /* init main configuration */
    NULL,                          /* create server configuration */
    NULL,                          /* merge server configuration */
    ngx_http_shuchu_create_loc_conf, /* create location configuration */
    ngx_http_shuchu_merge_loc_conf   /* merge location configuration */
};

// 模块定义
ngx_module_t ngx_http_shuchu_module = {
    NGX_MODULE_V1,
    &ngx_http_shuchu_ctx,          /* module context */
    ngx_http_shuchu_commands,      /* module directives */
    NGX_HTTP_MODULE,               /* module type */
    NULL,                          /* init master */
    NULL,                          /* init module */
    NULL,                          /* init process */
    NULL,                          /* init thread */
    NULL,                          /* exit thread */
    NULL,                          /* exit process */
    NULL,                          /* exit master */
    NGX_MODULE_V1_PADDING
};

关键点:

  • 三大核心结构:指令集、上下文、模块定义
  • 指令处理函数
  • 配置创建和合并函数
  • 模块类型和生命周期函数

9. 配置解析流程

Nginx配置解析流程:

char * ngx_conf_parse(ngx_conf_t *cf, ngx_str_t *filename) {
    // 打开配置文件
    fd = ngx_open_file(filename->data, NGX_FILE_RDONLY, NGX_FILE_OPEN, 0);
    
    // 读取并解析token
    rc = ngx_conf_read_token(cf);
    
    // 处理指令
    rc = ngx_conf_handler(cf, rc);
}

static ngx_int_t ngx_conf_handler(ngx_conf_t *cf, ngx_int_t last) {
    // 遍历所有模块
    for (i = 0; ngx_modules[i]; i++) {
        cmd = ngx_modules[i]->commands;
        
        // 遍历模块指令
        for ( /* void */ ; cmd->name.len; cmd++) {
            // 根据指令类型获取配置
            if (cmd->type & NGX_DIRECT_CONF) {
                conf = ((void **) cf->ctx)[ngx_modules[i]->index];
            } else if (cmd->type & NGX_MAIN_CONF) {
                conf = &(((void **) cf->ctx)[ngx_modules[i]->index]);
            } else if (cf->ctx) {
                confp = *(void **) ((char *) cf->ctx + cmd->conf);
                if (confp) {
                    conf = confp[ngx_modules[i]->ctx_index];
                }
            }
            
            // 调用指令处理函数
            rv = cmd->set(cf, cmd, conf);
        }
    }
}

关键点:

  • 递归解析配置文件
  • 根据指令类型获取正确的配置上下文
  • 调用模块提供的指令处理函数
  • 支持多级配置块(http/server/location)

总结

Nginx的启动和初始化流程是一个复杂但高效的过程,主要包含以下几个关键阶段:

  1. 事件模块初始化:预分配连接和事件内存,设置监听套接字的事件处理函数
  2. HTTP模块初始化:创建配置结构,调用各模块的初始化函数
  3. 配置解析:递归解析配置文件,处理各模块的指令
  4. 服务器配置优化:处理服务器名称,创建哈希表,初始化监听套接字
  5. 连接管理:使用连接池管理连接,通过instance标志防止事件混淆
  6. 事件处理:高效的事件处理机制,支持高并发

理解这些流程对于开发Nginx模块和深入理解Nginx工作原理至关重要。

Nginx启动初始化及代码执行流程详解 1. Nginx事件模块初始化 Nginx的事件模块初始化主要在 ngx_event_process_init 函数中完成: 关键点: 为所有可能的连接预分配内存 遍历所有监听套接字,为每个监听套接字设置读事件 设置读事件的回调函数为 ngx_event_accept 将读事件添加到事件监听机制中 2. 连接接受处理流程 当有新连接到达时, ngx_event_accept 函数被调用: 关键点: 调用系统accept接受新连接 通过 ngx_get_connection 获取连接对象 调用监听器配置的handler处理新连接 3. HTTP模块初始化流程 HTTP模块的初始化从 ngx_http_block 开始: 关键点: 为所有HTTP模块创建配置存储空间 调用各模块的create配置函数初始化配置 解析http块内的配置指令 最后优化服务器配置 4. 服务器配置初始化 服务器配置初始化流程: 关键点: 为server块创建配置上下文 解析server块内的配置指令 解析listen指令并添加到监听配置中 5. 监听配置优化 监听配置优化流程: 关键点: 为每个端口和地址处理服务器名称 创建服务器名称哈希表用于快速查找 创建实际的监听套接字 设置连接处理函数为 ngx_http_init_connection 6. 连接管理机制 Nginx的连接管理机制: 关键点: 使用连接池管理连接对象 通过instance标志防止事件混淆 连接重用减少内存分配开销 7. 事件处理机制 Nginx的事件处理机制: 关键点: 将instance标志嵌入事件数据中 处理事件时检查instance是否匹配 防止处理已关闭连接的事件 8. 第三方模块开发示例 一个简单的输出模块示例: 关键点: 三大核心结构:指令集、上下文、模块定义 指令处理函数 配置创建和合并函数 模块类型和生命周期函数 9. 配置解析流程 Nginx配置解析流程: 关键点: 递归解析配置文件 根据指令类型获取正确的配置上下文 调用模块提供的指令处理函数 支持多级配置块(http/server/location) 总结 Nginx的启动和初始化流程是一个复杂但高效的过程,主要包含以下几个关键阶段: 事件模块初始化 :预分配连接和事件内存,设置监听套接字的事件处理函数 HTTP模块初始化 :创建配置结构,调用各模块的初始化函数 配置解析 :递归解析配置文件,处理各模块的指令 服务器配置优化 :处理服务器名称,创建哈希表,初始化监听套接字 连接管理 :使用连接池管理连接,通过instance标志防止事件混淆 事件处理 :高效的事件处理机制,支持高并发 理解这些流程对于开发Nginx模块和深入理解Nginx工作原理至关重要。