从 mian 函数开始一步一步分析 nginx(一)

如不做特殊说明,本博客所使用的 nginx 源码版本是 1.0.14

我们先贴出 main 函数的部分代码:

  

 205 int ngx_cdecl
 206 main(int argc, char *const *argv)
 207 {
 208     ngx_int_t         i;
 209     ngx_log_t        *log;
 210     ngx_cycle_t      *cycle, init_cycle;
 211     ngx_core_conf_t  *ccf;
 212
 213 #if (NGX_FREEBSD)
 214     ngx_debug_init();
 215 #endif
 216     /* 错误信息字符串初始化 */
 217     if (ngx_strerror_init() != NGX_OK) {
 218         return 1;
 219     }
 220
 221     /* 解析命令参数 */
 222     if (ngx_get_options(argc, argv) != NGX_OK) {
 223         return 1;
 224     }

  main 函数很简单全是函数的调用,所有功能都放在各个函数中实现!

  1.我们先看 217 行的第一个函数 ngx_strerror_init()[os/unix/ngx_error.c],该函数比较简单,是将系统的每个错误信息保存到 nginx 自身的变量 ngx_sys_errlist 中,以便后面调用。其实 nginx 中很多变量都是这样,将系统提供的变量重新定义成 nginx 的名字。如果 ngx_strerror_init() 函数返回成功,main 函数继续,错误 nginx 终止运行。

  2. 接下来第二个函数 ngx_get_options()[core/nginx.c],这个函数有两个参数,argc,argv:一个是系统调用 main函数传过来参数的个数,另一个就是参数值数组.该函数也比叫容易理解。我们知道在运行 Nginx 的时候可以附带一些命令,形式如 nginx -v 这就是在运行 ngxin 后,并显示 Nginx 版本号的命令。该函数就是对 nginx 命令之后的选项(这里的 -v) 进行解析,然后设置一些全局变量的值。 main 函数在根据这些全局变量的值做出相应的动作。

  接着看 main() 函数代码:

 226      /* 显示 nginx 的版本*/
 227     if (ngx_show_version) {
 228         ngx_write_stderr("nginx version: " NGINX_VER NGX_LINEFEED);
 229         /* 如果需要显示帮助 */
 230         if (ngx_show_help) {
 231             ngx_write_stderr(
 232                 "Usage: nginx [-?hvVtq] [-s signal] [-c filename] "
 233                              "[-p prefix] [-g directives]" NGX_LINEFEED
 234                              NGX_LINEFEED
 235                 "Options:" NGX_LINEFEED
 236                 "  -?,-h         : this help" NGX_LINEFEED
 237                 "  -v            : show version and exit" NGX_LINEFEED
 238                 "  -V            : show version and configure options then      exit"
 239                                    NGX_LINEFEED
 240                 "  -t            : test configuration and exit" NGX_LINEFEE     D
 241                 "  -q            : suppress non-error messages "
242                                    "during configuration testing" NGX_LINEF     EED
 243                 "  -s signal     : send signal to a master process: "
 244                                    "stop, quit, reopen, reload" NGX_LINEFEE     D
 245 #ifdef NGX_PREFIX
 246                 "  -p prefix     : set prefix path (default: "
 247                                    NGX_PREFIX ")" NGX_LINEFEED
 248 #else
 249                 "  -p prefix     : set prefix path (default: NONE)" NGX_LIN     EFEED
 250 #endif
 251                 "  -c filename   : set configuration file (default: "
 252                                    NGX_CONF_PATH ")" NGX_LINEFEED
 253                 "  -g directives : set global directives out of configurati     on "
 254                                    "file" NGX_LINEFEED NGX_LINEFEED
 255                 );
 256         }
 257         /* 如果需要显示配置文件 */
 258         if (ngx_show_configure) {
 259             ngx_write_stderr(
 260 #ifdef NGX_COMPILER
 261                 "built by " NGX_COMPILER NGX_LINEFEED
 262 #endif
 263 #if (NGX_SSL)
 264 #ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
 265                 "TLS SNI support enabled" NGX_LINEFEED
 266 #else
 267                 "TLS SNI support disabled" NGX_LINEFEED
 268 #endif
 269 #endif
 270                 "configure arguments:" NGX_CONFIGURE NGX_LINEFEED);
 271         }
 272
 273         if (!ngx_test_config) {
 274             return 0;
 275         }
 276     }

  3. 这段代码根据一些全局变量进行相应的操作,里的 ngx_show_version、ngx_show_help、ngx_show_configure、ngx_test_config 都是全局变量,其值在 ngx_get_options() 得到设置。

    a. ngx_show_version 显示版本号

    b. ngx_show_help 显示帮助

    c. ngx_show_configure 显示配置文件

    d. ngx_test_config 只是为了测试配置,如果设置了,nginx 程序到此为止

 278     /* TODO */ ngx_max_sockets = -1;
 279
 280     /* 初始化并更新时间,如全局变量 ngx_cached_time,ngx_time_init() 函数定义在 ngx_time.h.c 文件中 */
 281     ngx_time_init();
 282
 283 #if (NGX_PCRE)
 284     /* 如果应用 pcre 库,正则表达式初始化 */
 285     ngx_regex_init();
 286 #endif
 287
 288     /* 获取进程 ID */
 289     ngx_pid = ngx_getpid();
 290     /* 日志初始化  ,如:初始化全局变量 ngx_prefix, 打开日志文件 ngx_log_file.fd */
 291     log = ngx_log_init(ngx_prefix);
 292     if (log == NULL) {
 293         return 1;
 294     }
 295
 296     /* STUB */
 297 #if (NGX_OPENSSL)
 298     ngx_ssl_init(log);
 299 #endif

  4. 281 行的 ngx_time_init() [core/nginx.h/nginx.c] 函数也很简单,将当前的时间保存到全局变量中。

  5. 285 行的 ngx_regex_init() 函数要有 prce 库的支持才会被调用,它的作用是初始化正则表达式,方便以后的匹配操作

  6. 291 行的 ngx_log_init()[core/ngx_log.h/ngx_log.c]  是初始化错误日志记录文件(你安装 nginx 文件夹下的 logs 文件中的 error.log 文件)。该函数所做的操作有打开错误日志记录文件,设置日志等级等等,并将文件描述符保存到结构体中。这里我们要关心的两个结构体有:

    a.struct ngx_log_s 他的定义如下:  

struct ngx_log_s {
    ngx_uint_t           log_level;        /* 日志等级   */
    ngx_open_file_t     *file;            /* 打开的文件(这也是个结构体) */

    ngx_atomic_uint_t    connection;    

    ngx_log_handler_pt   handler;        /* 回调函数   */
    void                *data;            /* 数据指针   */

    /*
     * we declare "action" as "char *" because the actions are usually
     * the static strings and in the "u_char *" case we have to override
     * their types all the time
     */
    char                *action;
};

  其中的日志等级有

* 日志标注错误 */
#define NGX_LOG_STDERR 0
/* 紧急 */
#define NGX_LOG_EMERG 1
/* 警告 */
#define NGX_LOG_ALERT 2
/* 中断 */
#define NGX_LOG_CRIT 3
/* 错误 */
#define NGX_LOG_ERR 4
/* 提醒 */
#define NGX_LOG_WARN 5
/* 通知 */
#define NGX_LOG_NOTICE 6
/* 信息 */
#define NGX_LOG_INFO 7
/* 调试 */
#define NGX_LOG_DEBUG 8

八个等级

b.而结构体变量 file 又是一个结构体,该结构体代码如下:

truct ngx_open_file_s {
    ngx_fd_t              fd;
    ngx_str_t             name;

    u_char               *buffer;
    u_char               *pos;
    u_char               *last;

#if 0
    /* e.g. append mode, error_log */
    ngx_uint_t            flags;
    /* e.g. reopen db file */
    ngx_uint_t          (*handler)(void *data, ngx_open_file_t *file);
    void                 *data;
#endif
};

      从该结构体我的定义我们可以看出,这是一个打开文件并将文件描述符保存到fd,同时将文件的内容缓冲到 *buf 的一个结构体

  好了,日志的初始化完成了,我们接着往下看 main 函数代码:

301     /*
 302      * init_cycle->log is required for signal handlers and
 303      * ngx_process_options()
 304      */
 305
 306     /*
 307      * 信号处理和 ngx_process_options() 函数会用到 init_cycle->log
 308      *
 309      */
 310     /* 清零 init_cycle */
 311     ngx_memzero(&init_cycle, sizeof(ngx_cycle_t));
 312
 313     /* 挂接 log 到 init_cycle */
 314     init_cycle.log = log;
 315
 316     /* ngx_cycle 应该是个 ngx_cycle_t * 类型,此文件中未看到定义 */
 317     ngx_cycle = &init_cycle;
 318
 319     /* 为u init_cycle 创建内存池,大小为 1024B  */
 320     init_cycle.pool = ngx_create_pool(1024, log);
 321     if (init_cycle.pool == NULL) {
 322         return 1;
 323     }
 324
 325     /* 保存命令行参数至全局变量 ngx_os_argv、ngx_argc、ngx_argv 中   */
 326     if (ngx_save_argv(&init_cycle, argc, argv) != NGX_OK) {
 327         return 1;
 328     }
 329
 330     /* 初始化 ngx_cycle 的 prefix,conf_prefie,conf_file,ngx_argv 中 */
 331     if (ngx_process_options(&init_cycle) != NGX_OK) {
 332         return 1;
 333     }
 334
 335     /* 初始化系统相关变量,如页面内存大小 ngx_pagesize,ngx_cacheline_size,最大连接数 ngx_max_sockets */
 336   if (ngx_os_init(log) != NGX_OK) {  /* 日志参数是为了记录 */
 337         return 1;
 338     }

  7. init_cycle 是一个很重要的变量,他的结构体是 ngc_cycle_t. nginx 的所有操作都是围绕这个变量进行的。下面我们来看看对他的操作,第 311 行,对 init_cycle的 内存进行清零

  8. 将全局变量 ngx_cycle 绑定到 init_cycle。320 行设置 init_cycle 的内存池大小为 1024B,调用 ngx_create_pool() 函数进行创建。nginx 的内存池是 nginx 作者自行设计的方便内存操作的结构体,网上有大牛分析的很透彻(http://blog.csdn.net/livelylittlefish/article/details/6586946),我这里不在讲解。你只要大致的知道,这是 nginx 是向内存申请一块较大的内存,以后的内存申请操作都不再向内存直接申请,而是在该分配的内存中直接获取进行了(除非该大块内存也被分配完).如果创建内存池失败则终止程序。

  9. 326 行的 ngx_save_argv()[core/nginx.c] 很简单,就是将 argv 中保存的参数保存到全局变量 ngx_argv 中。两个参数分别是 init_cycle, argv。argv 是要被保存的,而在函数中的操作要用到 init_cycle 中的日志变量

  10.  331 行中的函数所做的事情就是设置 init_cycle 结构体中的 conf_file、conf_param、conf_prefix、prefix 的值。ngx_cycle_t 结构体的定义已经有大牛分析的很详细,这里不再赘述!详见:http://blog.csdn.net/livelylittlefish/article/details/7247080

  11. 336 行的 ngx_os_init 函数设置一些与操作系统有关的变量比如 ngx_pagesize(页面大小)、ngx_cacheline_size(缓存行的大小)、ngx_max_sockets(最大连接数)

继续贴 main 函数代码

/*
 342      * ngx_crc32_table_init() requires ngx_cacheline_size set in ngx_os_init()
 343      */
 344     /* 初始化 CRC 表---后续的 CRC 校验通过查表进行,效率高 */
 345     if (ngx_crc32_table_init() != NGX_OK) {
 346         return 1;
 347     }
 348
 349      /* 调用 ngx_add_inherited_sockets 继承 sockets; --- 将环境变量中的监听端口保存到 init_cycle->listening 数组中去 */
 350     if (ngx_add_inherited_sockets(&init_cycle) != NGX_OK) {
 351         return 1;
 352     }
 353
 354     /* 处理每个模块的 index 变量 ,并计算 ngx_max_moudle */
 355     ngx_max_module = 0;
 356     for (i = 0; ngx_modules[i]; i++) {
 357         ngx_modules[i]->index = ngx_max_module++;
 358     }
 359
 360     /* 初始化 init_cycle 结构 --- 很重要的一个结构体变量,许多东西都保存在这个结构体中*/
 361     /* ngx_init_cycle() 在 ngx_cycle.h/c 文件中 */
 362     cycle = ngx_init_cycle(&init_cycle);
 363     if (cycle == NULL) {
 364         if (ngx_test_config) {
 365             ngx_log_stderr(0, "configuration file %s test failed",
 366                            init_cycle.conf_file.data);
 367         }
 368
 369         return 1;
 370     }
 371     /* 是否开启测试配置文件,开启则测试配置文件 */
 372     if (ngx_test_config) {
 373         if (!ngx_quiet_mode) {
 374             ngx_log_stderr(0, "configuration file %s test is successful",
 375                            cycle->conf_file.data);
 376         }
 377
 378         return 0;
 379     }
 380     /* 若有信号,处理收到的信号 */
 381     if (ngx_signal) {
 382         return ngx_signal_process(cycle, ngx_signal);
 383     }
 384
 385     ngx_os_status(cycle->log);
 386
 387     ngx_cycle = cycle;
 388
 389     /* 读取 ngx_core_moudle 的配置文件,值得关注 */
 390     ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_core_module);
 391
 392     /* 有 master 线程并且 ngx_process 是单*/
 393     if (ccf->master && ngx_process == NGX_PROCESS_SINGLE) {
 394         ngx_process = NGX_PROCESS_MASTER;
 395     }
 396
 397 #if !(NGX_WIN32)
 398     /* 初始化信号 */
 399     if (ngx_init_signals(cycle->log) != NGX_OK) {
 400         return 1;
 401     }
 402     /* 若无继承 sockets,且设置了守护进程标识,则调用 ngx_daemon() 创建守护进程 */
 403      if (!ngx_inherited && ccf->daemon) {
 404         /*
 405         if (ngx_daemon(cycle->log) != NGX_OK) {
 406             return 1;
 407         }
 408         */
 409         ngx_daemonized = 1;
 410     }

  12. 344 行中的 ngx_crc32_table_init() 函数是初始化 CRC 标,后续的 CRC 校验直接通过查表进行,效率高!

  13. 350 行中的 ngx_add_inherited_sockets()函数在 http://blog.csdn.net/livelylittlefish/article/details/7277607 中有分析!很详细,这里不再赘述!

  14. 355-357 行是为了设置每个模块结构体变量中的 index 变量,同时统计模块的个数

  15. 362 行调用 ngx_init_cycle() 根据 init_cycle 初始化 cycle 变量,具体分析见博客:http://blog.csdn.net/livelylittlefish/article/details/7247080 !很详细,也很重要,一定要认真看,搞懂,配置文件的解析并保存到全局变量都在此函数中进行,不再赘述!如果初始化失败,并且设置了 ngx_test_cnfig 为 1,则输出错误信息到标准错误,main 函数返回!

  16. 381 行等待信号的来临,如果 master 进程(执行 main 函数的进程) 收到信号,那么该进程要作出相应的处理,由 ngx_singal_process()[core/ngx_cycle.c]函数执行!

  17. 390 行获取 ngx_core_module 模块的配置信息,393-395 行:如果配置中 master 变量 > 1 并且 ngx_process 是 NGX_PROCESS_SINGLE 模式那么就将 ngx_process 设置成 NGX_PROCESS_MASTER

  18. 399 行中的 ngx_init_signals() 函数初始化信号在博客 http://blog.csdn.net/livelylittlefish/article/details/7308100 分析的很详细,这里不再赘述!

  19. 403-410 根据配置中的 daemon 变量和 是否 继承 sockets(ngx_inherited,会在 ngx_add_inherited_sockets() 函数中设置) 来设置 master 进程(执行 main 函数的进程)是否为守护进程!我这里为了方便调试,将设置成守护进程打代码注释了。

继续贴 main() 函数代码

 440     /* 进入主循环 */
 441     /* ngx_single_process() 和 ngx_master_process_cycle() 定义在 OS/ngx_process_cycle.h/c 文件中*/
 442     if (ngx_process == NGX_PROCESS_SINGLE) {
 443         ngx_single_process_cycle(cycle);   /* 若为 NGX_PROCESS_SINGLE = 1 模式,则调用 ngx_single_process_cycle() 进入主循环*/
 444
 445     } else {
 446         ngx_master_process_cycle(cycle);   /* 否则为 master-worker 模式,调用 ngx_master_process_cycle() 进入主循环*/
 447     }
 448
 449     return 0;
 450 }

  20.根据 ngx_process 使进程调用不同的处理函数进入主循环!

