【nginx流程分析】

自上而下

写在前面

如果没有看到前面的文章,推荐读者看一下前面的文章,方便有一些快速的理解。
传送门
【nginx源码分析系列】
【nginx 模块分析】

main文件

首先我们看一下main文件所在的地方,地址是在/root/nginx/nginx-1.14.2/src/core/nginx.c,因为我是在/root下面解压的,读者可能因为解压的目录不同,而有所不同。照常我们还是先截个图。
在这里插入图片描述
左边的这一栏,是vim中的tagList,也算是vim中的一个插件,可以很方便的展示当前目录下面的所有变量和方法。

变量分析

可以看到开头定义的变量 ngx_buf_t,ngx_log_t,ngx_uint_t 之前我们已经说过,其实本质就是 unsigned long 。然后就是 ngx_cycle_t,ngx_conf_dump_t,ngx_core_conf_t,可参考 nginx流程分析之变量篇. 好的经过一番曲折我们变量篇再试告一段落,接下来就是方法的开始。

ngx_debug_init

这个其实没什么作用哈,这边就不浪费笔墨了。

ngx_strerror_init

这个从名字可以看出来,初始化错误,我们可以先看一下方法
在这里插入图片描述
我们开始分析,先是根据NGX_SYS_NERR和sizeof(ngx_str_t)给ngx_sys_errlist申请了NGX_SYS_NERR * sizeof(ngx_str_t) 大的内存,然后从下标0开始到NGX_SYS_NERR(107)结束,给ngx_sys_errlist 复制错误,可以看出这边nginx一共有107错误,当然这个也是全局变量。我这边列几个,Undefined error,Operation not permitted,No such file or directory,No such process,Interrupted system call等等,有兴趣的读者可以自己断点或者打印看看。
简单概括一下,这个方法就是申明了107种错误,存在ngx_sys_errlist这个结构中。

ngx_get_options

这个方法从名称上面可以看出来,是获取相关的参数。我们来看一下代码:

static ngx_int_t
ngx_get_options(int argc, char *const *argv)
{
    u_char     *p;
    ngx_int_t   i;

    for (i = 1; i < argc; i++) { //直接用参数第二位开始

        p = (u_char *) argv[i];

        if (*p++ != '-') { // 判断是都是 -v -p 这样的用-开头的命令 移动指针
            ngx_log_stderr(0, "invalid option: \"%s\"", argv[i]);
            return NGX_ERROR;
        }

        while (*p) { //判断 

            switch (*p++) { //判断当前值

            case '?':
            case 'h':
                ngx_show_version = 1;
                ngx_show_help = 1;
                break;

            case 'v':
                ngx_show_version = 1;
                break;

            case 'V':
                ngx_show_version = 1;
                ngx_show_configure = 1;
                break;

            case 't':
                ngx_test_config = 1;
                break;

            case 'T':
                ngx_test_config = 1;
                ngx_dump_config = 1;
                break;

            case 'q':
                ngx_quiet_mode = 1;
                break;

            case 'p':
                if (*p) {
                    ngx_prefix = p;
                    goto next;
                }

                if (argv[++i]) {
                    ngx_prefix = (u_char *) argv[i];
                    goto next;
                }

                ngx_log_stderr(0, "option \"-p\" requires directory name");
                return NGX_ERROR;

            case 'c':
                if (*p) {
                    ngx_conf_file = p;
                    goto next;
                }

                if (argv[++i]) {
                    ngx_conf_file = (u_char *) argv[i];
                    goto next;
                }

                ngx_log_stderr(0, "option \"-c\" requires file name");
                return NGX_ERROR;

            case 'g':
                if (*p) {
                    ngx_conf_params = p;
                    goto next;
                }

                if (argv[++i]) {
                    ngx_conf_params = (u_char *) argv[i];
                    goto next;
                }

                ngx_log_stderr(0, "option \"-g\" requires parameter");
                return NGX_ERROR;

            case 's':
                if (*p) {
                    ngx_signal = (char *) p;

                } else if (argv[++i]) {
                    ngx_signal = argv[i];

                } else {
                    ngx_log_stderr(0, "option \"-s\" requires parameter");
                    return NGX_ERROR;
                }

                if (ngx_strcmp(ngx_signal, "stop") == 0
                    || ngx_strcmp(ngx_signal, "quit") == 0
                    || ngx_strcmp(ngx_signal, "reopen") == 0
                    || ngx_strcmp(ngx_signal, "reload") == 0)
                {
                    ngx_process = NGX_PROCESS_SIGNALLER;
                    goto next;
                }

                ngx_log_stderr(0, "invalid option: \"-s %s\"", ngx_signal);
                return NGX_ERROR;

            default:
                ngx_log_stderr(0, "invalid option: \"%c\"", *(p - 1));
                return NGX_ERROR;
            }
        }

    next:

        continue;
    }

    return NGX_OK;
}

