1 流程图

2 流程解析
2.1 参数解析
主要 是ngx_get_options和ngx_process_options
ngx_get_options是根据参数,设置一些全局标识变量以及保存参数信息
static ngx_int_t
ngx_get_options(int argc, char *const *argv)
{
u_char *p;
ngx_int_t i;
for (i = 1; i < argc; i++) {
p = (u_char *) argv[i];
if (*p++ != '-') {
ngx_log_stderr(0, "invalid option: \"%s\"", argv[i]);
return NGX_ERROR;
}
while (*p) {
switch (*p++) {
case '?':
case 'h':
ngx_show_version = 1;
ngx_show_help = 1;
break;
case 'v':
ngx_show_version = 1;
break;
case 'V':
ngx_show_version = 1;
ngx_show_configure = 1;
break;
case 't':
ngx_test_config = 1;
break;
case 'T':
ngx_test_config = 1;
ngx_dump_config = 1;
break;
case 'q':
ngx_quiet_mode = 1;
break;
case 'p':
if (*p) {
ngx_prefix = p;
goto next;
}
if (argv[++i]) {
ngx_prefix = (u_char *) argv[i];
goto next;
}
ngx_log_stderr(0, "option \"-p\" requires directory name");
return NGX_ERROR;
case 'e':
if (*p) {
ngx_error_log = p;
} else if (argv[++i]) {
ngx_error_log = (u_char *) argv[i];
} else {
ngx_log_stderr(0, "option \"-e\" requires file name");
return NGX_ERROR;
}
if (ngx_strcmp(ngx_error_log, "stderr") == 0) {
ngx_error_log = (u_char *) "";
}
goto next;
case 'c':
if (*p) {
ngx_conf_file = p;
goto next;
}
if (argv[++i]) {
ngx_conf_file = (u_char *) argv[i];
goto next;
}
ngx_log_stderr(0, "option \"-c\" requires file name");
return NGX_ERROR;
case 'g':
if (*p) {
ngx_conf_params = p;
goto next;
}
if (argv[++i]) {
ngx_conf_params = (u_char *) argv[i];
goto next;
}
ngx_log_stderr(0, "option \"-g\" requires parameter");
return NGX_ERROR;
case 's':
if (*p) {
ngx_signal = (char *) p;
} else if (argv[++i]) {
ngx_signal = argv[i];
} else {
ngx_log_stderr(0, "option \"-s\" requires parameter");
return NGX_ERROR;
}
if (ngx_strcmp(ngx_signal, "stop") == 0
|| ngx_strcmp(ngx_signal, "quit") == 0
|| ngx_strcmp(ngx_signal, "reopen") == 0
|| ngx_strcmp(ngx_signal, "reload") == 0)
{
ngx_process = NGX_PROCESS_SIGNALLER;
goto next;
}
ngx_log_stderr(0, "invalid option: \"-s %s\"", ngx_signal);
return NGX_ERROR;
default:
ngx_log_stderr(0, "invalid option: \"%c\"", *(p - 1));
return NGX_ERROR;
}
}
next:
continue;
}
return NGX_OK;
}
-?或者-h:设置全局标识ngx_show_version和ngx_show_help
-v:设置全局标识ngx_show_version,后面输出nginx版本信息
-V:设置全局标识 ngx_show_version和ngx_show_configure,后面输出版本信息以及配置信息
-t :设置全局标识ngx_test_config,后面用于检查配置的有效性
-T:设置全局标识ngx_test_config和ngx_dump_config,用于检查配置的有效性以及输出配置文件列表
-q:设置全局标识 ngx_quiet_mode。
在解析配置文件时,全局标识ngx_test_config开启时,ngx_quiet_mode没有开启时,会输出配置文件的语法正确
在ngx_init_cycle处理完后,在ngx_test_config开启,ngx_quiet_mode没有开启时,输出检测配置文件成功
-pdir或者-p dir:指定目录前缀。全局变量ngx_prefix用于存放目录前缀名
-elog或者-e log:指定错误日志文件 ,全局变量ngx_error_log用于存放错误日志文件名
-cfile或者-c file:指定配置文件名,全局变量ngx_conf_file用于存储配置文件 名
-gparam或者 -g param:指定配置参数,全局变量ngx_conf_params用于存放配置参数名
-ssigname或者-s signame:指定信号名称,全局变量ngx_signal用于存放信号名称。
注意:
在ngx_show_version开启时,ngx_test_config没有开启时,nginx直接退出,即在参数-?/-h/-v/-V没有-t/-T时
ngx_test_config开启时,检测完配置文件后,程序直接退出,即在参数-t/-T,在-T时会输出配置文件
ngx_signal时,主要是master进程来处理ngx_signal_process后退出
ngx_save_argv用来保存参数个数以及参数
static ngx_int_t
ngx_save_argv(ngx_cycle_t *cycle, int argc, char *const *argv)
{
#if (NGX_FREEBSD)
ngx_os_argv = (char **) argv;
ngx_argc = argc;
ngx_argv = (char **) argv;
#else
size_t len;
ngx_int_t i;
ngx_os_argv = (char **) argv;
ngx_argc = argc;
ngx_argv = ngx_alloc((argc + 1) * sizeof(char *), cycle->log);
if (ngx_argv == NULL) {
return NGX_ERROR;
}
for (i = 0; i < argc; i++) {
len = ngx_strlen(argv[i]) + 1;
ngx_argv[i] = ngx_alloc(len, cycle->log);
if (ngx_argv[i] == NULL) {
return NGX_ERROR;
}
(void) ngx_cpystrn((u_char *) ngx_argv[i], (u_char *) argv[i], len);
}
ngx_argv[i] = NULL;
#endif
ngx_os_environ = environ;
return NGX_OK;
}
将argc和argv保存到全局变量ngx_argv,ngx_os_argve和ngx_argv中
ngx_process_options主要是根据选项ngx_prefix, ngx_conf_file, ngx_error_log, ngx_conf_params和ngx_test_config来设置ngx_cycle_t
static ngx_int_t
ngx_process_options(ngx_cycle_t *cycle)
{
u_char *p;
size_t len;
if (ngx_prefix) {
len = ngx_strlen(ngx_prefix);
p = ngx_prefix;
if (len && !ngx_path_separator(p[len - 1])) {
p = ngx_pnalloc(cycle->pool, len + 1);
if (p == NULL) {
return NGX_ERROR;
}
ngx_memcpy(p, ngx_prefix, len);
p[len++] = '/';
}
cycle->conf_prefix.len = len;
cycle->conf_prefix.data = p;
cycle->prefix.len = len;
cycle->prefix.data = p;
} else {
#ifndef NGX_PREFIX
p = ngx_pnalloc(cycle->pool, NGX_MAX_PATH);
if (p == NULL) {
return NGX_ERROR;
}
if (ngx_getcwd(p, NGX_MAX_PATH) == 0) {
ngx_log_stderr(ngx_errno, "[emerg]: " ngx_getcwd_n " failed");
return NGX_ERROR;
}
len = ngx_strlen(p);
p[len++] = '/';
cycle->conf_prefix.len = len;
cycle->conf_prefix.data = p;
cycle->prefix.len = len;
cycle->prefix.data = p;
#else
#ifdef NGX_CONF_PREFIX
ngx_str_set(&cycle->conf_prefix, NGX_CONF_PREFIX);
#else
ngx_str_set(&cycle->conf_prefix, NGX_PREFIX);
#endif
ngx_str_set(&cycle->prefix, NGX_PREFIX);
#endif
}
if (ngx_conf_file) {
cycle->conf_file.len = ngx_strlen(ngx_conf_file);
cycle->conf_file.data = ngx_conf_file;
} else {
ngx_str_set(&cycle->conf_file, NGX_CONF_PATH);
}
if (ngx_conf_full_name(cycle, &cycle->conf_file, 0) != NGX_OK) {
return NGX_ERROR;
}
for (p = cycle->conf_file.data + cycle->conf_file.len - 1;
p > cycle->conf_file.data;
p--)
{
if (ngx_path_separator(*p)) {
cycle->conf_prefix.len = p - cycle->conf_file.data + 1;
cycle->conf_prefix.data = cycle->conf_file.data;
break;
}
}
if (ngx_error_log) {
cycle->error_log.len = ngx_strlen(ngx_error_log);
cycle->error_log.data = ngx_error_log;
} else {
ngx_str_set(&cycle->error_log, NGX_ERROR_LOG_PATH);
}
if (ngx_conf_params) {
cycle->conf_param.len = ngx_strlen(ngx_conf_params);
cycle->conf_param.data = ngx_conf_params;
}
if (ngx_test_config) {
cycle->log->log_level = NGX_LOG_INFO;
}
return NGX_OK;
}
处理ngx_prefix,如果有配置,对于前缀不是以/或者\\结尾的会补充,没有配置ngx_prefix,会以当前工作目录作为前缀。设置ngx_cycle_t中的conf_prefix以及prefix。
处理ngx_conf_file,有配置使用指定的,没有配置使用NGX_CONF_PATH,来设置ngx_cycle_t中的conf_file
处理ngx_error_log,有配置使用指定的,没有配置使用NGX_ERROR_LOG_PATH来设置ngx_cycle_t中的error_log
处理ngx_conf_params,使用指定的来设置ngx_cycle_t中的conf_param
处理日志级别,如果开启了全局标识ngx_test_config,将ngx_cycle_t中log的log_level设置为NGX_LOG_INFO
2.2 基础组件初始化

