本章先分析Nginx启动过程中main函数依次调用的各个子函数,分析完main中的所有子函数后,然后在文章前面总结完整的main()代码以及nginx启动流程。
/*
函数名:ngx_get_options
功能:根据命令行参数,将对应标志位置位;将输入的命令行参数(字符串地址)保存到对应的全局变量中。
参数:argc,argv
*/
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;
// 使用-q参数可以不把error级别以下的信息输出到目录
case 'q':
ngx_quiet_mode = 1;
break;
/* -p参数指定nginx的安装目录来启动nginx,例如:/usr/local/nginx/sbin/nginx -p /usr/local/nginx */
case 'p':
/* 如果-p后面接的参数与-p之间没有空格,如:nginx -p/usr/local/nginx/,p指向/usr/local/nginx/ */
if (*p) {
//全局变量ngx_prefix指向字符串/usr/local/nginx/
ngx_prefix = p;
//跳出while循环,解析下一个命令行参数。
goto next;
}
/* 若-p与后面的参数有空格,比如:nginx –p /usr/local/nginx/,argv[++i]指向/usr/local/nginx/ */
if (argv[++i]) {
ngx_prefix = (u_char *) argv[i];
goto next;
}
ngx_log_stderr(0, "option \"-p\" requires directory name");
return NGX_ERROR;
/*使用-c参数指定配置文件来启动nginx,例如:
/usr/local/nginx/sbin/nginx -c /tmp/nginx.conf */
case 'c':
/* 如果-c后面接的参数与-c之间没有空格,如:nginx -c/tmp/nginx.conf,p指向/tmp/nginx.conf。*/
if (*p) {
// ngx_conf_file指向/tmp/nginx.conf。
ngx_conf_file = p;
goto next;
}
/*如果-c与后面的参数有空格,如:nginx -c /tmp/nginx.conf,argv[++i]指向字符串/tmp/nginx.conf的地址 */
if (argv[++i]) {
ngx_conf_file = (u_char *) argv[i];
goto next;
}
ngx_log_stderr(0, "option \"-c\" requires file name");
return NGX_ERROR;
// 使用-g参数临时指定一些全局配置项,以使新的配置项生效。
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;
// -s是告诉nginx程序向正在运行的nginx服务发出信号量。
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;
}
/* stop:强行停止,quit:优雅停止,reopen:回滚日志文件,reload:重载配置文件 */
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;
}
}
// 跳出while循环
next:
continue;
}
return NGX_OK;
}
/*初始化并更新时间 */
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_time = &cached_time[0];
/* 初始化全局变量ngx_cached_time,ngx_cached_http_time.data,ngx_cached_http_log_time.data,ngx_cached_http_log_time.data */
ngx_time_update();
}
/*
函数名:ngx_log_init
功能:初始化全局变量ngx_log,创建或打开日志文件ngx_log_file.fd
参数prefix:main函数调用传递的实参为ngx_prefix,即Nginx的安装路径
*/
//下面是与该函数相关的一些全局变量的定义
struct ngx_log_s {
ngx_unit_t log_level;
ngx_open_file_t *file;
ngx_atomic_unit_t connection;
ngx_log_handler_pt handler;
void *data;
char *action;
}
struct ngx_open_file_s {
ngx_fd_t fd;
ngx_str_t name;
u_char *buffer;
u_char *pos;
u_char *last;
#if 0
/* e.g. append mode, error_log */
ngx_uint_t flags;
/* e.g. reopen db file */
ngx_uint_t (*handler)(void *data, ngx_open_file_t *file);
void *data;
#endif
}
static ngx_log_t ngx_log;
static ngx_open_file_t ngx_log_file;
ngx_log_t *
ngx_log_init(u_char *prefix)
{
u_char *p, *name;
size_t nlen, plen;
ngx_log.file = &ngx_log_file;
ngx_log.log_level = NGX_LOG_NOTICE;
// 默认值“logs/error.log“
name = (u_char *) NGX_ERROR_LOG_PATH;
/*
* we use ngx_strlen() here since BCC warns about
* condition is always false and unreachable code
*/
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为0,且定义了默认路径,则使用默认的安装路径NGX_PREFIX,默认"/usr/local/nginx/" */
prefix = (u_char *) NGX_PREFIX;
plen = ngx_strlen(prefix);
#else
plen = 0;
#endif
}
if (plen) {
// 分配长度为 /usr/local/nginx/ + logs/error.log
name = malloc(plen + nlen + 2);
if (name == NULL) {
return NULL;
}
// 复制路径如/usr/local/nginx/,到name所指的存储区域
p = ngx_cpymem(name, prefix, plen);
// 若上条语句复制的prefix不是以”/”结尾,则在后面加上”/”
if (!ngx_path_separator(*(p - 1))) {
*p++ = '/';
}
/* 以尾部添加的方式复制 NGX_ERROR_LOG_PATH到name,此时name所指的字符串为prefix + NGX_ERROR_LOG_PATH,如/usr/local/nginx/ logs/error.log */
ngx_cpystrn(p, (u_char *) NGX_ERROR_LOG_PATH, nlen + 1);
//使p指向字符串首
p = name;
}
}
// 创建或打开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 notopen error log file: "
ngx_open_file_n "\"%s\" failed", name);
#if (NGX_WIN32)
ngx_event_log(ngx_errno,
"could not openerror log file: "
ngx_open_file_n "\"%s\" failed", name);
#endif
ngx_log_file.fd = ngx_stderr;
}
// 释放p指向的内存
if (p) {
ngx_free(p);
}
return &ngx_log;
}
/* 函数名:ngx_process_options
参数:main函数调用时传递的init_cycle的地址
功能:初始化cycle结构体中的prefix,conf_prefix,conf_file等变量
*/
static ngx_int_t
ngx_process_options(ngx_cycle_t *cycle)
{
u_char *p;
size_t len;
// 如果ngx_prefix已被初始化且不为0
if (ngx_prefix) {
len = ngx_strlen(ngx_prefix);
p = ngx_prefix;
/* 若p指向的内容不是以‘/’开头,则为它分配内存,将ngx_prefix的内容复制过去 */
if (!ngx_path_separator(*p)) {
p = ngx_pnalloc(cycle->pool, len + 1);
if (p == NULL) {
return NGX_ERROR;
}
ngx_memcpy(p, ngx_prefix, len);
// p指向的内容以‘/’结尾
p[len++] = '/';
}
// conf_prefix为Nginx配置文件所在目录的路径,这里对其进行初始化
cycle->conf_prefix.len = len;
cycle->conf_prefix.data = p;
// prefix为Nginx安装目录的路径
cycle->prefix.len = len;
cycle->prefix.data = p;
}else {
// 若没有初始化ngx_prefix,且自动生成的配置文件中也没有定义NGX_PREFIX
#ifndef NGX_PREFIX
p = ngx_pnalloc(cycle->pool, NGX_MAX_PATH);
if (p == NULL) {
return NGX_ERROR;
}
// 获取当前工作目录,将其绝对地址保存在p中
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;
// 若定义了NGX_PREFIX
#else
// 若定义NGX_CONF_PREFIX
#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
}
/* 若已初始化ngx_conf_file(配置文件相对于安装目录的路径),对cycle的conf_file成员初始化 */
if (ngx_conf_file) {
cycle->conf_file.len = ngx_strlen(ngx_conf_file);
cycle->conf_file.data = ngx_conf_file;
} else {
/* 若没有初始化ngx_conf_file,则使用自动生成的配置文件中定义的宏,如:“conf/nginx.conf”*/
ngx_str_set(&cycle->conf_file, NGX_CONF_PATH);
}
// 将获得配置文件的绝对路径,并保存到cycle->conf_file,为什么要执行这步操作?
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--)
{
/* p指针由字符串尾向头部移动,遇到第一个‘/’,执行下面操作,实际是去掉配置文件名,conf.prefix只保存配置文件的目录 */
if (ngx_path_separator(*p)) {
cycle->conf_prefix.len = p - ngx_cycle->conf_file.data + 1;
cycle->conf_prefix.data = ngx_cycle->conf_file.data;
break;
}
}
// Nginx处理配置文件时需要特殊处理的在配置行携带的参数
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;
}
下面这段代码是main()中初始化每个module的index,index的值是按ngx_modules.c中模块的定义顺序来初始化的。编译nginx后,会在源代码根生成objs目录,该目录中包含ngx_auto_config.h,ngx_auto_headers.h,ngx_modules.c,makefile。
// ngx_max_module表示nginx一共有的模块个数。
ngx_max_module = 0;
for (i = 0; ngx_modules[i]; i++) {
ngx_modules[i]->index = ngx_max_module++;
}