至此,main() 函数分析完毕,下一节我们将分析进入 ngx_master_process_cycle() 函数的代码!

时间: 2024-10-30 15:29:10

从 mian 函数开始一步一步分析 nginx(一)的相关文章

一步一步写算法(之函数堆栈显示)

原文:一步一步写算法(之函数堆栈显示) [ 声明:版权所有,欢迎转载,请勿用于商业用途.  联系信箱:feixiaoxing @163.com ] 在继续图的讨论之前,我们今天开个小差,讨论一下函数堆栈的基本原理.有过编程经验的朋友都知道,堆栈调试是我们在程序开发中经常应用的一个功能.那么大家有没有想过,函数堆栈是怎么开始的啊?其实我们可以自己写一个函数堆栈输出函数分析一下. 因为一般来说,函数的压栈过程是这样的: |    参数三  | |    参数二  | |    参数一  | |  

【转】朱兆祺带你一步一步学习嵌入式(连载)

原文网址:http://bbs.elecfans.com/jishu_357014_2_1.html#comment_top  从最初涉及嵌入式Linux开始到现在,深深的知道嵌入式的每一步学习都是举步维艰.从去年11月份开始,我就着手整理各种学习资料,希望推动嵌入式学习的前进贡献自己微不足道的一份力量.从去年到现在,将C语言的学习经验整理成<攻破C语言笔试与机试陷阱及难点>(现在仍在更新),这份资料已经在电子发烧友论坛的单片机论坛连载(http://bbs.elecfans.com/jish

