Chinaunix首页 | 论坛 | 博客
  • 博客访问: 108909
  • 博文数量: 60
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 280
  • 用 户 组: 普通用户
  • 注册时间: 2013-09-09 12:19
文章分类

全部博文(60)

文章存档

2015年(3)

2014年(41)

2013年(16)

我的朋友

分类: LINUX

2013-11-26 08:30:07

2 Nginx启动与执行流程 2.1 Nginx启动

“工欲善其事必先利其器”。毛主席说,理论实践相结合,分析研究源代码,也不例外。我们首先搭建一个Nginx的环境,以源码编译安装,然后从配置深入了解配置文件,启动参数,最后结合代码中熟悉的main()函数开始,走向一条合理的分析线路。Nginx的安装在这里就不赘述。

我们进入Nginx安装好的目录,可以看到如下图所示的目录树结构:

说明: C:.Documents and Settings.junyong.Application Data.Tencent.Users.15261619.QQ.WinTemp.RichOle.W8}6Z_9O`C}{3A_J4XI[PTN.jpg

 SEQ \* ARABIC 3 Nginx安装完成后的目录结构树

如上图所示,最重要的是nginx.conf这个文件,里面包含了nginx配置的各项参数。我们在这里先不深入分析其内容,而从启动开始。默认安装后,不需要进行配置也可以按默认项启动。

执行“nginx -?”看看帮助,我们发现Nginx有以下启动选项。

root@debian:/nginx# ./sbin/nginx -?

nginx: nginx version: nginx/1.0.5

nginx: Usage: nginx [-?hvVtq] [-s signal] [-c filename] [-p prefix] [-g directives]

 

Options:

  -?,-h         : this help

  -v            : show version and exit

  -V            : show version and configure options then exit

  -t            : test configuration and exit

  -q            : suppress non-error messages during configuration testing

  -s signal     : send signal to a master process: stop, quit, reopen, reload

  -p prefix     : set prefix path (default: /nginx/)

  -c filename   : set configuration file (default: conf/nginx.conf)

  -g directives : set global directives out of configuration file

 

默认情况下,在nginx安装后的目录执行./sbin/nginx即可启动,默认端口是80,通过浏览器,我们可以实现对nginx web服务器的访问。

2.2 Nginx执行流程

和普通的应用程序一样,Nginx程序的执行入口函数仍旧从main()函数开始。Main()函数位于src/core/nginx.c中。Nginx启动执行主要调用的函数概况如下:

main()

  --ngx_debug_init()

  --ngx_strerror_init()

  --ngx_get_options(argc, argv)

  --ngx_time_init()

  --ngx_regex_init()

  --ngx_log_init()

  --ngx_ssl_init()

  --ngx_memzero()

  --ngx_create_pool()

  --ngx_save_argv()

  --ngx_process_options()

  --ngx_os_init()

  --ngx_crc32_table_init()

  --ngx_add_inherited_sockets()

  --ngx_init_cycle()

  --ngx_signal_process()

  --ngx_os_status()

  --ngx_get_conf()

  --ngx_init_signals()

  --ngx_daemon()

  --ngx_create_pidfile()

  ----ngx_single_process_cycle()

  ----ngx_master_process_cycle()

以上是main函数调用到函数,其中可能包括一些分支才能调用到的函数,也在这里一并顺序罗列。我们现对这个调用流程有一个整体的印象,然后在深入代码,分析每个函数,并逐步分析完这个流程执行过程所做的事情。

下面我们开始正式的源代码情景分析:

198 int ngx_cdecl

 199 main(int argc, char *const *argv)

 200 {

 201     ngx_int_t         i;

 202     ngx_log_t        *log;

 203     ngx_cycle_t      *cycle, init_cycle;

 204     ngx_core_conf_t  *ccf;

 205

 206     ngx_debug_init();

 207

 208     if (ngx_strerror_init() != NGX_OK) {

 209         return 1;

 210     }

 211

 212     if (ngx_get_options(argc, argv) != NGX_OK) {

 213         return 1;

 214     }

以上是main函数启动最开始的执行片段,具体的数据类型我们到函数中进行分析,因此,main执行开始涉及到的变量类型暂不进行分析。第一个函数ngx_debug_init(),这个函数主要是作为初始化debug用的,nginx中的debug,主要是对内存池分配管理方面的debug,因为作为一个应用程序,最容易出现bug的地方也是内存管理这块。在Linux版本中,这个函数只是一个空定义(src/os/unix/ngx_linux_config.h)。

#define ngx_debug_init()

2.2.1 ngx_strerror_init函数分析

我们来到程序的第208行,ngx_strerror_init(),该函数的定义在文件src/os/unix/ngx_errno.c中。该函数主要初始化系统中错误编号对应的含义,这样初始化中进行对应的好处是,当出现错误,不用再去调用strerror()函数来获取错误原因,而直接可以根据错误编号找到对应的错误原因,可以提高运行时的执行效率。从这里可以看到,nginx开发者的别有用心,对于微小的性能提升也毫不放过。

44 ngx_uint_t

 45 ngx_strerror_init(void)

 46 {

 47     char       *msg;

 48     u_char     *p;

 49     size_t      len;

 50     ngx_err_t   err;

 51

 52     /*

 53      * ngx_strerror() is not ready to work at this stage, therefore,

 54      * malloc() is used and possible errors are logged using strerror().

 55      */

 56

 57     len = NGX_SYS_NERR * sizeof(ngx_str_t); 

 58

 59     ngx_sys_errlist = malloc(len);

 60     if (ngx_sys_errlist == NULL) {

 61         goto failed;

 62     }

 63

 64     for (err = 0; err < NGX_SYS_NERR; err++) {

 65         msg = strerror(err);

 66         len = ngx_strlen(msg);

 67

 68         p = malloc(len);

 69         if (p == NULL) {

 70             goto failed;

 71         }

 72

 73         ngx_memcpy(p, msg, len);

 74         ngx_sys_errlist[err].len = len;

 75         ngx_sys_errlist[err].data = p;

 76     }

 77

 78     return NGX_OK;

 79

 80 failed:

 81

 82     err = errno;

 83     ngx_log_stderr(0, "malloc(%uz) failed (%d: %s)", len, err, strerror(err));

 84

 85     return NGX_ERROR;

 86 }

57行中,NGX_SYS_NERR定义在objs/ngx_auto_config.h文件中,特别注意,这是一个auto性质的文件,只有在源码安装nginx时,执行了./configure后,才能生成这个文件。

171 #ifndef NGX_SYS_NERR

172 #define NGX_SYS_NERR  132

173 #endif

Linux系统中有132个错误编码。我们可以写一个简单的小程序来测试系统中的错误编码对应的说明:

#include

#include

#include

 

 

#define ERRO_NUM        132

 

int main()

{

        int i;

        for(i = 0; i < ERRO_NUM; i++)

                printf("%d:%s\n", i, strerror(i));

        return 0;

}

具体执行结果请读者运行查看,在这里不再赘述。

 

同样在第57行中,sizeof(ngx_str_t)这是对nginx自己定义的字符串结构的字节长度计算。我们来分析一下nginx的字符串表示形式。

src/core/ngx_string.h文件中:

15 typedef struct {

 16     size_t      len;

 17     u_char     *data;

 18 } ngx_str_t;

 19

 20

 21 typedef struct {

 22     ngx_str_t   key;

 23     ngx_str_t   value;

 24 } ngx_keyval_t;

Nginx自身对字符串的表示,进行了“长度—内容”这样的方式,字符串在初始化时就进行了长度的计算记录,这样就方便了后续在使用过程中,不必重复计算字符串长度。同样是很细微的提升效率的表现啊!

接着回到对ngx_strerror_init的分析,第59行,malloc为全局的ngx_sys_errlist分配内存,这是一个全局静态变量,指向错误编码及对应字符串说明值数组的起始地址。从6476行,完成了数组中,每一个值对应的err字符串及其长度的初始化工作。这部分也是该函数的核心工作部分。在这里nginx并没有使用其内存池,而是使用率默认的malloc进行内存分配,因为在这里程序还没有创建内存池,而后续的初始化工作,可能出现未知的错误,那么,该处初始化的错误数组就可以派上用场了。

2.2.2 ngx_get_options()

ngx_get_options函数主要是解析Nginx的启动参数,Nginx的启动选项在2.1节中已经说明。

657 static ngx_int_t

 658 ngx_get_options(int argc, char *const *argv)

 659 {

 660     u_char     *p;

 661     ngx_int_t   i;

 662

/* 这个地方特别注意,argc是从1开始检查解析的,我们知道nginx的默认启动,可以不带任何参数,所以,如果是默认第一次启动nginx,就不用进入for循环,而直接返回 */

 663     for (i = 1; i < argc; i++) {

 664

 665         p = (u_char *) argv[i];

 666

/* Nginx的启动选项,只要带了参数的,都以“-”开始,因此此处主要检查参数的合法性,如果不合法,就不在做后续检查,直接返回。这个地方指针也用得比较巧妙,*p++检查当前p指向的字符,然后指针下移。*/

 667         if (*p++ != '-') {

 668             ngx_log_stderr(0, "invalid option: \"%s\"", argv[i]);

 669             return NGX_ERROR;

 670         }

 671

 672         while (*p) {

 673

 674             switch (*p++) {

 675

/* 如果是-?或者-h,则既要显示版本信息,又要显示帮助 */

 676             case '?':

 677             case 'h':

 678                 ngx_show_version = 1;

 679                 ngx_show_help = 1;

 680                 break;

 681

/* 如果是执行 –v,则显示版本信息 */

 682             case 'v':

 683                 ngx_show_version = 1;

 684                 break;

 685

/* 如果执行-V,则显示版本信息,并显示相关配置信息,主要包括编译时的gcc版本,启用了哪些编译选项等 */

 686             case 'V':

 687                 ngx_show_version = 1;

 688                 ngx_show_configure = 1;

 689                 break;

 690

/* 如果执行的是-t,则用于test nginx的配置是否有语法错误,如果有错误则会提示,没有错误会提示syntax oksuccessful 字样,这个跟apache类似。*/

 691             case 't':

 692                 ngx_test_config = 1;

 693                 break;

 694

/* -qquiet模式,主要是在配置测试过程中,避免非错误信息的输出 */

 695             case 'q':

 696                 ngx_quiet_mode = 1;

 697                 break;

 698

/* -p主要是指明nginx启动时的配置目录,这对于重新配置nginx目录时有用 */

 699             case 'p':

 700                 if (*p) {

 701                     ngx_prefix = p;

 702                     goto next;

 703                 }

 704

 705                 if (argv[++i]) {

 706                     ngx_prefix = (u_char *) argv[i];

707                     goto next;

 708                 }

 709

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

 711                 return NGX_ERROR;

 712

/* -c指明启动配置文件nginx.conf的路径,当该文件存储在非标准目录的时候有用 */

 713             case 'c':

 714                 if (*p) {

 715                     ngx_conf_file = p;

 716                     goto next;

 717                 }

 718

 719                 if (argv[++i]) {

 720                     ngx_conf_file = (u_char *) argv[i];

 721                     goto next;

 722                 }

 723

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

 725                 return NGX_ERROR;

 726

/* -g 指明设置配置文件的全局指令,如:nginx -g "pid /var/run/nginx.pid; worker_processes `sysctl -n hw.ncpu`;",多个选项之间以分号分开 */

 727             case 'g':

 728                 if (*p) {

 729                     ngx_conf_params = p;

 730                     goto next;

 731                 }

 732

 733                 if (argv[++i]) {

 734                     ngx_conf_params = (u_char *) argv[i];

 735                     goto next;

 736                 }

 737

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

 739                 return NGX_ERROR;

 740

/* -s是信号处理选项,主要可以处理stop, quit, reopen, reload 这几个作用的信号,其中,stop为停止运行,quit为退出,reopen为重新打开,reload为重新读配置文件。信号都是有master进程处理的,关于masterworker进程,在后续章节中介绍 */

 741             case 's':                               

/* 接下来的if-else主要是将全局变量ngx_signal指向信号值,以方便在后续的处理中,来判断处理具体的信号 */

 742                 if (*p) {

 743                     ngx_signal = (char *) p;

 744

 745                 } else if (argv[++i]) {

 746                     ngx_signal = argv[i];

 747

748                 } else {

 749                     ngx_log_stderr(0, "option \"-s\" requires parameter");

 750                     return NGX_ERROR;

 751                 }

 752

 753                 if (ngx_strcmp(ngx_signal, "stop") == 0

 754                     || ngx_strcmp(ngx_signal, "quit") == 0

 755                     || ngx_strcmp(ngx_signal, "reopen") == 0

 756                     || ngx_strcmp(ngx_signal, "reload") == 0)

 757                 {

 758                     ngx_process = NGX_PROCESS_SIGNALLER;

 759                     goto next;

 760                 }

 761

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

 763                 return NGX_ERROR;

 764

 765             default:

 766                 ngx_log_stderr(0, "invalid option: \"%c\"", *(p - 1));

 767                 return NGX_ERROR;

 768             }

 769         }

 770

 771     next:

 772

 773         continue;

 774     }

 775

 776     return NGX_OK;

 777 }

ngx_get_options函数体之所以是一个while循环,是因为nginx可以一次传递多个参数,比如“nginx –V –c file”,所以只有循环才能解析完整个启动命令参数值。

2.2.3 ngx_time_init

58 void

 59 ngx_time_init(void)

 60 {

 61     ngx_cached_err_log_time.len = sizeof("1970/09/28 12:00:00") - 1;

 62     ngx_cached_http_time.len = sizeof("Mon, 28 Sep 1970 06:00:00 GMT") - 1;

 63     ngx_cached_http_log_time.len = sizeof("28/Sep/1970:12:00:00 +0600") - 1;

 64     ngx_cached_http_log_iso8601.len = sizeof("1970-09-28T12:00:00+06:00") - 1;

 65

 66     ngx_cached_time = &cached_time[0];

 67

 68     ngx_time_update();

 69 }

在上述代码片段中,ngx_cached_err_log_timengx_cached_http_timengx_cached_http_log_timengx_cached_http_log_iso8601均为ngx_str_t类型的,用于记录错误日志时间,http缓存时间,http缓存log时间级iso8061时间,初始化过程中,先计算该时间表示的字符串的长度,这样可以省却在用到的时候再进行计算。ngx_cached_timenginx时间类型的数据结构,他是volatile类型的,即防止编译器优化,每次都要从内存中读取,而不是用缓存值。

15 typedef struct {

 16     time_t      sec;

 17     ngx_uint_t  msec;

 18     ngx_int_t   gmtoff;

 19 } ngx_time_t;

ngx_time_update()函数用于更新系统时间。

72 void

 73 ngx_time_update(void)

 74 {

 75     u_char          *p0, *p1, *p2, *p3;

 76     ngx_tm_t         tm, gmt;

 77     time_t           sec;

 78     ngx_uint_t       msec;

 79     ngx_time_t      *tp;

 80     struct timeval   tv;

 81

82     if (!ngx_trylock(&ngx_time_lock)) {

 83         return;

 84     }

我们先看该函数(行7384)片段的实现,ngx_trylock()获取时间更新的互斥锁,避免进程或线程间并发更新系统时间。对于ngx_trylock()我们可以跟踪其实现原型:

306 #define ngx_trylock(lock)  (*(lock) == 0 && ngx_atomic_cmp_set(lock, 0, 1))

这里判断*lock0,表示没有被枷锁,并对其进行原子操作加锁ngx_atomic_cmp_set,设置其值为1ngx_atomic_cmp_set的实现如下(x86 unix系统):

14 /*

 15  * "cmpxchgl  r, [m]":

 16  *

 17  *     if (eax == [m]) {

 18  *         zf = 1;

 19  *         [m] = r;

 20  *     } else {

 21  *         zf = 0;

 22  *         eax = [m];

 23  *     }

 24  *

 25  *

 26  * The "r" means the general register.

 27  * The "=a" and "a" are the %eax register.

 28  * Although we can return result in any register, we use "a" because it is

 29  * used in cmpxchgl anyway.  The result is actually in %al but not in %eax,

 30  * however, as the code is inlined gcc can test %al as well as %eax,

 31  * and icc adds "movzbl %al, %eax" by itself.

 32  *

 33  * The "cc" means that flags were changed.

 34  */

 35

 36 static ngx_inline ngx_atomic_uint_t

 37 ngx_atomic_cmp_set(ngx_atomic_t *lock, ngx_atomic_uint_t old,

 38     ngx_atomic_uint_t set)

 39 {

 40     u_char  res;

 41

 42     __asm__ volatile (

 43

 44          NGX_SMP_LOCK

 45     "    cmpxchgl  %3, %1;   "

 46     "    sete      %0;       "

 47

 48     : "=a" (res) : "m" (*lock), "a" (old), "r" (set) : "cc", "memory");

 49

 50     return res;

 51 }

该函数通过嵌入式汇编的方式,进行加锁并原子设定lock的值为1。这里不展开对函数实现的细节分析。我们进一步分析该函数的后续实现部分。这里假设我们通过ngx_trylock函数获得了更新系统时间的锁,继续往后走。

 85

 86     ngx_gettimeofday(&tv); /* 此处为系统标准函数,获取系统时间,存储到tv变量中 */

 87

 88     sec = tv.tv_sec;

 89     msec = tv.tv_usec / 1000;

/* 以上两行把tv获取的值,转换为秒和毫秒,并在接下来的一行代码中统一换算为秒*/

 90

 91     ngx_current_msec = (ngx_msec_t) sec * 1000 + msec;

 92

 93     tp = &cached_time[slot];

 94

 95     if (tp->sec == sec) {

 96         tp->msec = msec;

 97         ngx_unlock(&ngx_time_lock);

 98         return;

 99     }

/* 如果系统缓存的时间秒和当前更新的秒值未发生变化,则只需更新毫秒值,然后返回,否则认为系统长时间未更新时间,继续往后执行 */

100

101     if (slot == NGX_TIME_SLOTS - 1) {

102         slot = 0;

103     } else {

104         slot++;

105     }

106

107     tp = &cached_time[slot];

108

109     tp->sec = sec;

110     tp->msec = msec;

111

112     ngx_gmtime(sec, &gmt);

/* ngx_gmtime 将时间换算为天、小时、分、秒具体实现比较简单,这里不再深入分析*/

113

114

115     p0 = &cached_http_time[slot][0];

116

117     (void) ngx_sprintf(p0, "%s, %02d %s %4d %02d:%02d:%02d GMT",

118                        week[gmt.ngx_tm_wday], gmt.ngx_tm_mday,

119                        months[gmt.ngx_tm_mon - 1], gmt.ngx_tm_year,

120                        gmt.ngx_tm_hour, gmt.ngx_tm_min, gmt.ngx_tm_sec);

121

122 #if (NGX_HAVE_GETTIMEZONE)

123

/* 这里指需要计算时区 */

124     tp->gmtoff = ngx_gettimezone();

125     ngx_gmtime(sec + tp->gmtoff * 60, &tm);

126

127 #elif (NGX_HAVE_GMTOFF)

128

129     ngx_localtime(sec, &tm);

130     cached_gmtoff = (ngx_int_t) (tm.ngx_tm_gmtoff / 60);

131     tp->gmtoff = cached_gmtoff;

132

133 #else

134

/* 直接计算本地系统时间 */

135     ngx_localtime(sec, &tm);

136     cached_gmtoff = ngx_timezone(tm.ngx_tm_isdst);

137     tp->gmtoff = cached_gmtoff;

138

139 #endif

140

141

142     p1 = &cached_err_log_time[slot][0];

143

144     (void) ngx_sprintf(p1, "%4d/%02d/%02d %02d:%02d:%02d",

145                        tm.ngx_tm_year, tm.ngx_tm_mon,

146                        tm.ngx_tm_mday, tm.ngx_tm_hour,

147                        tm.ngx_tm_min, tm.ngx_tm_sec);

148

149

150     p2 = &cached_http_log_time[slot][0];

151

152     (void) ngx_sprintf(p2, "%02d/%s/%d:%02d:%02d:%02d %c%02d%02d",

153                        tm.ngx_tm_mday, months[tm.ngx_tm_mon - 1],

154                        tm.ngx_tm_year, tm.ngx_tm_hour,

155                        tm.ngx_tm_min, tm.ngx_tm_sec,

156                        tp->gmtoff < 0 ? '-' : '+',

157                        ngx_abs(tp->gmtoff / 60), ngx_abs(tp->gmtoff % 60));

158

159     p3 = &cached_http_log_iso8601[slot][0];

160

161     (void) ngx_sprintf(p3, "%4d-%02d-%02dT%02d:%02d:%02d%c%02d:%02d",

162                        tm.ngx_tm_year, tm.ngx_tm_mon,

163                        tm.ngx_tm_mday, tm.ngx_tm_hour,

164                        tm.ngx_tm_min, tm.ngx_tm_sec,

165                        tp->gmtoff < 0 ? '-' : '+',

166                        ngx_abs(tp->gmtoff / 60), ngx_abs(tp->gmtoff % 60));

167

/* 上述的几个printf 完全按照时间的需求格式,将系统时间存储到对应的变量中,这里我们看到,已经不再有长度的计算之类的。*/

168

169     ngx_memory_barrier(); /* 一个应用层设置内存屏障的函数,表示上述片段已经计算完毕,需要完成内存的同步,然后在后续的几步操作中,实现对初始化最初的几个全局变量的赋值操作。这里再次看到,没有字符串长度的计算,nginx通过初始化一次长度计算从而一劳永逸,而不用每次计算时再去纠结字符串的长度问题。这一点比起apache来说,确实优化不少。*/

170

171     ngx_cached_time = tp;

172     ngx_cached_http_time.data = p0;

173     ngx_cached_err_log_time.data = p1;

174     ngx_cached_http_log_time.data = p2;

175     ngx_cached_http_log_iso8601.data = p3;

176

177     ngx_unlock(&ngx_time_lock); /* 释放锁 */

178 }

2.2.4 ngx_regex_init()

269 #if (NGX_PCRE)

 270     ngx_regex_init();

 271 #endif

如果启用了PCRE功能,则进行正则表达式的初始化工作。Nginx中的pcre主要是用来支持URL Rewrite的,URL Rewrite主要是为了满足代理模式下,对请求访问的URL地址进行rewrite操作,来实现定向访问。

18 void

 19 ngx_regex_init(void)

 20 {

 21     pcre_malloc = ngx_regex_malloc;

 22     pcre_free = ngx_regex_free;

 23 }

Pcre的初始化操作,主要是初始化处理pcre正则时的内存分配和释放,因此其赋值操作也仅是两个内存操作。关于内存管理,在后续独立的内存管理章节进行分析讨论。

2.2.5 ngx_log_init()

由于log是相对独立的一块,在这里我们对Nginxlog部分,进行细致的分析研究。

先看一下log的结构体:

typedef struct ngx_log_s         ngx_log_t;

47 struct ngx_log_s {

 48     ngx_uint_t           log_level;

 49     ngx_open_file_t     *file;

 50

 51     ngx_atomic_uint_t    connection;

 52

 53     ngx_log_handler_pt   handler;

 54     void                *data;

 55

 56     /*

 57      * we declare "action" as "char *" because the actions are usually

 58      * the static strings and in the "u_char *" case we have to override

 59      * their types all the time

 60      */

 61

 62     char                *action;

 63 };

第一个字段是log_level,说明日志记录的级别,在nginx中,与其他的web服务器一样,也包含了08个相对标准的级别:

15 #define NGX_LOG_STDERR            0

 16 #define NGX_LOG_EMERG             1

 17 #define NGX_LOG_ALERT             2

 18 #define NGX_LOG_CRIT              3

 19 #define NGX_LOG_ERR               4

 20 #define NGX_LOG_WARN              5

 21 #define NGX_LOG_NOTICE            6

 22 #define NGX_LOG_INFO              7

 23 #define NGX_LOG_DEBUG             8

在正式使用中,建议配置在3级以下,因为级别数字越大,产生的告警日志越多。日志越多,对于调试分析非常有用,但在正式使用过程中,日志数量产生太多,会写磁盘IO,对性能会造成极大的影响。

Filelog日志的记录文件标识符,在nginx中文件描述符同样进行了特定的封装:

18 typedef struct ngx_open_file_s   ngx_open_file_t;

89 struct ngx_open_file_s {

 90     ngx_fd_t              fd; /* 标准IO文件描述符 */

 91     ngx_str_t             name; /* 文件名称 */

 92

 93     u_char               *buffer; /* 文件buffer */

 94     u_char               *pos; /* 指示文件中的位置 */

 95     u_char               *last;

 96

 97 #if 0

 98     /* e.g. append mode, error_log */

 99     ngx_uint_t            flags;

100     /* e.g. reopen db file */

101     ngx_uint_t          (*handler)(void *data, ngx_open_file_t *file);

102     void                 *data;

103 #endif

104 };

 

重新回到ngx_log_init()函数实现,其他几个为分析的参数后续再做说明。

264 ngx_log_t *

265 ngx_log_init(u_char *prefix)

266 {

267     u_char  *p, *name;

268     size_t   nlen, plen;

269

270     ngx_log.file = &ngx_log_file; /*此处初始化log中的file字段存储全局变量ngx_log_file的地址*/

271     ngx_log.log_level = NGX_LOG_NOTICE;

272

273     name = (u_char *) NGX_ERROR_LOG_PATH;

这里名字初始化为error日志文件路径,默认定义为(objs/ngx_auto_config.h):

321 #ifndef NGX_ERROR_LOG_PATH

322 #define NGX_ERROR_LOG_PATH  "logs/error.log"

323 #endif

 

274

275     /*

276      * we use ngx_strlen() here since BCC warns about

277      * condition is always false and unreachable code

278      */

279

280     nlen = ngx_strlen(name);

281

282     if (nlen == 0) {

283         ngx_log_file.fd = ngx_stderr;

284         return &ngx_log;

285     }

286

287     p = NULL;

288

289 #if (NGX_WIN32)

290     if (name[1] != ':') {

291 #else

292     if (name[0] != '/') {

293 #endif

294

295         if (prefix) {

296             plen = ngx_strlen(prefix);

297

298         } else {

299 #ifdef NGX_PREFIX

300             prefix = (u_char *) NGX_PREFIX;

301             plen = ngx_strlen(prefix);

302 #else

303             plen = 0;

304 #endif

305         }

306

307         if (plen) {

308             name = malloc(plen + nlen + 2);

309             if (name == NULL) {

310                 return NULL;

311             }

312

313             p = ngx_cpymem(name, prefix, plen);

314

315             if (!ngx_path_separator(*(p - 1))) {

316                 *p++ = '/';

317             }

318

319             ngx_cpystrn(p, (u_char *) NGX_ERROR_LOG_PATH, nlen + 1);

320

321             p = name;

322         }

323     }

在上述这个片段中,主要分配内存,来存储log文件名,prefix为指定的路径前缀。初始化log文件的路径名称后,后续就要打开log文件,进行必要的初始化操作了。

324

325     ngx_log_file.fd = ngx_open_file(name, NGX_FILE_APPEND,

326                                     NGX_FILE_CREATE_OR_OPEN,

327                                     NGX_FILE_DEFAULT_ACCESS);

看看src/os/unix/ngx_files.h中对ngx_open_file的定义:

57 #define ngx_open_file(name, mode, create, access)                            \

 58     open((const char *) name, mode|create|O_BINARY, access)

实际上就是调用了标准的open函数。

src/os/unix/ngx_files.h文件中

75 #define NGX_FILE_APPEND          O_WRONLY|O_APPEND

72 #define NGX_FILE_CREATE_OR_OPEN  O_CREAT

78 #define NGX_FILE_DEFAULT_ACCESS  0644

可以看到文件是以只写方式打开的,并执行追加的方式,如果文件不存在,则先创建该文件,并赋予文件0644的权限,创建者和超级用户才具有读写权限,其他用户和组用户只有读权限。这里要特别注意这一点,普通用户是没办法改写nginx的日志的,另外文件是初始化时候打开的初始化的,不要试图在运行过程中以超级用户权限删除文件,认为还会继续有日志文件产生记录。这个和apache是类似的。

328

329     if (ngx_log_file.fd == NGX_INVALID_FILE) {

330         ngx_log_stderr(ngx_errno,

331                        "[alert] could not open error log file: "

332                        ngx_open_file_n " \"%s\" failed", name);

333 #if (NGX_WIN32)

334         ngx_event_log(ngx_errno,

335                        "could not open error log file: "

336                        ngx_open_file_n " \"%s\" failed", name);

337 #endif

338

339         ngx_log_file.fd = ngx_stderr;

340     }

如果文件创建出错,将标准错误赋给log文件描述符。

341

342     if (p) {

343         ngx_free(p);

344     }

之前处理文件名这一串,都是为了打开文件做准备的,完毕后,它的使命也结束了,释放存储的内存。并返回,nginxlog便初始化完毕。

345

346     return &ngx_log;

347 }

 

Nginxssl部分暂不进行分析,因此其初始化暂时略过。由于后续的初始化部分涉及到很多模块内容,顺着初始化这条线,后续部分暂不在本章节中叙述。初始化中的下一个片段是内存池的初始化部分,这部分相对较复杂,我们单独列一章来进行分析。见第三章。

阅读(505) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~