在阅读日志模块的时候发现ngx_log_error_core的90~91行:ngx_memcpy(errstr, ngx_cached_err_log_time.data,ngx_cached_err_log_time.len);可以看出在日志所用函数的时间来至于变量ngx_cached_err_log_time,顺便找到该变量在ngx_times.c中定义。先把一个主要的函数代码贴上来,在细细分析一下:
void ngx_time_update(time_t sec, ngx_uint_t msec)
{
u_char *p0, *p1, *p2;
ngx_tm_t tm, gmt;
ngx_time_t *tp;
struct timeval tv;
if (!ngx_trylock(&ngx_time_lock)) {
return;
}
if (sec == 0) {
ngx_gettimeofday(&tv);
sec = tv.tv_sec;
msec = tv.tv_usec / 1000;
}
ngx_current_msec = (ngx_msec_t) sec * 1000 + msec;
tp = &cached_time[slot];
if (tp->sec == sec) {
tp->msec = msec;
ngx_unlock(&ngx_time_lock);
return;
}
if (slot == NGX_TIME_SLOTS - 1) {
slot = 0;
} else {
slot++;
}
tp = &cached_time[slot];
tp->sec = sec;
tp->msec = msec;
ngx_gmtime(sec, &gmt);
p0 = &cached_http_time[slot][0];
(void) ngx_sprintf(p0, "%s, %02d %s %4d %02d:%02d:%02d GMT",
week[gmt.ngx_tm_wday], gmt.ngx_tm_mday,
months[gmt.ngx_tm_mon - 1], gmt.ngx_tm_year,
gmt.ngx_tm_hour, gmt.ngx_tm_min, gmt.ngx_tm_sec);
#if (NGX_HAVE_GETTIMEZONE)
tp->gmtoff = ngx_gettimezone();
ngx_gmtime(sec + tp->gmtoff * 60, &tm);
#elif (NGX_HAVE_GMTOFF)
ngx_localtime(sec, &tm);
tp->gmtoff = (ngx_int_t) (tm.ngx_tm_gmtoff / 60);
#else
ngx_localtime(sec, &tm);
tp->gmtoff = ngx_timezone(tm.ngx_tm_isdst);
#endif
p1 = &cached_err_log_time[slot][0];
(void) ngx_sprintf(p1, "%4d/%02d/%02d %02d:%02d:%02d",
tm.ngx_tm_year, tm.ngx_tm_mon,
tm.ngx_tm_mday, tm.ngx_tm_hour,
tm.ngx_tm_min, tm.ngx_tm_sec);
p2 = &cached_http_log_time[slot][0];
(void) ngx_sprintf(p2, "%02d/%s/%d:%02d:%02d:%02d %c%02d%02d",
tm.ngx_tm_mday, months[tm.ngx_tm_mon - 1],
tm.ngx_tm_year, tm.ngx_tm_hour,
tm.ngx_tm_min, tm.ngx_tm_sec,
tp->gmtoff < 0 ? '-' : '+',
ngx_abs(tp->gmtoff / 60), ngx_abs(tp->gmtoff % 60));
ngx_memory_barrier();
ngx_cached_time = tp;
ngx_cached_http_time.data = p0;
ngx_cached_err_log_time.data = p1;
ngx_cached_http_log_time.data = p2;
ngx_unlock(&ngx_time_lock);
}
看最后几行代码就可以知道这个函数要想得到什么:ngx_cached_http_time.data = p0;
ngx_cached_err_log_time.data = p1;
ngx_cached_http_log_time.data = p2;
好我现在想将函数里面比较细节的几个东西。
ngx_trylock(&ngx_time_lock) 这个函数,没看到ngx_time_lock在什么地方赋值,由于是全局变量,故其值为0;ngx_trylock在ngx_atomic.h中定义为:#define ngx_trylock(lock) (*(lock) == 0 && ngx_atomic_cmp_set(lock, 0, 1)), 而ngx_atomic_cmp_set定义在ngx_gcc_atomic_x86.h中:
static ngx_inline ngx_atomic_uint_t
ngx_atomic_cmp_set(ngx_atomic_t *lock, ngx_atomic_uint_t old,
ngx_atomic_uint_t set)
{
u_char res;
__asm__ volatile (
NGX_SMP_LOCK
" cmpxchgl %3, %1; "
" sete %0; "
: "=a" (res) : "m" (*lock), "a" (old), "r" (set) : "cc", "memory");
return res;
}
到这里我们要思考一个问题,为什么ngx_time_update这个函数使用ngx_trylock这个函数,我们一步一步来分析。
再来看ngx_atomic_cmp_set这个函数:
1.NGX_SMP_LOCK是当PC是SMP时的“lock”的宏定义。
2.
在x86 平台上,CPU提供了在指令执行期间对总线加锁的手段。CPU芯片上有一条引线#HLOCK
pin,如果汇编语言的程序中在一条指令前面加上前缀"LOCK",经过汇编以后的机器代码就使CPU在执行这条指令的时候把#HLOCK
pin的电位拉低,持续到这条指令结束时放开,从而把总线锁住,这样同一总线上别的CPU就暂时不能通过总线访问内存了,保证了这条指令在多处理器环境中的原子性。3.cmpxchgl 指令是比较如果相等并将第二个操作数放到第一个操作数的位置上,%3,%1指第三个和第一个参数。这里指的应当是(×lock)和(set)。
4. sete %0;“sete” 是一个设置指令,如果zf=1则%0也就是第一个参数res为1,否则为0;
更多要知道嵌入式汇编的的东西我觉得看这篇文章就差不多了:
http://blog.csdn.net/zdfonline/archive/2005/03/17/321904.aspx另外,ngx_time_update函数还调用了ngx_localtime 注意在计算月份和年份时,月份要加1,年份加1900,见代码:
voidngx_localtime(time_t s, ngx_tm_t *tm)
{
#if (NGX_HAVE_LOCALTIME_R)
(void) localtime_r(&s, tm);
#else
ngx_tm_t *t;
t = localtime(&s);
*tm = *t;
#endif
tm->ngx_tm_mon++;
tm->ngx_tm_year += 1900;
}