一步一步学习Vue(十一)

本篇继续学习vuex,还是以实例为主:我们以一步一步学Vue(四)中讲述的例子为基础,对其改造,基于vuex重构一遍,这是原始的代码: todolist.js ; (function () { var list = []; var Todo = (function () { var id = 1; return function (title, desc) { this.title = title; this.desc = desc; this.id = id++; } })(); /** *

Rhythmk 一步一步学 JAVA (21) JAVA 多线程

1.JAVA多线程简单示例 1.1 .Thread  集成接口 Runnable 1.2 .线程状态,可以通过  Thread.getState()获取线程状态: New (新创建) Runnable (可以运行) Blocked  (被阻塞) Waiting  (等待) Timed waiting (计时等待) Terminated  (被终止) ? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27

一步一步写jQuery插件

转载自:http://www.cnblogs.com/joey0210/p/3408349.html 前言 如今做web开发,jquery 几乎是必不可少的,就连vs神器在2010版本开始将Jquery 及ui 内置web项目里了.至于使用jquery好处这里就不再赘述了,用过的都知道.今天我们来讨论下jquery的插件机制,jquery有着成千上万的第三方插件,有时我们写好了一个独立的功能,也想将其与jquery结合起来,可以用jquery链式调用,这就要扩展jquery,写成插件形式了,如下

大流量网站性能优化:一步一步打造一个适合自己的BigRender插件(转)

BigRender 当一个网站越来越庞大,加载速度越来越慢的时候,开发者们不得不对其进行优化,谁愿意访问一个需要等待 10 秒,20 秒才能出现的网页呢? 常见的也是相对简单易行的一个优化方案是 图片的延迟加载.一个庞大的页面,有时我们并不会滚动去看下面的内容,这样就浪费了非首屏部分的渲染,而这些无用的渲染,不仅包括图片,还包括其他的 DOM 元素,甚至一些 js/css(某些js/css 是根据模块请求的,比如一些 ajax),理论上,每增加一个 DOM,都会增加渲染的时间.有没有办法能使得

一步一步教你用Swift开发俄罗斯方块:No.7 下落机制

上一章节我们完成了shape的建立,现在游戏里面的元素(blocks,shapes)都已经完成了,背景也搭好了(array2D),让我们开始制定游戏规则吧.首先就是需要让我们的shape掉下来,还记得我们刚开始的时候每个600毫秒要刷新一下屏幕呢?那会还有一个closure我们都不太明白是干嘛用的,马上就知道了. 好了,今天章节过后,你的程序运行起来应该是这样的: 让我们来修改代码吧,这次要修改的代码比较多,而且没有上一章节那样重复的工作.不用太过担心,我们一步一步来: 在#1, 函数在执行时会

Android 从硬件到应用:一步一步向上爬 4 -- 使用 JNI 方法调硬件驱动

Android下,java应用程序通过JNI方法调用硬件抽象层模块,在Android 从硬件到应用:一步一步向上爬 3 -- 硬件抽象层访问硬件驱动 中我们已经编译好了硬件抽象层模块,下面就要开始为HAL层编写JNI方法,为上层提供服务. cd到frameworks/base/services/jni目录中,新建一个com_android_server_GpioService.cpp文件: #include "jni.h" #include "JNIHelp.h"

一步一步学会puppet(二)--模块和类

上一篇主要介绍了puppet的基本工作原理和资源的相关初步使用: 这一篇主要介绍puppet中很重要的2个概念:模块和类: =================================================================== 1 模块 1.1 需求 1.2 定义 1.3 详细说明 1.4 模块的目录组织结构 1.5 模块管理 2 类 2.1 定义 2.2 详细说明 2.3 类的分类 2.4 类的声明 2.5 实例 ========================

一步一步学习数据结构(三)栈的顺序存储结构实现代码

//栈这种逻辑结构的实现与一般线性表的实现类似,有两种存储方式:顺序存储和链式存储 //先学习顺序存储 //1. #include<stdio.h> #include<stdlib.h> #define null NULL #define SIZE 100 typedef int dataType ; typedef struct { dataType data[SIZE]; int top; }cStack,*cStackPointer; //初始化栈空间 void initSt