- 论坛徽章:
- 3
|
接<<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 } |
|