2.2.1 ngx_debug_init
主要是对于内存分配作调试初始化。对于不同的操作系统采用不同的方式。
darwin系统,依赖于环境变量MallocScribble
freebsd系统,依赖于环境 变量MALLOC_OPTIONS
对于solaris,linux,posix,win32系统,无操作
2.2.2 ngx_strerror_init
处理errno与errnomsg之间的对应关系
对于unix系统,如果有函数定义strerrordesc_np,不做处理。
否则做下面处理
ngx_int_t
ngx_strerror_init(void)
{
char *msg;
u_char *p;
size_t len;
ngx_err_t err;
#if (NGX_SYS_NERR)
ngx_first_error = 0;
ngx_last_error = NGX_SYS_NERR;
#elif (EPERM > 1000 && EPERM < 0x7fffffff - 1000)
/*
* If number of errors is not known, and EPERM error code has large
* but reasonable value, guess possible error codes based on the error
* messages returned by strerror(), starting from EPERM. Notably,
* this covers GNU/Hurd, where errors start at 0x40000001.
*/
for (err = EPERM; err > EPERM - 1000; err--) {
ngx_set_errno(0);
msg = strerror(err);
if (errno == EINVAL
|| msg == NULL
|| strncmp(msg, "Unknown error", 13) == 0)
{
continue;
}
ngx_first_error = err;
}
for (err = EPERM; err < EPERM + 1000; err++) {
ngx_set_errno(0);
msg = strerror(err);
if (errno == EINVAL
|| msg == NULL
|| strncmp(msg, "Unknown error", 13) == 0)
{
continue;
}
ngx_last_error = err + 1;
}
#else
/*
* If number of errors is not known, guess it based on the error
* messages returned by strerror().
*/
ngx_first_error = 0;
for (err = 0; err < 1000; err++) {
ngx_set_errno(0);
msg = strerror(err);
if (errno == EINVAL
|| msg == NULL
|| strncmp(msg, "Unknown error", 13) == 0)
{
continue;
}
ngx_last_error = err + 1;
}
#endif
/*
* ngx_strerror() is not ready to work at this stage, therefore,
* malloc() is used and possible errors are logged using strerror().
*/
len = (ngx_last_error - ngx_first_error) * sizeof(ngx_str_t);
ngx_sys_errlist = malloc(len);
if (ngx_sys_errlist == NULL) {
goto failed;
}
for (err = ngx_first_error; err < ngx_last_error; err++) {
msg = strerror(err);
if (msg == NULL) {
ngx_sys_errlist[err - ngx_first_error] = ngx_unknown_error;
continue;
}
len = ngx_strlen(msg);
p = malloc(len);
if (p == NULL) {
goto failed;
}
ngx_memcpy(p, msg, len);
ngx_sys_errlist[err - ngx_first_error].len = len;
ngx_sys_errlist[err - ngx_first_error].data = p;
}
return NGX_OK;
failed:
err = errno;
ngx_log_stderr(0, "malloc(%uz) failed (%d: %s)", len, err, strerror(err));
return NGX_ERROR;
}
先计算第一个和最后一个错误编号ngx_first_error和ngx_last_error,然后使用ngx_sys_errlist来存放errno与errno_msg的对应关系
2.2.3 ngx_time_init
主要是初始化几个全局变量
ngx_cached_err_log_time
ngx_cached_http_time
ngx_cached_http_log_time
ngx_cached_http_log_iso8601
ngx_cached_syslog_time
ngx_cached_time
cached_http_time
cached_err_log_time
cached_http_log_time
cached_http_log_iso8601
cached_syslog_time
void
ngx_time_init(void)
{
ngx_cached_err_log_time.len = sizeof("1970/09/28 12:00:00") - 1;
ngx_cached_http_time.len = sizeof("Mon, 28 Sep 1970 06:00:00 GMT") - 1;
ngx_cached_http_log_time.len = sizeof("28/Sep/1970:12:00:00 +0600") - 1;
ngx_cached_http_log_iso8601.len = sizeof("1970-09-28T12:00:00+06:00") - 1;
ngx_cached_syslog_time.len = sizeof("Sep 28 12:00:00") - 1;
ngx_cached_time = &cached_time[0];
ngx_time_update();
}
void
ngx_time_update(void)
{
u_char *p0, *p1, *p2, *p3, *p4;
ngx_tm_t tm, gmt;
time_t sec;
ngx_uint_t msec;
ngx_time_t *tp;
struct timeval tv;
if (!ngx_trylock(&ngx_time_lock)) {
return;
}
ngx_gettimeofday(&tv);
sec = tv.tv_sec;
msec = tv.tv_usec / 1000;
ngx_current_msec = ngx_monotonic_time(sec, 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);
cached_gmtoff = (ngx_int_t) (tm.ngx_tm_gmtoff / 60);
tp->gmtoff = cached_gmtoff;
#else
ngx_localtime(sec, &tm);
cached_gmtoff = ngx_timezone(tm.ngx_tm_isdst);
tp->gmtoff = cached_gmtoff;
#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%02i%02i",
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));
p3 = &cached_http_log_iso8601[slot][0];
(void) ngx_sprintf(p3, "%4d-%02d-%02dT%02d:%02d:%02d%c%02i:%02i",
tm.ngx_tm_year, tm.ngx_tm_mon,
tm.ngx_tm_mday, 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));
p4 = &cached_syslog_time[slot][0];
(void) ngx_sprintf(p4, "%s %2d %02d:%02d:%02d",
months[tm.ngx_tm_mon - 1], tm.ngx_tm_mday,
tm.ngx_tm_hour, tm.ngx_tm_min, tm.ngx_tm_sec);
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_cached_http_log_iso8601.data = p3;
ngx_cached_syslog_time.data = p4;
ngx_unlock(&ngx_time_lock);
}
在ngx_time_update过程中,先获取 ngx_time_lock锁,获取当前的时间,与当前槽位的时间作比较,如果当前时间与当前槽位时间的秒相等,同更新更新当前槽位的毫秒,释放ngx_time_lock锁,退出。计算下一槽位的时间,如cached_http_time, cached_err_log_time,cached_http_log_time,cached_http_log_iso8601和cached_syslog_time,更新对应ngx_cached_http_time, ngx_cached_err_log_time,ngx_cached_http_log_time, ngx_cached_http_log_iso8601和ngx_cached_syslog_time,然后释放ngx_time_lock锁
2.2.4 ngx_regex_init
初始化pcre_malloc和pcre_free
void
ngx_regex_init(void)
{
#if !(NGX_PCRE2)
pcre_malloc = ngx_regex_malloc;
pcre_free = ngx_regex_free;
#endif
}
2.2.5 ngx_log_init
初始化日志全局变量ngx_log,初始化日志文件以及日志级别,没有-e参数指定错误日志文件时,使用默认的NGX_ERROR_LOG_PATH。如果错误日志文件 时相对目录时,对于使用-p指定前缀时,日志文件前缀+错误日志文件 。
ngx_log_t *
ngx_log_init(u_char *prefix, u_char *error_log)
{
u_char *p, *name;
size_t nlen, plen;
ngx_log.file = &ngx_log_file;
ngx_log.log_level = NGX_LOG_NOTICE;
if (error_log == NULL) {
error_log = (u_char *) NGX_ERROR_LOG_PATH;
}
name = error_log;
nlen = ngx_strlen(name);
if (nlen == 0) {
ngx_log_file.fd = ngx_stderr;
return &ngx_log;
}
p = NULL;
#if (NGX_WIN32)
if (name[1] != ':') {
#else
if (name[0] != '/') {
#endif
if (prefix) {
plen = ngx_strlen(prefix);
} else {
#ifdef NGX_PREFIX
prefix = (u_char *) NGX_PREFIX;
plen = ngx_strlen(prefix);
#else
plen = 0;
#endif
}
if (plen) {
name = malloc(plen + nlen + 2);
if (name == NULL) {
return NULL;
}
p = ngx_cpymem(name, prefix, plen);
if (!ngx_path_separator(*(p - 1))) {
*p++ = '/';
}
ngx_cpystrn(p, error_log, nlen + 1);
p = name;
}
}
ngx_log_file.fd = ngx_open_file(name, NGX_FILE_APPEND,
NGX_FILE_CREATE_OR_OPEN,
NGX_FILE_DEFAULT_ACCESS);
if (ngx_log_file.fd == NGX_INVALID_FILE) {
ngx_log_stderr(ngx_errno,
"[alert] could not open error log file: "
ngx_open_file_n " \"%s\" failed", name);
#if (NGX_WIN32)
ngx_event_log(ngx_errno,
"could not open error log file: "
ngx_open_file_n " \"%s\" failed", name);
#endif
ngx_log_file.fd = ngx_stderr;
}
if (p) {
ngx_free(p);
}
return &ngx_log;
}
2.2.6 ngx_ssl_init
openssl的初始化,会根据当前所使用的版本作不同的处理,同时会申请一此index资源
ngx_int_t
ngx_ssl_init(ngx_log_t *log)
{
#if OPENSSL_VERSION_NUMBER >= 0x10100003L
if (OPENSSL_init_ssl(OPENSSL_INIT_LOAD_CONFIG, NULL) == 0) {
ngx_ssl_error(NGX_LOG_ALERT, log, 0, "OPENSSL_init_ssl() failed");
return NGX_ERROR;
}
/*
* OPENSSL_init_ssl() may leave errors in the error queue
* while returning success
*/
ERR_clear_error();
#else
OPENSSL_config(NULL);
SSL_library_init();
SSL_load_error_strings();
OpenSSL_add_all_algorithms();
#endif
#ifndef SSL_OP_NO_COMPRESSION
{
/*
* Disable gzip compression in OpenSSL prior to 1.0.0 version,
* this saves about 522K per connection.
*/
int n;
STACK_OF(SSL_COMP) *ssl_comp_methods;
ssl_comp_methods = SSL_COMP_get_compression_methods();
n = sk_SSL_COMP_num(ssl_comp_methods);
while (n--) {
(void) sk_SSL_COMP_pop(ssl_comp_methods);
}
}
#endif
ngx_ssl_connection_index = SSL_get_ex_new_index(0, NULL, NULL, NULL, NULL);
if (ngx_ssl_connection_index == -1) {
ngx_ssl_error(NGX_LOG_ALERT, log, 0, "SSL_get_ex_new_index() failed");
return NGX_ERROR;
}
ngx_ssl_server_conf_index = SSL_CTX_get_ex_new_index(0, NULL, NULL, NULL,
NULL);
if (ngx_ssl_server_conf_index == -1) {
ngx_ssl_error(NGX_LOG_ALERT, log, 0,
"SSL_CTX_get_ex_new_index() failed");
return NGX_ERROR;
}
ngx_ssl_session_cache_index = SSL_CTX_get_ex_new_index(0, NULL, NULL, NULL,
NULL);
if (ngx_ssl_session_cache_index == -1) {
ngx_ssl_error(NGX_LOG_ALERT, log, 0,
"SSL_CTX_get_ex_new_index() failed");
return NGX_ERROR;
}
ngx_ssl_ticket_keys_index = SSL_CTX_get_ex_new_index(0, NULL, NULL, NULL,
NULL);
if (ngx_ssl_ticket_keys_index == -1) {
ngx_ssl_error(NGX_LOG_ALERT, log, 0,
"SSL_CTX_get_ex_new_index() failed");
return NGX_ERROR;
}
ngx_ssl_ocsp_index = SSL_CTX_get_ex_new_index(0, NULL, NULL, NULL, NULL);
if (ngx_ssl_ocsp_index == -1) {
ngx_ssl_error(NGX_LOG_ALERT, log, 0,
"SSL_CTX_get_ex_new_index() failed");
return NGX_ERROR;
}
ngx_ssl_certificate_index = SSL_CTX_get_ex_new_index(0, NULL, NULL, NULL,
NULL);
if (ngx_ssl_certificate_index == -1) {
ngx_ssl_error(NGX_LOG_ALERT, log, 0,
"SSL_CTX_get_ex_new_index() failed");
return NGX_ERROR;
}
ngx_ssl_next_certificate_index = X509_get_ex_new_index(0, NULL, NULL, NULL,
NULL);
if (ngx_ssl_next_certificate_index == -1) {
ngx_ssl_error(NGX_LOG_ALERT, log, 0, "X509_get_ex_new_index() failed");
return NGX_ERROR;
}
ngx_ssl_certificate_name_index = X509_get_ex_new_index(0, NULL, NULL, NULL,
NULL);
if (ngx_ssl_certificate_name_index == -1) {
ngx_ssl_error(NGX_LOG_ALERT, log, 0, "X509_get_ex_new_index() failed");
return NGX_ERROR;
}
ngx_ssl_stapling_index = X509_get_ex_new_index(0, NULL, NULL, NULL, NULL);
if (ngx_ssl_stapling_index == -1) {
ngx_ssl_error(NGX_LOG_ALERT, log, 0, "X509_get_ex_new_index() failed");
return NGX_ERROR;
}
return NGX_OK;
}
2.2.7 ngx_os_init
ngx_os_specific_init会根据操作系统设置对应的io操作
ngx_init_setproctitle设置进程标题
获取系统的pagesize和cachelinesize以及cpu数
ngx_cpuinfo获取cpu信息
getrlimit获取资源限制配置,设置支持最大的sockets
ngx_int_t
ngx_os_init(ngx_log_t *log)
{
ngx_time_t *tp;
ngx_uint_t n;
#if (NGX_HAVE_LEVEL1_DCACHE_LINESIZE)
long size;
#endif
#if (NGX_HAVE_OS_SPECIFIC_INIT)
if (ngx_os_specific_init(log) != NGX_OK) {
return NGX_ERROR;
}
#endif
if (ngx_init_setproctitle(log) != NGX_OK) {
return NGX_ERROR;
}
ngx_pagesize = getpagesize();
ngx_cacheline_size = NGX_CPU_CACHE_LINE;
for (n = ngx_pagesize; n >>= 1; ngx_pagesize_shift++) { /* void */ }
#if (NGX_HAVE_SC_NPROCESSORS_ONLN)
if (ngx_ncpu == 0) {
ngx_ncpu = sysconf(_SC_NPROCESSORS_ONLN);
}
#endif
if (ngx_ncpu < 1) {
ngx_ncpu = 1;
}
#if (NGX_HAVE_LEVEL1_DCACHE_LINESIZE)
size = sysconf(_SC_LEVEL1_DCACHE_LINESIZE);
if (size > 0) {
ngx_cacheline_size = size;
}
#endif
ngx_cpuinfo();
if (getrlimit(RLIMIT_NOFILE, &rlmt) == -1) {
ngx_log_error(NGX_LOG_ALERT, log, errno,
"getrlimit(RLIMIT_NOFILE) failed");
return NGX_ERROR;
}
ngx_max_sockets = (ngx_int_t) rlmt.rlim_cur;
#if (NGX_HAVE_INHERITED_NONBLOCK || NGX_HAVE_ACCEPT4)
ngx_inherited_nonblocking = 1;
#else
ngx_inherited_nonblocking = 0;
#endif
tp = ngx_timeofday();
srandom(((unsigned) ngx_pid << 16) ^ tp->sec ^ tp->msec);
return NGX_OK;
}
2.2.8 ngx_crc32_table_init
crc表的初始化
ngx_int_t
ngx_crc32_table_init(void)
{
void *p;
if (((uintptr_t) ngx_crc32_table_short
& ~((uintptr_t) ngx_cacheline_size - 1))
== (uintptr_t) ngx_crc32_table_short)
{
return NGX_OK;
}
p = ngx_alloc(16 * sizeof(uint32_t) + ngx_cacheline_size, ngx_cycle->log);
if (p == NULL) {
return NGX_ERROR;
}
p = ngx_align_ptr(p, ngx_cacheline_size);
ngx_memcpy(p, ngx_crc32_table16, 16 * sizeof(uint32_t));
ngx_crc32_table_short = p;
return NGX_OK;
}
2.2.9 ngx_slab_sizes_init
根据pagesize来设置ngx_slab_max_size和ngx_slab_exact_size
void
ngx_slab_sizes_init(void)
{
ngx_uint_t n;
ngx_slab_max_size = ngx_pagesize / 2;
ngx_slab_exact_size = ngx_pagesize / (8 * sizeof(uintptr_t));
for (n = ngx_slab_exact_size; n >>= 1; ngx_slab_exact_shift++) {
/* void */
}
}
2.3 平滑升级
ngx_add_inherited_sockets主要是在平滑升级场景使用到
2.4 模块预初始化
调用ngx_preinit_modules,初始化ngx_modules数组的index下标以及name模块名,统计总共模块数ngx_modules_n和ngx_max_module最大模块数
ngx_int_t
ngx_preinit_modules(void)
{
ngx_uint_t i;
for (i = 0; ngx_modules[i]; i++) {
ngx_modules[i]->index = i;
ngx_modules[i]->name = ngx_module_names[i];
}
ngx_modules_n = i;
ngx_max_module = ngx_modules_n + NGX_MAX_DYNAMIC_MODULES;
return NGX_OK;
}
其中ngx_modules是由configure脚本生成的,参考ngx_modules
2.5 初始化ngx_cycle_t
基于旧的ngx_cycle_t来创建ngx_cycle_t,其中旧的ngx_cycle_t是在main中的栈变量。
创建16k的内存池,在内存池中分配ngx_cycle_t
将旧cycle中的conf_prefix,prefix,conf_param,error_log, conf_file赋值给新的cycle
初始化cycle中的paths,config_dump,config_dump_rbtree,open_files,shared_memory,listening,reusable_connections_queue,conf_ctx,hostname和modules
调用核心模块的create_conf创建相应的配置
调用ngx_conf_param和ngx_conf_parse解析配置
2.5.1 解析配置
解析配置分为三种:解析命令行指定的配置,解析文件 ,解析文件中的配置块

解析命令行指定的配置是通过函数ngx_conf_param,最后还是调用ngx_conf_parse
char *
ngx_conf_param(ngx_conf_t *cf)
{
char *rv;
ngx_str_t *param;
ngx_buf_t b;
ngx_conf_file_t conf_file;
param = &cf->cycle->conf_param;
if (param->len == 0) {
return NGX_CONF_OK;
}
ngx_memzero(&conf_file, sizeof(ngx_conf_file_t));
ngx_memzero(&b, sizeof(ngx_buf_t));
//b用于存放解析的字符串缓存,即命令行指定的配置
b.start = param->data; //缓存的起始地址
b.pos = param->data; //需要解析字符串的起始地址
b.last = param->data + param->len;//需要解析字符串的结束地址
b.end = b.last;//缓存的结束地址
b.temporary = 1;
conf_file.file.fd = NGX_INVALID_FILE;
conf_file.file.name.data = NULL;
conf_file.line = 0;
//修改解析配置
cf->conf_file = &conf_file;
cf->conf_file->buffer = &b;
rv = ngx_conf_parse(cf, NULL);
//恢复解析配置
cf->conf_file = NULL;
return rv;
}
解析配置文件是通过ngx_conf_parse。在指定filename时,是开始解析配置文件 ,同时打开文件 ,将文件描述符fd赋值给cf->conf_file->file.fd,在解析当中是通过递归来实现的。因为设置了fd,在解析块时,不指定filename即可,表示解析类型是配置块。cf->conf_file->buffer用于存放读取的缓冲区并且解析。
char *
ngx_conf_parse(ngx_conf_t *cf, ngx_str_t *filename)
{
char *rv;
ngx_fd_t fd;
ngx_int_t rc;
ngx_buf_t buf;
ngx_conf_file_t *prev, conf_file;
enum {
parse_file = 0,
parse_block,
parse_param
} type;
#if (NGX_SUPPRESS_WARN)
fd = NGX_INVALID_FILE;
prev = NULL;
#endif
if (filename) {
/* open configuration file */
fd = ngx_open_file(filename->data, NGX_FILE_RDONLY, NGX_FILE_OPEN, 0);
if (fd == NGX_INVALID_FILE) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, ngx_errno,
ngx_open_file_n " \"%s\" failed",
filename->data);
return NGX_CONF_ERROR;
}
prev = cf->conf_file;
cf->conf_file = &conf_file;
if (ngx_fd_info(fd, &cf->conf_file->file.info) == NGX_FILE_ERROR) {
ngx_log_error(NGX_LOG_EMERG, cf->log, ngx_errno,
ngx_fd_info_n " \"%s\" failed", filename->data);
}
cf->conf_file->buffer = &buf;
buf.start = ngx_alloc(NGX_CONF_BUFFER, cf->log);//存放字符串的起始地址
if (buf.start == NULL) {
goto failed;
}
buf.pos = buf.start;//需要解析的字符串的开始位置
buf.last = buf.start;//需要解析的字符串的结束始位置
buf.end = buf.last + NGX_CONF_BUFFER;//存放字符串的结束地址
buf.temporary = 1;
cf->conf_file->file.fd = fd;
cf->conf_file->file.name.len = filename->len;
cf->conf_file->file.name.data = filename->data;
cf->conf_file->file.offset = 0;
cf->conf_file->file.log = cf->log;
cf->conf_file->line = 1;
type = parse_file;
if (ngx_dump_config
#if (NGX_DEBUG)
|| 1
#endif
)
{
if (ngx_conf_add_dump(cf, filename) != NGX_OK) {
goto failed;
}
} else {
cf->conf_file->dump = NULL;
}
} else if (cf->conf_file->file.fd != NGX_INVALID_FILE) {
type = parse_block;
} else {
type = parse_param;
}
for ( ;; ) {
//读取配置的token,放在cf->args中
rc = ngx_conf_read_token(cf);
/*
* ngx_conf_read_token() may return
*
* NGX_ERROR there is error
* NGX_OK the token terminated by ";" was found
* NGX_CONF_BLOCK_START the token terminated by "{" was found
* NGX_CONF_BLOCK_DONE the "}" was found
* NGX_CONF_FILE_DONE the configuration file is done
*/
if (rc == NGX_ERROR) {
goto done;
}
if (rc == NGX_CONF_BLOCK_DONE) {
if (type != parse_block) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "unexpected \"}\"");
goto failed;
}
goto done;
}
if (rc == NGX_CONF_FILE_DONE) {
if (type == parse_block) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"unexpected end of file, expecting \"}\"");
goto failed;
}
goto done;
}
if (rc == NGX_CONF_BLOCK_START) {
if (type == parse_param) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"block directives are not supported "
"in -g option");
goto failed;
}
}
/* rc == NGX_OK || rc == NGX_CONF_BLOCK_START */
//针对一些配置的特殊处理
if (cf->handler) {
/*
* the custom handler, i.e., that is used in the http's
* "types { ... }" directive
*/
if (rc == NGX_CONF_BLOCK_START) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "unexpected \"{\"");
goto failed;
}
rv = (*cf->handler)(cf, NULL, cf->handler_conf);
if (rv == NGX_CONF_OK) {
continue;
}
if (rv == NGX_CONF_ERROR) {
goto failed;
}
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "%s", rv);
goto failed;
}
//处理token,主要是调用ngx_command_t的set接口来设置配置
rc = ngx_conf_handler(cf, rc);
if (rc == NGX_ERROR) {
goto failed;
}
}
failed:
rc = NGX_ERROR;
done:
if (filename) {
if (cf->conf_file->buffer->start) {
ngx_free(cf->conf_file->buffer->start);
}
if (ngx_close_file(fd) == NGX_FILE_ERROR) {
ngx_log_error(NGX_LOG_ALERT, cf->log, ngx_errno,
ngx_close_file_n " %s failed",
filename->data);
rc = NGX_ERROR;
}
cf->conf_file = prev;
}
if (rc == NGX_ERROR) {
return NGX_CONF_ERROR;
}
return NGX_CONF_OK;
}
2.5.2 词法分析
通过ngx_conf_read_token来读取token,依赖于ngx_buf_t数据结构来解析
struct ngx_buf_s {
u_char *pos; //需要解析字符串的开始位置
u_char *last;//需要解析字符串的结束位置
off_t file_pos;
off_t file_last;
u_char *start; //存储从文件中读取字符,表示存储的开始位置
u_char *end; //表示存储的结束位置
ngx_buf_tag_t tag;
ngx_file_t *file;
ngx_buf_t *shadow;
/* the buf's content could be changed */
unsigned temporary:1;
/*
* the buf's content is in a memory cache or in a read only memory
* and must not be changed
*/
unsigned memory:1;
/* the buf's content is mmap()ed and must not be changed */
unsigned mmap:1;
unsigned recycled:1;
unsigned in_file:1;
unsigned flush:1;
unsigned sync:1;
unsigned last_buf:1;
unsigned last_in_chain:1;
unsigned last_shadow:1;
unsigned temp_file:1;
/* STUB */ int num;
};
在分析过程上中,主要有以下几种情况要处理
注释的处理
\符号的处理
单引号的处理
双引号的处理,比如环境变量env FOO = "bar"
变量元素的处理
空格的处理
static ngx_int_t
ngx_conf_read_token(ngx_conf_t *cf)
{
u_char *start, ch, *src, *dst;
off_t file_size;
size_t len;
ssize_t n, size;
ngx_uint_t found, need_space, last_space, sharp_comment, variable;
ngx_uint_t quoted, s_quoted, d_quoted, start_line;
ngx_str_t *word;
ngx_buf_t *b, *dump;
found = 0;
need_space = 0;
last_space = 1;
sharp_comment = 0;
variable = 0;
quoted = 0;
s_quoted = 0;
d_quoted = 0;
cf->args->nelts = 0;
b = cf->conf_file->buffer;
dump = cf->conf_file->dump;
start = b->pos;
start_line = cf->conf_file->line;
file_size = ngx_file_size(&cf->conf_file->file.info);
for ( ;; ) {
//当pos>=last时表示buf中的字符串已经处理完,需要从文件中读取字符串继续解析
if (b->pos >= b->last) {
if (cf->conf_file->file.offset >= file_size) {
if (cf->args->nelts > 0 || !last_space) {
if (cf->conf_file->file.fd == NGX_INVALID_FILE) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"unexpected end of parameter, "
"expecting \";\"");
return NGX_ERROR;
}
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"unexpected end of file, "
"expecting \";\" or \"}\"");
return NGX_ERROR;
}
return NGX_CONF_FILE_DONE;
}
len = b->pos - start;
if (len == NGX_CONF_BUFFER) {
cf->conf_file->line = start_line;
if (d_quoted) {
ch = '"';
} else if (s_quoted) {
ch = '\'';
} else {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"too long parameter \"%*s...\" started",
10, start);
return NGX_ERROR;
}
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"too long parameter, probably "
"missing terminating \"%c\" character", ch);
return NGX_ERROR;
}
if (len) {
ngx_memmove(b->start, start, len);
}
size = (ssize_t) (file_size - cf->conf_file->file.offset);
if (size > b->end - (b->start + len)) {
size = b->end - (b->start + len);
}
n = ngx_read_file(&cf->conf_file->file, b->start + len, size,
cf->conf_file->file.offset);
if (n == NGX_ERROR) {
return NGX_ERROR;
}
if (n != size) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
ngx_read_file_n " returned "
"only %z bytes instead of %z",
n, size);
return NGX_ERROR;
}
b->pos = b->start + len;
b->last = b->pos + n;
start = b->start;
if (dump) {
dump->last = ngx_cpymem(dump->last, b->pos, size);
}
}
//获取当前字符,同时pos指向下一个字符
ch = *b->pos++;
//当前字符是换行符,将行数加1,如果当前行是注释,将注释标识重置为0
if (ch == LF) {
cf->conf_file->line++;
if (sharp_comment) {
sharp_comment = 0;
}
}
//如果当前行是注释,继续读取下一字符
if (sharp_comment) {
continue;
}
//如果前一字符是转义字符,将转义字符标识置为0,直接处理下一个
if (quoted) {
quoted = 0;
continue;
}
//在遇到单引号或者双引号时,会将need_space置为1
if (need_space) {
if (ch == ' ' || ch == '\t' || ch == CR || ch == LF) {
last_space = 1;
need_space = 0;
continue;
}
if (ch == ';') {
return NGX_OK;
}
if (ch == '{') {
return NGX_CONF_BLOCK_START;
}
if (ch == ')') {
last_space = 1;
need_space = 0;
} else {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"unexpected \"%c\"", ch);
return NGX_ERROR;
}
}
if (last_space) {
//pos指向当前字符的下一个字符,需要 -1
start = b->pos - 1;
start_line = cf->conf_file->line;
if (ch == ' ' || ch == '\t' || ch == CR || ch == LF) {
continue;
}
switch (ch) {
case ';':
case '{':
if (cf->args->nelts == 0) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"unexpected \"%c\"", ch);
return NGX_ERROR;
}
if (ch == '{') {
return NGX_CONF_BLOCK_START;
}
return NGX_OK;
case '}':
if (cf->args->nelts != 0) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"unexpected \"}\"");
return NGX_ERROR;
}
return NGX_CONF_BLOCK_DONE;
case '#':
sharp_comment = 1;
continue;
case '\\':
quoted = 1;
last_space = 0;
continue;
case '"':
start++;
d_quoted = 1;
last_space = 0;
continue;
case '\'':
start++;
s_quoted = 1;
last_space = 0;
continue;
case '$':
variable = 1;
last_space = 0;
continue;
default:
last_space = 0;
}
} else {
if (ch == '{' && variable) {
continue;
}
variable = 0;
if (ch == '\\') {
quoted = 1;
continue;
}
if (ch == '$') {
variable = 1;
continue;
}
if (d_quoted) {
if (ch == '"') {
d_quoted = 0;
need_space = 1;
found = 1;
}
} else if (s_quoted) {
if (ch == '\'') {
s_quoted = 0;
need_space = 1;
found = 1;
}
} else if (ch == ' ' || ch == '\t' || ch == CR || ch == LF
|| ch == ';' || ch == '{')
{
last_space = 1;
found = 1;
}
//当找到token时,将token放入到cf->args中
if (found) {
word = ngx_array_push(cf->args);
if (word == NULL) {
return NGX_ERROR;
}
word->data = ngx_pnalloc(cf->pool, b->pos - 1 - start + 1);
if (word->data == NULL) {
return NGX_ERROR;
}
for (dst = word->data, src = start, len = 0;
src < b->pos - 1;
len++)
{
if (*src == '\\') {
switch (src[1]) {
case '"':
case '\'':
case '\\':
src++;
break;
case 't':
*dst++ = '\t';
src += 2;
continue;
case 'r':
*dst++ = '\r';
src += 2;
continue;
case 'n':
*dst++ = '\n';
src += 2;
continue;
}
}
*dst++ = *src++;
}
*dst = '\0';
word->len = len;
if (ch == ';') {
return NGX_OK;
}
if (ch == '{') {
return NGX_CONF_BLOCK_START;
}
found = 0;
}
}
}
}
2.6 单进程运行ngx_single_process_cycle
其流程为

