源码分析是一个逐步取精的过程,最开始是一个大概了解的过程,各种认识不会太深刻,但是把这些真实的感受也记录下来,觉得挺有意思的,可能有些认识是片面或者是不正确的,但可以通过后面更深入细致的分析过程,不断的纠正错误和深化理解。源码分析是一个过程,经验是逐步累积起来的,希望文字可以把这种累积的感觉也准确记录下来。



现在就看看对nginx源码的第一印象吧。



源码包解压之后,根目录下有几个子目录和几个文件,最重要的子目录是auto和src,最重要的文件是configure脚本,不同于绝大多数的开源代码,nginx的configure脚本是作者手工编写的,没有使用autoconf之类的工具去自动生成,configure脚本会引用auto目录下面的脚本文件来干活。根据不同的用途,auto目录下面的脚本各司其职,有检查编译器版本的,有检查操作系统版本的,有检查标准库版本的,有检查模块依赖情况的,有关于安装的,有关于初始化的,有关于多线程检查的等等。configure作为一个总驱动,调用这些脚本去生成版本信息头文件、默认被包含的模块的声明代码和Makefile文件,版本信息头文件(ngx_auto_config.h,ngx_auto_headers.h)和默认被包含的模块的声明代码(ngx_modules.c)被放置在新创建的objs目录下。要注意的是,这几个生成的文件和src下面的源代码一样重要,对于理解源码是不可忽略的重要部分。



src是源码存放的目录,configure创建的objs/src目录是用来存放生成的.o文件的,注意区分一下。



src按照功能特性划分为几个部分,对应着是几个不同的子目录。



src/core存放着主干部分、基础数据结构和基础设施的源码,main函数在src/core/nginx.c中,这是分析源码的一个很好的起点。



src/event存放着事件驱动模型和相关模块的源码。



src/http存放着http server和相关模块的源码。



src/mail存放着邮件代理和相关模块的源码。



src/misc存放着C++兼容性测试和google perftools模块的源码。



src/os存放着依赖于操作系统实现的源码,nginx启动过程中最重要的master和workers创建代码就在这个目录下,多少让人觉得有点意外。



nginx的实现中有非常多的结构体,一般命名为ngx_XXX_t,这些结构体分散在许多头文件中,而在src/core/ngx_core.h中把几乎所有的头文件都集合起来,所有的实现文件都会包含这个ngx_core.h头文件,说nginx的各部分源码耦合厉害就是这个原因,但实际上nginx各个部分之间逻辑上是划分的很清晰的,整体上是一种松散的结构。nginx实现了一些精巧的基础数据结构,例如ngx_string_t,ngx_list_t,ngx_array_t,ngx_pool_t,ngx_buf_t,ngx_queue_t,ngx_rbtree_t,ngx_radix_tree_t等等,还有一些重要的基础设施,比如log,configure file,time等等,这些数据结构和基础设施频繁的被使用在许多地方,这会让人感觉nginx逻辑上的联系比较紧密,但熟悉了这些基础数据结构的实现代码就会感觉到这些数据结构都是清晰分明的,并没有真正的耦合在一起,只是有些多而已,不过nginx中“家酿”的代码也正是它的一个很明显的亮点。



nginx是高度模块化的,可以根据自己的需要定制模块,也可以自己根据一定的标准开发需要的模块,已经定制的模块会在objs/ngx_modules.c中声明,这个文件是由configure生成的。



nginx启动过程中,很重要的一步就是加载和初始化模块,这是在ngx_init_cycle中完成的,ngx_init_cycle会调用模块的hook接口(init_module)对模块初始化,ngx_init_cycle还会调用ngx_open_listening_sockets初始化socket,如果是多进程方式启动,就会调用ngx_master_process_cycle完成最后的启动动作,ngx_master_process_cycle调用ngx_start_worker_processes生成多个工作子进程,ngx_start_worker_processes调用ngx_worker_process_cycle创建工作内容,如果进程有多个子线程,这里也会初始化线程和创建线程工作内容,初始化完成之后,ngx_worker_process_cycle会进入处理循环,调用ngx_process_events_and_timers,该函数调用ngx_process_events监听事件,并把事件投递到事件队列ngx_posted_events中,最终会在ngx_event_thread_process_posted中处理事件。



事件机制是nginx中很关键的一个部分,linux下使用了epool,freebsd下使用了kqueue管理事件。