October 16th Friday

  The following is the analysis of the source of the nginx.   Let's go on!

 

三、Nginx启动处理

1.  main()函数开始,进行了一系列的初始化处理工作。下面将分别介绍,对于不是很重要或是很好理解力的部分可能不作详细说明。

a)       首先是从命令行获取参数,打印参数的用法说明。

b)       ngx_time_init()函数,获取当前系统的日期和时间。Nginx中定义了三个全局ngx_str_t变量,ngx_cached_err_log_timengx_cached_http_timengx_cached_http_log_time分别用来表示error.log中的时间格式,http协议中的时间格式,和accesse.log中的时间格式。另外,nginx中还分别定义了ngx_str_t 类型cached_err_log_time[NGX_TIME_SLOTS]cached_http_time[NGX_TIME_SLOTS]cached_http_log_time[NGX_TIME_SLOTS]用来缓存系统的时间。还有static ngx_time_t cached_time[NGX_TIME_SLOTS]

这些数组的长度为64。这此缓存数极组有什么用处,还不知道。

在函数最后,调用ngx_time_update()

c)       ngx_time_update()函数,通过ngx_gettimeofday()取得当前的毫秒数,其后逻辑比较简单,只是根据获取的毫秒数通过一些系统调用获取年,月,日,时,分,秒以及按不同时区进行的转换。

d)       注意,在ngx_time_update()函数后部使用了一个ngx_memory_barrier()。这是个宏。对不同的编译器不同的环境有不同的定义。关于x86gcc的定义如下:

/*

 * on x86 the write operations go in a program order, so we need only

 * to disable the gcc reorder optimizations

 */

 

#define ngx_memory_barrier()    __asm__ volatile ("" ::: "memory")

2.  ngx_getpid()获取nginxpid

3.  如果宏NGX_OPENSSL定义了,在ngx_ssl_init()函数中载入open ssl的库。

4.  随后在全局的ngx_cycle变量中,创建内存池,大小为1024

5.  ngx_save_argv()函数则是将从main()函数传入的参数表保存到nginx的全局变量ngx_argcngx_argv[]ngx_os_argv[]ngx_os_environ中。

6.  ngx_process_options()函数中取得当前程序所在的工作目录,并且创建相应的config文件。获取config文件的全路径。

7.  ngx_crc32_table_init()函数中初始化用于crc32循环冗余校验的数据表。

8.  ngx_add_inherited_sockets()函数中,继承父进程中的socket。继承方法是通过取得”NGINX”这个环境变量的值,该值是个由”:””;”作为分隔符的列表,列表是表示socket的文件描述符。Nginx将继承的socket压入一个堆栈中,然后置变量ngx_inherited1,表示已经取得要继承的socket

 

struct ngx_listening_s {

    ngx_socket_t    fd;                       /* socket所用的文件描述符 */

 

    struct sockaddr  *sockaddr;        /* 指向sockaddr地址 */

    socklen_t       socklen;        /* size of sockaddr */

    size_t          addr_text_max_len;  /* 地址的文本表示的最大长度 */

    ngx_str_t           addr_text;  /* 地址的文本表示 */

 

    int                 type;            /*  */

 

    int                 backlog;      /* listen()中使用的参数,默认511 */

    int                 rcvbuf;    /* 接受缓冲区的大小 */

    int                 sndbuf;    /* 发送缓冲区的大小 */

 

    /* handler of accepted connection */

    ngx_connection_handler_pt   handler;

 

    void               *servers;/* array of ngx_http_in_addr_t, for example */

 

    ngx_log_t           log;

    ngx_log_t          *logp;

 

    size_t              pool_size;

    /* should be here because of the AcceptEx() preread */

    size_t              post_accept_buffer_size;

    /* should be here because of the deferred accept */

    ngx_msec_t          post_accept_timeout;

 

    ngx_listening_t    *previous;

    ngx_connection_t   *connection;

 

    unsigned            open:1;    /* socket已经打开 */

    unsigned            remain:1;  /*  */

    unsigned            ignore:1;   /*  */

 

    unsigned            bound:1;    /* already bound */

    unsigned            inherited:1;  /* inherited from previous process */

    unsigned            nonblocking_accept:1;

    unsigned            listen:1;

    unsigned            nonblocking:1;

    unsigned            shared:1;  /* shared between threads or processes */

    unsigned            addr_ntop:1;

 

#if (NGX_HAVE_INET6 && defined IPV6_V6ONLY)

    unsigned            ipv6only:2;

#endif

 

#if (NGX_HAVE_DEFERRED_ACCEPT)

    unsigned            deferred_accept:1;

    unsigned            delete_deferred:1;

    unsigned            add_deferred:1;

#ifdef SO_ACCEPTFILTER

    char               *accept_filter;

#endif

#endif

 

};

 

随后,ngx_open_listening_sockets()函数,对本进程所监视的socket遍历。对于ignore标志为1的或fd-1以及继承来的socket,不作处理跳过。

除此之外的其他listening数组中的元素,认为是空的。

于是调用ngx_socket()(socket())创建一个新的socket;接着立即设置socket为重用(SO_REUSEADDR项设为1)

如果所在操作系统中不支持aio,则将ngx_nonblocking()socket设为非阻塞方式;随后绑定地址,置于监听状态。

 

注:如果是IPv6地址,则按IPv6的方式处理。

9.  计算nginx的模块数,同时设置每个模块的index值。

10.              如果执行nginx的命令行参数中设置了signal值,则进行ngx_signal_process()处理。

ngx_signal_process()函数先取得自己的pid(可能是从/proc目录下nginxpid文件中读取的pid值。仅是猜测,目前还不清楚是不是这样。)

最后调用ngx_os_signal_process() 函数,来向pid代表的进程发送信号。

发送的信号都定义在一个全局信号表中:

ngx_signal_t  signals[] = {

    { ngx_signal_value(NGX_RECONFIGURE_SIGNAL),

      "SIG" ngx_value(NGX_RECONFIGURE_SIGNAL),

      "reload",

      ngx_signal_handler },

 

    { ngx_signal_value(NGX_REOPEN_SIGNAL),

      "SIG" ngx_value(NGX_REOPEN_SIGNAL),

      "reopen",

      ngx_signal_handler },

 

    { ngx_signal_value(NGX_NOACCEPT_SIGNAL),

      "SIG" ngx_value(NGX_NOACCEPT_SIGNAL),

      "",

      ngx_signal_handler },

 

    { ngx_signal_value(NGX_TERMINATE_SIGNAL),

      "SIG" ngx_value(NGX_TERMINATE_SIGNAL),

      "stop",

      ngx_signal_handler },

 

    { ngx_signal_value(NGX_SHUTDOWN_SIGNAL),

      "SIG" ngx_value(NGX_SHUTDOWN_SIGNAL),

      "quit",

      ngx_signal_handler },

 

    { ngx_signal_value(NGX_CHANGEBIN_SIGNAL),

      "SIG" ngx_value(NGX_CHANGEBIN_SIGNAL),

      "",

      ngx_signal_handler },

 

    { SIGALRM, "SIGALRM", "", ngx_signal_handler },

 

    { SIGINT, "SIGINT", "", ngx_signal_handler },

 

    { SIGIO, "SIGIO", "", ngx_signal_handler },

 

    { SIGCHLD, "SIGCHLD", "", ngx_signal_handler },

 

    { SIGSYS, "SIGSYS, SIG_IGN", "", SIG_IGN },

 

    { SIGPIPE, "SIGPIPE, SIG_IGN", "", SIG_IGN },

 

    { 0, NULL, "", NULL }

};

 

其中元素的结构如下:

typedef struct {

     int     signo;

     char   *signame;

     char   *name;

     void  (*handler)(int signo);

} ngx_signal_t;

 

11.              ngx_os_status()函数则是对应不同的操作系统,将不操作系统的内核版本,发布版本等写出到log文件中。不同系统输出内容不一样。

12.              随后ngx_init_signals()函数中,针对singals表中的信号安装信号处理函数。

Nginx中的信号处理函数是ngx_signal_handler()

ngx_signal_handler()函数中,会检查收到的信号是不是nginx有效的信号,取得当前系统的时间。

接着根据不同的进程模式——NGX_PROCESS_MASTERNGX_PROCESS_SINGLENGX_PROCESS_WORKER,对应不同信号进行相应处理。

NGX_PROCESS_MASTERNGX_PROCESS_SINGLE模式下信号处理方式一致。其中NGX_CHANGEBIN_SIGNAL信号需要特别在意。

case ngx_signal_value(NGX_CHANGEBIN_SIGNAL):

            if (getppid() > 1 || ngx_new_binary > 0) {

 

                /*

                 * Ignore the signal in the new binary if its parent is

                 * not the init process, i.e. the old binary's process

                 * is still running. Or ignore the signal in the old binary's

                 * process if the new binary's process is already running.

                 */

 

                action = ", ignoring";

                ignore = 1;

                break;

            }

 

            ngx_change_binary = 1;

            action = ", changing binary";

            break;

  

   收到这个信号表示要进程切换,ngx_change_binary置为1。如果收到该信号的进程是下列两种情况,就忽略信号。

a)       新启动的进程,其父进程不是init

b)       老的进程(即将终了的进程),其派生的子进程已经在运行了。

NGX_CHANGEBIN_SIGNAL信号用来restart起动nginxNginx是采用一种平滑进程切换的方式来重启。这种方式保证了不间断地处理请求。上面两种情况其实是指由将要退出的老进程生成新的进程。新生的进程对NGX_CHANGEBIN_SIGNAL信号不作处理。

13.              如果配置文件中设置了daemon运行模式且ngx_inherited0

调用ngx_daemon()将进程置为daemon运行模式。ngx_daemon()函数使用fork()setsid()系统调用将进程变成后台进程。

14.              ngx_create_pidfile()函数将pid写入文件。

15.              将标准错误输出重定向到当前打开的log文件。

16.              根据进程模式是NGX_PROCESS_SINGLE还是NGX_PROCESS_MASTER分别调用ngx_single_process_cycle()ngx_master_process_cycle()

四、List

1.       结构

 

struct ngx_list_part_s {

    void           *elts;  /* list中元素 */

    ngx_uint_t      nelts;  /* 使用的元素的个数 */

    ngx_list_part_t  *next;  /* 指向下一个list part结点 */

};

 

 

typedef struct {

    ngx_list_part_t  *last;    /* 指向最后一个list part结点 */

    ngx_list_part_t  part;    /* list的头结点中list part部份 */

    size_t          size;    /* 元素大小 */

    ngx_uint_t      nalloc;  /* list part结点中申请的元素的容量大小 */

    ngx_pool_t     *pool;   /* 指向内存池 */

} ngx_list_t;

 

2.       相关函数

ngx_list_init() 函数创建一个新的list对象设置ngx_list_t指针,内存池中申请的头结点大小为n * size

            ngx_list_create() 函数中没有指定list指针,创建一个新的list对象,头结点大小为n*size

ngx_list_push() 函数,将list当作堆栈将元素压栈,如果当前的list结点中没有空间,则在内存池中申请一个新的list结点,且作为堆栈的顶部。

 

 

  The next I have two choices, one is to analyse the cycle object in nginx, another is to parse the source the events mechanism in nginx.  They are both important points in the comprehension of nginx.

你可能感兴趣的:(nginx,struct,socket,list,Signal,Sockets)