免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
12下一页
最近访问板块 发新帖
查看: 8354 | 回复: 11
打印 上一主题 下一主题

原码分析第三期<<Nginx源代码情景分析(2)——Nginx系统启动初始化>> [复制链接]

论坛徽章:
3
金牛座
日期:2014-06-14 22:04:062015年辞旧岁徽章
日期:2015-03-03 16:54:152015年迎新春徽章
日期:2015-03-04 09:49:45
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2011-11-30 21:49 |只看该作者 |倒序浏览
接<<Nginx源代码情景分析(1)——预备知识>>部分


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_time,ngx_cached_http_time,ngx_cached_http_log_time,ngx_cached_http_log_iso8601均为ngx_str_t类型的,用于记录错误日志时间,http缓存时间,http缓存log时间级iso8061时间,初始化过程中,先计算该时间表示的字符串的长度,这样可以省却在用到的时候再进行计算。ngx_cached_time是nginx时间类型的数据结构,他是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     }
我们先看该函数(行73—84)片段的实现,ngx_trylock()获取时间更新的互斥锁,避免进程或线程间并发更新系统时间。对于ngx_trylock()我们可以跟踪其实现原型:
306 #define ngx_trylock(lock)  (*(lock) == 0 && ngx_atomic_cmp_set(lock, 0, 1))
这里判断*lock为0,表示没有被枷锁,并对其进行原子操作加锁ngx_atomic_cmp_set,设置其值为1。ngx_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 }

论坛徽章:
3
金牛座
日期:2014-06-14 22:04:062015年辞旧岁徽章
日期:2015-03-03 16:54:152015年迎新春徽章
日期:2015-03-04 09:49:45
2 [报告]
发表于 2011-12-01 21:14 |只看该作者
Nginx日志初始化部分:



2.2.5 ngx_log_init()
由于log是相对独立的一块,在这里我们对Nginx的log部分,进行细致的分析研究。
先看一下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服务器一样,也包含了0—8个相对标准的级别:
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,对性能会造成极大的影响。
File是log日志的记录文件标识符,在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     }
之前处理文件名这一串,都是为了打开文件做准备的,完毕后,它的使命也结束了,释放存储的内存。并返回,nginx的log便初始化完毕。
345
346     return &ngx_log;
347 }

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

论坛徽章:
0
3 [报告]
发表于 2011-12-01 22:23 |只看该作者
mark

论坛徽章:
3
金牛座
日期:2014-06-14 22:04:062015年辞旧岁徽章
日期:2015-03-03 16:54:152015年迎新春徽章
日期:2015-03-04 09:49:45
4 [报告]
发表于 2011-12-01 22:36 |只看该作者
mark
zhangchuhu 发表于 2011-12-01 22:23



    欢迎参与一起分析!

论坛徽章:
3
2015年迎新春徽章
日期:2015-03-04 09:48:31平安夜徽章
日期:2015-12-26 00:06:30C
日期:2016-10-25 16:26:25
5 [报告]
发表于 2011-12-18 17:43 |只看该作者
楼主讲的很具体,连结构体定义和宏定义出处都列出了。赞一个!

论坛徽章:
0
6 [报告]
发表于 2012-04-05 12:21 |只看该作者
不错!再次阅读

论坛徽章:
0
7 [报告]
发表于 2012-04-05 12:23 |只看该作者
nginx的openssl模块分析,后面有空的话,我来补充上

论坛徽章:
0
8 [报告]
发表于 2012-04-05 16:26 |只看该作者
刚找到
Emiller's Guide To Nginx Module Development
这篇的原文和中文PDF,贴在这里备份下,方便大家直接阅读。

E文链接:
http://www.evanmiller.org/nginx-modules-guide.html

中文翻译
nginx-modules-guide-cn.pdf
nginx-modules-guide-cn.pdf (339.14 KB, 下载次数: 228)


论坛徽章:
0
9 [报告]
发表于 2013-06-26 12:14 |只看该作者
分析很漂亮

论坛徽章:
0
10 [报告]
发表于 2013-10-31 18:10 |只看该作者
这个项目还在继续吗,如果还在继续,我想申请参加一下
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP