12.Nginx启动流程之ngx_init_cycle

本文详细介绍了Nginx在启动过程中调用ngx_init_cycle函数所执行的关键任务,包括利用旧cycle预测内存需求、新cycle初始化、配置文件解析、模块初始化、pidfile创建以及错误回滚和资源关闭等操作。
摘要由CSDN通过智能技术生成

Nginx的启动流程在ngx_add_inherited_sockets之后,会调用ngx_init_cycle来创建cycle,从下面的代码分析,我们可以看出,在ngx_init_cycle中,Nginx主要进行了以下工作:

1.根据旧cycle来创建新cycle,根据旧cycle的经验,可以推测出创建新cycle所需要的内存空间

2.对新cycle进行初始化工作,包括打开文件、打开监听套接字等

3.解析配置文件

4.模块初始化

5.创建pidfile文件

6.出错回滚及关闭不需要的文件描述符和监听套接字


/* core/ngx_core.h */

typedef struct ngx_cycle_s       ngx_cycle_t;


/* core/ngx_cycle.h */

struct ngx_cycle_s {                 // ngx_cycle_t结构体定义
    void           ****conf_ctx;     // 模块配置上下文数组
    ngx_pool_t        *pool;         // 使用的内存池指针

    ngx_log_t         *log;          // 使用的日志指针
    ngx_log_t         *new_log;      // 新的日志指针

    ngx_array_t        listening;    // 监听套接字数组
    ngx_array_t        pathes;       // 路径数组
    ngx_list_t         open_files;   // 打开文件链表

    ngx_uint_t         connection_n; // 最大连接数
    ngx_connection_t  *connections;
    ngx_event_t       *read_events;
    ngx_event_t       *write_events;

    ngx_cycle_t       *old_cycle;    // 旧cycle

    ngx_str_t          conf_file;    // 配置文件
    ngx_str_t          root;         // 工作目录
};


/* core/ngx_connection.c */

/* 为cycle打开监听套接字
   param cycle: ngx_cycle_t结构体指针
*/
ngx_int_t ngx_open_listening_sockets(ngx_cycle_t *cycle)
{
    ngx_uint_t        tries, failed, reuseaddr, i;
    ngx_err_t         err;
    ngx_log_t        *log;
    ngx_socket_t      s;
    ngx_listening_t  *ls;

    reuseaddr = 1;
#if (NGX_SUPPRESS_WARN)
    failed = 0;
#endif

    log = cycle->log;

    // tries用于指定重试次数, 这里为5次
    for (tries = /* STUB */ 5; tries; tries--) {
        failed = 0;

        ls = cycle->listening.elts;
        // 遍历cycle的监听套接字数组
        for (i = 0; i < cycle->listening.nelts; i++) {

            if (ls[i].ignore) {
                // 如果监听套接字的ignore成员不为0, 那么就直接忽略
                continue;
            }

            if (ls[i].fd != -1) {
                // 如果监听套接字的fd不为-1, 那么说明已经打开
                continue;
            }

            if (ls[i].inherited) {
                // 如果监听套接字的inherited不为0, 那么说明是继承而来的, 直接跳过
                continue;
            }

            // ngx_socket宏就是调用socket库函数来创建套接字
            s = ngx_socket(ls[i].family, ls[i].type, ls[i].protocol,
                           ls[i].flags);

            if (s == -1) {
                ngx_log_error(NGX_LOG_EMERG, log, ngx_socket_errno,
                              ngx_socket_n " %s failed", ls[i].addr_text.data);
                return NGX_ERROR;
            }

#if (WIN32)
            /*
             * Winsock assignes a socket number divisible by 4
             * so to find a connection we divide a socket number by 4.
             */

            if (s % 4) {
                ngx_log_error(NGX_LOG_EMERG, ls->log, 0,
                              ngx_socket_n " created socket %d", s);
                return NGX_ERROR;
            }
#endif
            // 设置套接字选项SO_REUSEADDR, 即允许端口重用
            if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR,
                           (const void *) &reuseaddr, sizeof(int)) == -1) {
                ngx_log_error(NGX_LOG_EMERG, log, ngx_socket_errno,
                              "setsockopt(SO_REUSEADDR) %s failed",
                              ls[i].addr_text.data);
                return NGX_ERROR;
            }

            if (!(ngx_event_flags & NGX_USE_AIO_EVENT)) {
                // 如果不使用异步IO
                
                // 使用fcntl或者ioctl设置套接字为非阻塞
                if (ngx_nonblocking(s) == -1) {
                    ngx_log_error(NGX_LOG_EMERG, log, ngx_socket_errno,
                                  ngx_nonblocking_n " %s failed",
                                  ls[i].addr_text.data);
                    return NGX_ERROR;
                }
            }

#if 0
            if (ls[i].nonblocking) {
                if (ngx_nonblocking(s) == -1) {
                    ngx_log_error(NGX_LOG_EMERG, log, ngx_socket_errno,
                                  ngx_nonblocking_n " %s failed",
                                  ls[i].addr_text.data);
                    return NGX_ERROR;
                }
            }
#endif
            // 绑定套接字到监听套接字的sockaddr成员所指地址结构
            if (bind(s, ls[i].sockaddr, ls[i].socklen) == -1) {
                err = ngx_socket_errno;
                ngx_log_error(NGX_LOG_EMERG, log, err,
                              "bind() to %s failed", ls[i].addr_text.data);
                // 绑定失败的错误不是NGX_EADDRINUSE即EADDRINUSE时, 返回NGX_ERROR
                if (err != NGX_EADDRINUSE)
                    return NGX_ERROR;

                // 绑定失败的错误是NGX_EADDRINUSE即EADDRINUSE时, 关闭套接字
                if (ngx_close_socket(s) == -1)
                    ngx_log_error(NGX_LOG_EMERG, log, ngx_socket_errno,
                                  ngx_close_socket_n " %s failed",
                                  ls[i].addr_text.data);

                failed = 1;
                continue;
            }

            // 调用listen使套接字转变为一个被动套接字, backlog由ls[i]的backlog成员指定
            if (listen(s, ls[i].backlog) == -1) {
                ngx_log_error(NGX_LOG_EMERG, log, ngx_socket_errno,
                            
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值