Nginx源代码阅读 第一部分- Nginx启动
上次
阅读Nginx源代码之一:前言。
修改
5411:5483d9e77b32 的含义是什么?
启动nginx
根据上次最后写的那样,从main函数开始自顶向下地阅读。因此要先找到main函数。
$ global -x main
main 202 src/core/nginx.c main(int argc, char *const *argv)
$
在这个主函数中,对各个组件(如定时器、正则表达式引擎、日志、信号等)进行了初始化。
int ngx_cdecl
main(int argc, char *const *argv)
{
・
・
ngx_debug_init();
・
・
ngx_time_init();
・
・
#if (NGX_PCRE)
ngx_regex_init();
#endif
・
・
log = ngx_log_init(ngx_prefix);
・
・
if (ngx_init_signals(cycle->log) != NGX_OK) {
return 1;
}
・
・
同时,在ngx_init_cycle中进行端口的监听。
cycle = ngx_init_cycle(&init_cycle);
这将在ngx_open_listening_sockets函数中执行。
if (ngx_open_listening_sockets(cycle) != NGX_OK) {
goto failed;
}
除此之外,还将环境变量、命令行参数等信息保存在全局变量中。最后还有以下分支。
if (ngx_process == NGX_PROCESS_SINGLE) {
ngx_single_process_cycle(cycle);
} else {
ngx_master_process_cycle(cycle);
}
通常情况下会调用ngx_master_process_cycle函数,接下来我们查看这个函数内部。在开头做了如下信号阻塞设置。
sigemptyset(&set);
sigaddset(&set, SIGCHLD);
sigaddset(&set, SIGALRM);
sigaddset(&set, SIGIO);
sigaddset(&set, SIGINT);
sigaddset(&set, ngx_signal_value(NGX_RECONFIGURE_SIGNAL));
sigaddset(&set, ngx_signal_value(NGX_REOPEN_SIGNAL));
sigaddset(&set, ngx_signal_value(NGX_NOACCEPT_SIGNAL));
sigaddset(&set, ngx_signal_value(NGX_TERMINATE_SIGNAL));
sigaddset(&set, ngx_signal_value(NGX_SHUTDOWN_SIGNAL));
sigaddset(&set, ngx_signal_value(NGX_CHANGEBIN_SIGNAL));
if (sigprocmask(SIG_BLOCK, &set, NULL) == -1) {
ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
"sigprocmask() failed");
}
NGINX的主进程将在此之后启动工作进程,并在需要时启动缓存管理器进程,然后等待信号通过sigsuspend发送过来。有关如何通过信号控制NGINX的方法的介绍可以在官方网站上找到,详细信息请参阅此处。简单来说,它可以同时关闭NGINX的所有进程、实现平稳重启,并进行无停机版本升级等操作。
顺便提一下,Nginx会将进程名称分别设置为master、worker和cache-manager,然后进行相应的进程名称替换处理,这些处理在此附近完成。
size = sizeof(master_process);
for (i = 0; i < ngx_argc; i++) {
size += ngx_strlen(ngx_argv[i]) + 1;
}
title = ngx_pnalloc(cycle->pool, size);
p = ngx_cpymem(title, master_process, sizeof(master_process) - 1);
for (i = 0; i < ngx_argc; i++) {
*p++ = ' ';
p = ngx_cpystrn(p, (u_char *) ngx_argv[i], size);
}
ngx_setproctitle(title);
我们接下来来看一下启动worker进程的过程(在这个过程中还没有进入sigsuspend)。ngx_start_worker_processes是它的入口。
ngx_start_worker_processes(cycle, ccf->worker_processes,
NGX_PROCESS_RESPAWN);
ngx_start_cache_manager_processes(cycle, 0);
调用ngx_spawn_process的次数应与worker_processes的数量相等。
for (i = 0; i < n; i++) {
ngx_spawn_process(cycle, ngx_worker_process_cycle,
(void *) (intptr_t) i, "worker process", type);
・
・
}
在这个函数中,通过使用ioctl或fcntl来进行套接字的各种设置后,从主进程fork出工作进程。
pid = fork();
switch (pid) {
case -1:
ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
"fork() failed while spawning \"%s\"", name);
ngx_close_channel(ngx_processes[s].channel, cycle->log);
return NGX_INVALID_PID;
case 0:
ngx_pid = ngx_getpid();
proc(cycle, data);
break;
default:
break;
}
当 master 进程在启动了所有的 worker 进程后,通过调用 sigsuspend 进入信号等待状态,此时 proc 将作为 ngx_spawn_process 函数的第二个参数 ngx_worker_process_cycle,成为 worker 进程的主循环。
所以,我读到了nginx已经启动并且单主多工作线程已经启动。这次就到这里了。
下次 (计划)
nginx源代码阅读之二:事件驱动引擎。