nginx中的main函数
1.简介:
这篇文章肯定是未完待续的...因为作者本身也是刚开始看nginx,只能逐步更新了...如果有错误的地方还望诸君海涵并指出。讲解的内容主要是按顺序在main函数中调用的其他函数或者宏的作用,其中定义的一些变量不会赘述,但会提到...另外每次介绍函数我都会在代码段中添加注释,主要是这个函数所在的文件什么的,还有就是我会先介绍该函数大致干了啥,至于函数中执行的过程因为有些太长了,建议最好自己去看一下,错误处理方面也会偶尔提到另外我的系统是ubuntu19.04,之所以在这里特别说明,是因为nginx在不同平台上,对某些路径字符串什么的处理有所不同,因此本文仅按照我个人的系统上的介绍,但是如果遇到这种情况我会特别说明。
2.主要内容:
ngx_debug_init();
这句宏应该是用作debug的...不知道有没有啥其他作用,但是因为nginx里面名字特别清楚,所以暂时认定这个是用作debug,另外后续可能存在一些我查找不到的宏定义我直接按照nginx名字含义来解释...如果有新的了解再进行更新。
if (ngx_strerror_init() != NGX_OK) {//<ngx_errno.c> return 1; }
该函数主要是初始化静态指针ngx_sys_errlist,将其作为一个数组首地址利用malloc申请大小为NGX_SYS_NERR * sizeof(ngx_str_t)的内存,存储错误列表,nginx检测的错误都在这了。
(实际上还不就是内核给的errno嘛然后转换成字符串嘛,搞得巨麻烦)
if (ngx_get_options(argc, argv) != NGX_OK) {//<nginx.c> return 1; }
该函数主要是在将nginx启动的时候传递的那些参数选项存储在nginx.c文件中的全局变量中,以方便之后cycle对象的初始化。(这个没得吐槽的,因为咱还不了解cycle是干啥用的--初步估计是一个全局的配置结构,稀奇古怪的东西巨多)
上图中的参数后为如果指明了该参数,进行设置的一些静态变量,这里-s我有一个参数没列出来即ngx_process,如果得到的ngx_sinal在["stop","quit","reopen","reload"]中,那么ngx_process将被设置为NGX_PROCESS_SIGNALLER,否则将会记录一个错误,也就是说给定的ngx_sinal只能是此列表中任何一个。
if (ngx_show_version) { ngx_show_version_info();//<nginx.c> if (!ngx_test_config) { return 0; } }
根据ngx_get_options()所得的ngx_show_version展示当前nginx版本信息,如果设置了ngx_show_help还会输出一系列的参数选项,要注意的是输出传输的文件描述符是STDERR_FILENO。
ngx_time_init();//<ngx_times.c>
根据当前时间初始化所有需要初始的时间,需要注意的是时间格式存在不同。
log = ngx_log_init(ngx_prefix);//<ngx_log.c>
先说明下这个返回值,如果初始化log成功,则返回的实际上是ngx_log.c文件中的static ngx_log_t ngx_log的地址。另外说明下ngx_refix的作用,它是由ngx_get_options()所得到的,当NGX_ERROR_LOG_PATH定义了(实际上在我的vscode中找不到这个宏定义),当这个宏定义的字符串长度为0时(注意是长度不是所占的内存大小),此函数返回的ngx_log中的字段ngx_log_file的文件描述符就是STDERR_FILENO,否则如果长度不为0,并且这个宏定义对应的路径并不是一个绝对路径(即首字符不为'/'),那么我们就需要利用上面所提到的ngx_prefix参数了,nginx的处理方式是将ngx_prefix作为一个路径的前缀,并将NGX_ERROR_LOG_PATH加入到ngx_prefix之后,例如:假设ngx_prefix指向的是"/home/zhuiyi",NGX_ERROR_LOG_PATH宏为"error/errlog.txt",那么这个路径实际上会被拼接为"/home/zhuiyi/error/errlog.txt",以O_WRONLY|O_APPEND|O_CREAT模式打开,权限为0644,,然后把上述的ngx_log_file文件描述符赋值为open函数返回的fd。另外这个返回值实际上后来init_cycle->log和它的指向相同。
init_cycle.pool = ngx_create_pool(1024, log);
在nginx官网中的Development guide文档中有介绍过这个init_cycle,它实际上就是在初始化过程中起到了初步的一些作用,例如利用上面的pool来分配内存,初始化后续cycle的配置文件的前缀字符串(以此为一个所谓的base目录)。如前述提到的,ngx_create_pool()函数是用于create一个内存分配池的,init_cycle将会使用这个内存分配池,对后续的一些需要分配内存的地方进行分配,其size即为1024。
if (ngx_save_argv(&init_cycle, argc, argv) != NGX_OK) { return 1; }
这个函数作用很明显即复制main函数中的参数(利用ngx_argc,ngx_argv),并且它会利用ngx_os_environ复制当前进程环境表指针environ(如果不了解可以去参看APUE或者一些博客书籍了解)另外就如同上面提到的init_cycle,这时候它的作用就体现出来了,利用之前初始化的init_cycle->log,作为内存分配时的错误记录日志,这也就是说,我们不需要ngx_log_stderr()函数将错误写入标准错误。
if (ngx_process_options(&init_cycle) != NGX_OK) { return 1; }
还是处理上面的参数,包括ngx_prefix,ngx_conf_file,ngx_conf_params,主要说下ngx_conf_file,如果没设置该参数则将cycle->conf_file设置为NGX_CONF_PATH,否则直接设置为参数值。而后将会测试cycle->conf_file是否是一个绝对路径,和上面的思路相同,如果不是,则将cycle->conf_prefix作为前缀串(base目录),紧接着根据cycle->conf_file重新设置cycle->conf_prefix(因为当前目录可能改变了,如果想不明白可以看上面的例子)
if (ngx_os_init(log) != NGX_OK) { return 1;
}
该函数用于得到在当前操作系统下的页大小ngx_pagesize和最大文件描述符ngx_max_sockets,并且检测了当前所使用的CPU并获得L2高速cache的行大小,以此设置了所谓的ngx_cacheline_size的值(32,64,128)。
if (ngx_crc32_table_init() != NGX_OK) { return 1; }
该函数判断当前的ngx_crc32_table_short(这是一个指针指向的是ngx_crc32_c文件中的现有静态表)是否已是根据ngx_cacheline_size内存对齐的,如果不是则重新分配一个循环冗余检验表的内存,并根据上面得到的ngx_cacheline_size进行内存对齐。为什么要这么做呢?我们知道CPU会将那些经常使用的数据放入到cache中,一旦数据命中,比从主存中取的速度快太多,因此如果我们经常使用某些数据,可以让它们根据cache块的大小进行内存对齐,这样使得我们可以将这些数据放入高速cache中,使得它们的命中率更高,访问速度更快。
ngx_slab_sizes_init();
有意思的是,上面所有函数都做了if检查,但是这个函数没有,实际上这个函数仅仅完成了对ngx_slab_max_size,ngx_slab_exact_size的初始化,它们是根据ngx_ox_init()函数中获得的ngx_pagesize(我的系统ubuntu19.04上是4096)来计算的。slab实际上是一种在linux上的内存分配机制,它主要是针对一些经常进行分配和释放的对象,这些对象一般占据的内存很小,如果我们经常释放这些对象很容易就会造成内存碎片(也就是说有些释放的连续内存很小以至于无法进行重复使用),因此如果我们使用一个slab分配器,将其size设定为一个特定的大小,专门对这些小型的对象进行内存方面的管理,那么就会防止出现上述的内存碎片现象了。(slab的介绍有点简单了建议查阅其他资料。。其实我有点好奇,如果这个slab只是在linux使用那么是否nginx做了对其他系统的检测啥的...因为我暂时没看到有..)
if (ngx_add_inherited_sockets(&init_cycle) != NGX_OK) { return 1; }
这个函数用于继承那些环境变量名为NGINX_VAR的socket fd,并向cycle->listening动态数组添加这些fd,并且根据fd的值,重新设置ngx_listening_t中的字段。
if (ngx_preinit_modules() != NGX_OK) { return 1; }
这个函数往后就是重头戏了...这个函数主要是初始化了一个ngx_modules列表的name和index,name由ngx_module_names列表得到,实际上最后利用的是cycle中的modules指针存放了这个模块列表的首地址。
cycle = ngx_init_cycle(&init_cycle); if (cycle == NULL) { if (ngx_test_config) { ngx_log_stderr(0, "configuration file %s test failed", init_cycle.conf_file.data); } return 1; }
ngx_init_cycle函数实际上就是整个nginx的核心,它对所有需要用到的模块的配置进行了初始化,当然也包括一些其他的重要变量,包括监听列表,连接列表等。