此时我们再看一下nginx的常用命令:
在这里插入图片描述
对比图片和代码,是不是就是一目了然。在调用ngx_get_options的时候,循环判断./nginx后面的参数,首先就是 ./nginx -h 或者 ./nginx -v 这样的简单命令
首先判断是不是-开头,如果不是直接返回错误,然后就是判断-后的字符,是 h,v还是其他等。
然后分别设置对应的全局变量。

比如 nginx -c 指定配置文件,其实就是在这里插入图片描述
设置了ngx_conf_file的全局变量。这样看下来是不是一目了然

ngx_show_version

其实就是nginx -h 后面的要走的,在上面可以看到在 nginx -h之后设置了
ngx_show_version = 1;
ngx_show_help = 1;
所以走到了这里,我们先看如下代码:
在这里插入图片描述
然后就是ngx_show_version_info这个方法了,我们先看看内容
在这里插入图片描述
再看看我们手动输入./nginx -h的命令,在这里插入图片描述
相比是不是一目了然,就是在ngx_show_version_info里面写的。方法就是ngx_write_stderr,其实就是写到了控制台。
然后就是 判断!ngx_test_config了,这个在 nginx -t 或者 nginx -T 的时候进行设置,主要是判断是否为了配置文件是否可以运行,如果没有,那么就直接返回了,

ngx_time_init

接下来就到了分析nginx初始化时间的地方,也就是ngx_time_init这个方法
在这里插入图片描述
因为这个方法内容比较多设计到时间操作和锁,所以我们单独开一个篇章分析,详见
【nginx流程分析之时间和并发控制】

获取进程id和父进程id

还是一样我们先看一下代码

    ngx_pid = ngx_getpid();
    ngx_parent = ngx_getppid();

我们先看一下ngx_getpid和ngx_getppid的定义:

#define ngx_getpid   getpid
#define ngx_getppid  getppid

然后可以看出来就是c语言中,获取当前进程的进程id和父进程的进程id。

ngx_log_init

从名称可以看出这个是初始化nginx的日志方法,还是一样我们先看一下具体的实现逻辑:

ngx_log_t *
ngx_log_init(u_char *prefix)
{
    u_char  *p, *name;
    size_t   nlen, plen;

    ngx_log.file = &ngx_log_file;
    ngx_log.log_level = NGX_LOG_NOTICE;

    name = (u_char *) NGX_ERROR_LOG_PATH;

    /*
     * we use ngx_strlen() here since BCC warns about
     * condition is always false and unreachable code
     */

    nlen = ngx_strlen(name);

    if (nlen == 0) {
        ngx_log_file.fd = ngx_stderr;
        return &ngx_log;
    }

    p = NULL;

    if (name[0] != '/') {
        if (prefix) {
            plen = ngx_strlen(prefix);

        } else {
            prefix = (u_char *) NGX_PREFIX;
            plen = ngx_strlen(prefix);
        }
        if (plen) {
            name = malloc(plen + nlen + 2);
            if (name == NULL) {
                return NULL;
            }
            p = ngx_cpymem(name, prefix, plen);
            if (!ngx_path_separator(*(p - 1))) {
                *p++ = '/';
            }
            ngx_cpystrn(p, (u_char *) NGX_ERROR_LOG_PATH, nlen + 1);
            p = name;
        }
    }

    ngx_log_file.fd = ngx_open_file(name, NGX_FILE_APPEND,
                                    NGX_FILE_CREATE_OR_OPEN,
                                    NGX_FILE_DEFAULT_ACCESS);

    if (ngx_log_file.fd == NGX_INVALID_FILE) {
        ngx_log_stderr(ngx_errno,
                       "[alert] could not open error log file: "
                       ngx_open_file_n " \"%s\" failed", name);
        ngx_log_file.fd = ngx_stderr;
    }

    if (p) {
        ngx_free(p);
    }

    return &ngx_log;
}

然后这边为了逻辑的流程,把代码中的针对win平台的宏定义去掉了。然后接下来我们简单分析一下这个流程:
首先就是把ngx_log_file的指针放到了ngx_log.file中,然后就是定义ngx_log.log_level的类型为notice。
然后看一下NGX_ERROR_LOG_PATH这个常量定义

#define NGX_ERROR_LOG_PATH  "logs/error.log"

其实就是logs/error.log,然后就是判断name[0] != ‘/’,此时我们的name是logs/error.log,很明显不是的,然后进入方法。
因为我们在运行nginx的时候一般不指定前缀,所以一般都是空,所以系统会指定一个默认的前缀给我们,也就是NGX_PREFIX,然后我们点进去看一下就是/usr/local/nginx/,这个也是我们默认的安装目录。
然后接下来,通过ngx_cpymem把前缀也就是/usr/local/nginx/ 赋值给p,再把NGX_ERROR_LOG_PATH赋值给p,那么此时p就是/usr/local/nginx/logs/error.log,感兴趣的同学在运行nginx之后,看一下/usr/local/nginx/logs/error.log这个目录,是不是有很多日志。最后把p赋值给name。
然后就是通过ngx_open_file ,ngx_open_file其实就是c语言中的open,然后打开获取得到一个文件描述符,如果失败用系统的默认STDERR_FILENO描述符输出到控制台,然后返回ngx_log。

init_cycle 初始化

首先我们来看一下代码
在这里插入图片描述
其实代码也是比较简单,主要就是把init_cycle 初始化,然后把log赋值过去,然后把init_cycle赋值给ngx_cycle。

ngx_create_pool

然后这个就是创建nginx的内存池,详见 nginx内存初始化和操作

ngx_save_argv

首先看一下方法,因为比较简单所以就加了一些注释

static ngx_int_t
ngx_save_argv(ngx_cycle_t *cycle, int argc, char *const *argv)
{
    size_t     len;
    ngx_int_t  i;

    ngx_os_argv = (char **) argv;
    ngx_argc = argc;

    //二级指针 char **ngx_argv;
    ngx_argv = ngx_alloc((argc + 1) * sizeof(char *), cycle->log);
    if (ngx_argv == NULL) {
        return NGX_ERROR;
    }

    for (i = 0; i < argc; i++) {
        len = ngx_strlen(argv[i]) + 1;

        //每个指针进行分配内存
        ngx_argv[i] = ngx_alloc(len, cycle->log);
        if (ngx_argv[i] == NULL) {
            return NGX_ERROR;
        }

        //将参数复制过去
        (void) ngx_cpystrn((u_char *) ngx_argv[i], (u_char *) argv[i], len);
    }
    ngx_argv[i] = NULL;

    //将系统的环境变量存到ngx_os_environ中
    ngx_os_environ = environ;

    return NGX_OK;
}

ngx_process_options

这个方法主要是初始化cycle的配置文件,因为我们就在代码中增加注释,

static ngx_int_t
ngx_process_options(ngx_cycle_t *cycle)
{
    u_char  *p;
    size_t   len;

    //这里我们没有指定前缀 因此ngx_prefix为null
    if (ngx_prefix) {
        len = ngx_strlen(ngx_prefix);
        p = ngx_prefix;

        if (len && !ngx_path_separator(p[len - 1])) {
            p = ngx_pnalloc(cycle->pool, len + 1);
            if (p == NULL) {
                return NGX_ERROR;
            }

            ngx_memcpy(p, ngx_prefix, len);
            p[len++] = '/';
        }

        cycle->conf_prefix.len = len;
        cycle->conf_prefix.data = p;
        cycle->prefix.len = len;
        cycle->prefix.data = p;

    } else {
        //设置cycle->conf_prefix为conf/
        ngx_str_set(&cycle->conf_prefix, NGX_CONF_PREFIX);

        //设置cycle->prefix 为/usr/local/nginx/
        ngx_str_set(&cycle->prefix, NGX_PREFIX);

    }

    //如果没有指定nginx配置 ngx_conf_file为null
    if (ngx_conf_file) {
        cycle->conf_file.len = ngx_strlen(ngx_conf_file);
        cycle->conf_file.data = ngx_conf_file;

    } else {
        //设置cycle->conf_file 为 conf/nginx.conf
        ngx_str_set(&cycle->conf_file, NGX_CONF_PATH);
    }

    //检查配置文件是否正确 同时把cycle->conf_file组装为/usr/local/nginx/conf/nginx.conf
    if (ngx_conf_full_name(cycle, &cycle->conf_file, 0) != NGX_OK) {
        return NGX_ERROR;
    }

    //将cycle->conf_prefix从/nginx设置成/usr/local/nginx/conf/nginx.conf cycle->conf_prefix.len为strlen(/usr/local/nginx/conf) +
    for (p = cycle->conf_file.data + cycle->conf_file.len - 1;
         p > cycle->conf_file.data;
         p--)
    {
        if (ngx_path_separator(*p)) {
            cycle->conf_prefix.len = p - cycle->conf_file.data + 1;
            cycle->conf_prefix.data = cycle->conf_file.data;
            break;
        }
    }
 
    if (ngx_conf_params) {
        cycle->conf_param.len = ngx_strlen(ngx_conf_params);
        cycle->conf_param.data = ngx_conf_params;
    }

    if (ngx_test_config) {
        cycle->log->log_level = NGX_LOG_INFO;
    }

    return NGX_OK;
}

ngx_os_init

从名称可以看出来,是初始化nginx的系统配合,因为内容比较多,所以我们单独开一篇文章。
详见 nginx流程分析之系统初始化

ngx_crc32_table_init

这个是初始化nginx的 crc32_table,我们先看一下代码:

//ngx_crc32_table_short 是ngx_crc32_table16的首地址
uint32_t *ngx_crc32_table_short = ngx_crc32_table16;


ngx_int_t
ngx_crc32_table_init(void)
{
    void  *p;

    //判断 ngx_crc32_table_short的地址是否是 按照ngx_cacheline_size的对齐的 详见
    if (((uintptr_t) ngx_crc32_table_short & ~((uintptr_t) ngx_cacheline_size - 1))
        == (uintptr_t) ngx_crc32_table_short)
    {
        return NGX_OK;
    }

    //分配 16*4 + 32/64 的内存 调用 malloc
    p = ngx_alloc(16 * sizeof(uint32_t) + ngx_cacheline_size, ngx_cycle->log);
    if (p == NULL) {
        return NGX_ERROR;
    }

    //地址按照ngx_cacheline_size 进行对齐
    p = ngx_align_ptr(p, ngx_cacheline_size);

    //把ngx_crc32_table16 赋值给p
    ngx_memcpy(p, ngx_crc32_table16, 16 * sizeof(uint32_t));

    //再把 p的地址赋值给ngx_crc32_table_short 
    ngx_crc32_table_short = p;
    
    //综上所述把ngx_crc32_table_short的地址变成了按照32/64 cpu对齐,这样cpu一次即可取出

    return NGX_OK;
}

这个里面比较令人头疼的就是各种位操作 进行内存对齐的判断和操作,这个里面 【nginx流程分析只内存对齐】

ngx_slab_sizes_init

初始化一些变量,见注释。当然这个在linux 平台,不同的平台getPageSize的值可能会不同,道理是一样的

void
ngx_slab_sizes_init(void)
{
    ngx_uint_t  n;

    //ngx_pagesize 4096
    //ngx_slab_max_size 2048
    ngx_slab_max_size = ngx_pagesize / 2;

    //ngx_slab_exact_size 64
    ngx_slab_exact_size = ngx_pagesize / (8 * sizeof(uintptr_t));

    for (n = ngx_slab_exact_size; n >>= 1; ngx_slab_exact_shift++) {
        /* void */
    }

     //ngx_slab_exact_shift 6

}

ngx_add_inherited_sockets

从名称上可以看出来,是继承已经存在的sockets。然后是从NGINX这个全局变量中获取,当然因为这个功能很少用,这里我们不多说了,后面有用到再补上吧。

ngx_preinit_modules

初始化模块的一些信息

ngx_int_t
ngx_preinit_modules(void)
{
    ngx_uint_t  i;

    //给ngx_modules的index和name赋值
    for (i = 0; ngx_modules[i]; i++) {
        ngx_modules[i]->index = i;
        ngx_modules[i]->name = ngx_module_names[i];
    }

    //ngx_modules_n 是总共的模块数量 这里为50
    ngx_modules_n = i;

    //ngx_max_module 当前nginx的模块数量加上128 等于 178
    ngx_max_module = ngx_modules_n + NGX_MAX_DYNAMIC_MODULES;

    return NGX_OK;
}

ngx_init_cycle

初始化nginx的cycle,详见 nginx流程分析只cycle初始化

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值