nginx源码分析
nginx-1.11.1
参考书籍《深入理解nginx模块开发与架构解析》
nginx简介
Nginx的作为服务端软件,表现的主要特点是更快、高扩展、高可靠性、低内存消耗、单机支持10万以上的并发连接和热部署等优点。Nginx是一般运行模型master-worker,启动多个worker来处理请求,一般master进程不会对用户提供服务,只用于管理真正提供服务的wroker进程,主要处理命令行服务,包括如启动、停止、平滑升级和重载配置文件等工作,当任意一个worker出现意外退出时,master进程会立刻启动新的worker进程继续提供服务,保证提供服务的worker数量与配置的相同。
nginx启动流程概述
相关的Nginx的配置文档大家可以自行查看,现在我们主要分析一下nginx的代码是如何启动并执行的,直接查看nginx.c中的main函数。
int ngx_cdecl
main(int argc, char *const *argv)
{
ngx_buf_t *b;
ngx_log_t *log;
ngx_uint_t i;
ngx_cycle_t *cycle, init_cycle;
ngx_conf_dump_t *cd;
ngx_core_conf_t *ccf;
ngx_debug_init(); // 检查是否设置为debug模式
if (ngx_strerror_init() != NGX_OK) { // 初始化错误类型数组
return 1;
}
if (ngx_get_options(argc, argv) != NGX_OK) { // 获取解析的输入参数
return 1;
}
if (ngx_show_version) { // 是否是显示输出nginx版本号
ngx_show_version_info(); // 显示输出版本号
if (!ngx_test_config) {
return 0;
}
}
/* TODO */ ngx_max_sockets = -1;
ngx_time_init(); // 时间初始化
#if (NGX_PCRE)
ngx_regex_init(); // 正则初始化
#endif
ngx_pid = ngx_getpid(); // 获取当前的pid
log = ngx_log_init(ngx_prefix); // 初始化日志
if (log == NULL) {
return 1;
}
/* STUB */
#if (NGX_OPENSSL)
ngx_ssl_init(log); // ssl初始化
#endif
/*
* init_cycle->log is required for signal handlers and
* ngx_process_options()
*/
ngx_memzero(&init_cycle, sizeof(ngx_cycle_t)); // 初始化init_cycle
init_cycle.log = log; // 设置日志
ngx_cycle = &init_cycle; // 赋值
init_cycle.pool = ngx_create_pool(1024, log); // 创建1024个池,保证工作进程
if (init_cycle.pool == NULL) {
return 1;
}
if (ngx_save_argv(&init_cycle, argc, argv) != NGX_OK) { // 保存命令行中输入的参数
return 1;
}
if (ngx_process_options(&init_cycle) != NGX_OK) { // 将ngx_get_options中获得参数赋值到ngx_cycle中
return 1;
}
if (ngx_os_init(log) != NGX_OK) { // 初始化系统相关变量 如cpu profile等信息
return 1;
}
/*
* ngx_crc32_table_init() requires ngx_cacheline_size set in ngx_os_init()
*/
if (ngx_crc32_table_init() != NGX_OK) { // 初始化hash表
return 1;
}
if (ngx_add_inherited_sockets(&init_cycle) != NGX_OK) { // 继承socket套接字,在平衡启动的时候继承
return 1;
}
if (ngx_preinit_modules() != NGX_OK) { // 初始化模块并编号
return 1;
}
cycle = ngx_init_cycle(&init_cycle); // 初始化全局cycle
if (cycle == NULL) {
if (ngx_test_config) {
ngx_log_stderr(0, "configuration file %s test failed",
init_cycle.conf_file.data);
}
return 1;
}
if (ngx_test_config) {
if (!ngx_quiet_mode) {
ngx_log_stderr(0, "configuration file %s test is successful",
cycle->conf_file.data);
}
if (ngx_dump_config) {
cd = cycle->config_dump.elts;
for (i = 0; i < cycle->config_dump.nelts; i++) {
ngx_write_stdout("# configuration file ");
(void) ngx_write_fd(ngx_stdout, cd[i].name.data,
cd[i].name.len);
ngx_write_stdout(":" NGX_LINEFEED);
b = cd[i].buffer;
(void) ngx_write_fd(ngx_stdout, b->pos, b->last - b->pos);
ngx_write_stdout(NGX_LINEFEED);
}
}
return 0;
}
if (ngx_signal) {
return ngx_signal_process(cycle, ngx_signal); // 如果有信号则进行信号处理
}
ngx_os_status(cycle->log);
ngx_cycle = cycle;
ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_core_module); // 获取配置
if (ccf->master && ngx_process == NGX_PROCESS_SINGLE) { // 检查是否是单个进程启动
ngx_process = NGX_PROCESS_MASTER;
}
#if !(NGX_WIN32)
if (ngx_init_signals(cycle->log) != NGX_OK) {
return 1;
}
if (!ngx_inherited && ccf->daemon) {
if (ngx_daemon(cycle->log) != NGX_OK) {
return 1;
}
ngx_daemonized = 1;
}
if (ngx_inherited) {
ngx_daemonized = 1;
}
#endif
if (ngx_create_pidfile(&ccf->pid, cycle->log) != NGX_OK) { // 创建pid 文件
return 1;
}
if (ngx_log_redirect_stderr(cycle) != NGX_OK) { // 重定向日志输出
return 1;
}
if (log->file->fd != ngx_stderr) {
if (ngx_close_file(log->file->fd) == NGX_FILE_ERROR) {
ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
ngx_close_file_n " built-in log failed");
}
}
ngx_use_stderr = 0;
if (ngx_process == NGX_PROCESS_SINGLE) { // 是否是单个主进程启动
ngx_single_process_cycle(cycle);
} else {
ngx_master_process_cycle(cycle); // 一个master 多个worker启动
}
return 0;
}
其中几个相对重要的函数分析一下。
传入参数ngx_get_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; // 如果是h则显示版本信息并显示帮助信息
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; // 不显示error以下内容
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 '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;
}
该函数主要是解析传入的参数并根据传入的参数解析。
ngx_signal_process信号发送函数
该函数主要是进行对worker进程的相关的信号的发送,其处理逻辑与kill的信号发送命令一样。
ngx_int_t
ngx_signal_process(ngx_cycle_t *cycle, char *sig)
{
ssize_t n;
ngx_pid_t pid;
ngx_file_t file;
ngx_core_conf_t *ccf;
u_char buf[NGX_INT64_LEN + 2];
ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, "signal process started");
ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_core_module); // 获取配置
ngx_memzero(&file, sizeof(ngx_file_t)); // 初始化文件
file.name = ccf->pid;
file.log = cycle->log;
file.fd = ngx_open_file(file.name.data, NGX_FILE_RDONLY,
NGX_FILE_OPEN, NGX_FILE_DEFAULT_ACCESS); // 打开文件
if (file.fd == NGX_INVALID_FILE) { // 检查是否是合法的pid
ngx_log_error(NGX_LOG_ERR, cycle->log, ngx_errno,
ngx_open_file_n " \"%s\" failed", file.name.data);
return 1;
}
n = ngx_read_file(&file, buf, NGX_INT64_LEN + 2, 0); // 读文件内容
if (ngx_close_file(file.fd) == NGX_FILE_ERROR) {
ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
ngx_close_file_n " \"%s\" failed", file.name.data);
}
if (n == NGX_ERROR) {
return 1;
}
while (n-- && (buf[n] == CR || buf[n] == LF)) { /* void */ }
pid = ngx_atoi(buf, ++n); // 获取pid号
if (pid == (ngx_pid_t) NGX_ERROR) { // 检查是否出错
ngx_log_error(NGX_LOG_ERR, cycle->log, 0,
"invalid PID number \"%*s\" in \"%s\"",
n, buf, file.name.data);
return 1;
}
return ngx_os_signal_process(cycle, sig, pid); // 调用信号处理函数
}
ngx_int_t
ngx_os_signal_process(ngx_cycle_t *cycle, char *name, ngx_pid_t pid)
{
ngx_signal_t *sig;
for (sig = signals; sig->signo != 0; sig++) { // 获取相关信号
if (ngx_strcmp(name, sig->name) == 0) { // 比较是否是改信号
if (kill(pid, sig->signo) != -1) { // 调用kill 给Pid发送相关信号
return 0;
}
ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
"kill(%P, %d) failed", pid, sig->signo);
}
}
return 1;
}
主要工作就是根据传入的参数,给进程发送信号。
ngx_single_process_cycle单个进程启动
ngx_single_process_cycle该函数就是单个进程启动,
void
ngx_single_process_cycle(ngx_cycle_t *cycle)
{
ngx_uint_t i;
if (ngx_set_environment(cycle, NULL) == NULL) { // 设置环境变量
/* fatal */
exit(2); // 如果失败则退出
}
for (i = 0; cycle->modules[i]; i++) { // 初始化给个module
if (cycle->modules[i]->init_process) {
if (cycle->modules[i]->init_process(cycle) == NGX_ERROR) { // 调用各个module的init_process方法
/* fatal */
exit(2);
}
}
}
for ( ;; ) {
ngx_log_debug0(NGX_LOG_DEBUG_EVENT, cycle->log, 0, "worker cycle");
ngx_process_events_and_timers(cycle); // 添加时间循环与事件处理 待后文分析
if (ngx_terminate || ngx_quit) { // 如果退出
for (i = 0; cycle->modules[i]; i++) {
if (cycle->modules[i]->exit_process) {
cycle->modules[i]->exit_process(cycle); // 调用各个module的退出
}
}
ngx_master_process_exit(cycle); // 主进程退出
}
if (ngx_reconfigure) { // 重新配置参数
ngx_reconfigure = 0;
ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, "reconfiguring");
cycle = ngx_init_cycle(cycle);
if (cycle == NULL) {
cycle = (ngx_cycle_t *) ngx_cycle;
continue;
}
ngx_cycle = cycle;
}
if (ngx_reopen) { // 重新打开日志文件
ngx_reopen = 0;
ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, "reopening logs");
ngx_reopen_files(cycle, (ngx_uid_t) -1);
}
}
}
主要工作就是完成,注册事件的添加与定时器的相关操作,启动有关定时器和event的事件注册调度机制,待后文分析。至此一个基本的nginx就启动了,基本的启动流程如上所述。
总结
本文主要就是大致了解了一下nginx的启动的过程,其中启动过程中的相关配套的初始化过程,参数的解析,相关信号的注册,启动运行的模式等过程,后续会继续深入分析nginx的相关机制与运行的原理,鉴于本人才疏学浅,如有疏漏请批评指正。