2.6.1 ngx_set_environment
主要是依据配置文件中的env以及环境变量。在env指定的环境变量是赋值形式时,直接使用,如果不是赋值形式,则依赖于系统中的环境变量
char **
ngx_set_environment(ngx_cycle_t *cycle, ngx_uint_t *last)
{
char **p, **env;
ngx_str_t *var;
ngx_uint_t i, n;
ngx_core_conf_t *ccf;
ngx_pool_cleanup_t *cln;
ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_core_module);
if (last == NULL && ccf->environment) {
return ccf->environment;
}
var = ccf->env.elts;
for (i = 0; i < ccf->env.nelts; i++) {
if (ngx_strcmp(var[i].data, "TZ") == 0
|| ngx_strncmp(var[i].data, "TZ=", 3) == 0)
{
goto tz_found;
}
}
var = ngx_array_push(&ccf->env);
if (var == NULL) {
return NULL;
}
var->len = 2;
var->data = (u_char *) "TZ";
var = ccf->env.elts;
tz_found:
n = 0;
for (i = 0; i < ccf->env.nelts; i++) {
//当env指定的环境变量是赋值形式时
if (var[i].data[var[i].len] == '=') {
n++;
continue;
}
//当env指定的环境变量不是赋值形式时,使用ngx_os_environ的变量
for (p = ngx_os_environ; *p; p++) {
if (ngx_strncmp(*p, var[i].data, var[i].len) == 0
&& (*p)[var[i].len] == '=')
{
n++;
break;
}
}
}
if (last) {
env = ngx_alloc((*last + n + 1) * sizeof(char *), cycle->log);
if (env == NULL) {
return NULL;
}
*last = n;
} else {
cln = ngx_pool_cleanup_add(cycle->pool, 0);
if (cln == NULL) {
return NULL;
}
env = ngx_alloc((n + 1) * sizeof(char *), cycle->log);
if (env == NULL) {
return NULL;
}
cln->handler = ngx_cleanup_environment;
cln->data = env;
}
n = 0;
for (i = 0; i < ccf->env.nelts; i++) {
if (var[i].data[var[i].len] == '=') {
env[n++] = (char *) var[i].data;
continue;
}
for (p = ngx_os_environ; *p; p++) {
if (ngx_strncmp(*p, var[i].data, var[i].len) == 0
&& (*p)[var[i].len] == '=')
{
env[n++] = *p;
break;
}
}
}
env[n] = NULL;
if (last == NULL) {
ccf->environment = env;
environ = env;
}
return env;
}
2.6.2 init_process
所有模块初始化进程 ,其实主要是ngx_event_core_module初始化事件模型,是使用epoll还是其它的
2.6.3 ngx_process_events_and_timers
处理网络和定时器事件
void
ngx_process_events_and_timers(ngx_cycle_t *cycle)
{
ngx_uint_t flags;
ngx_msec_t timer, delta;
if (ngx_timer_resolution) {
timer = NGX_TIMER_INFINITE;
flags = 0;
} else {
timer = ngx_event_find_timer();
flags = NGX_UPDATE_TIME;
#if (NGX_WIN32)
/* handle signals from master in case of network inactivity */
if (timer == NGX_TIMER_INFINITE || timer > 500) {
timer = 500;
}
#endif
}
if (ngx_use_accept_mutex) {
if (ngx_accept_disabled > 0) {
ngx_accept_disabled--;
} else {
if (ngx_trylock_accept_mutex(cycle) == NGX_ERROR) {
return;
}
if (ngx_accept_mutex_held) {
flags |= NGX_POST_EVENTS;
} else {
if (timer == NGX_TIMER_INFINITE
|| timer > ngx_accept_mutex_delay)
{
timer = ngx_accept_mutex_delay;
}
}
}
}
if (!ngx_queue_empty(&ngx_posted_next_events)) {
ngx_event_move_posted_next(cycle);
timer = 0;
}
delta = ngx_current_msec;
(void) ngx_process_events(cycle, timer, flags);
delta = ngx_current_msec - delta;
ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
"timer delta: %M", delta);
ngx_event_process_posted(cycle, &ngx_posted_accept_events);
if (ngx_accept_mutex_held) {
ngx_shmtx_unlock(&ngx_accept_mutex);
}
ngx_event_expire_timers();
ngx_event_process_posted(cycle, &ngx_posted_events);
}
2.7 master,worker进程运行
开始使用sigprocmask来屏蔽信号SIGCHLD, SIGALRM, SIGIO,SIGINT,SIGHUP(重新加载配置),SIGUSR1(重新打开文件 ),SIGWINCH,SIGTERM,SIGQUIT,SIGUSR2(重新执行新的二进制)
,在master进程的循环中,调用sigsuspend去除屏蔽,处理信号。
工作进程是通过ngx_start_worker_processes来创建,通过调用socketpair来创建通道用于父子进程双工通信,channel[0]用于写,channel[1]用于写或者channel[1]用于写,channel[0]用于读,并且将新创建的进程纳入ngx_processes管理中,包含如下属性
pid:子进程id
exited:子进程退出标识
proc:子进程 处理循环
data:子进程依赖数据
name:子进程名
exiting:子进程正在退出标识,在master进程在向worker进程通过通道发送关闭信息时,会将该标识设置为1
respawn:子进程异常退出时,master是否需要将其重启
just_spawn:刚刚生成的进程标识,在重启加载配置时会用到,生成新的进程传递的是NGX_PROCESS_JUST_RESPAWN,生成的新进程的just_spawn是1,向进程发送关闭信号时,会检测just_spawn标识,如果为1,就跳过,只处理旧的worker进程。
detached:是否分离的
master进程向worker进程发送打开通道是通过ngx_pass_open_channel
参考资料: