Nginx的启动流程在ngx_add_inherited_sockets之后,会调用ngx_init_cycle来创建cycle,从下面的代码分析,我们可以看出,在ngx_init_cycle中,Nginx主要进行了以下工作:
1.根据旧cycle来创建新cycle,根据旧cycle的经验,可以推测出创建新cycle所需要的内存空间
2.对新cycle进行初始化工作,包括打开文件、打开监听套接字等
3.解析配置文件
4.模块初始化
5.创建pidfile文件
6.出错回滚及关闭不需要的文件描述符和监听套接字
/* core/ngx_core.h */
typedef struct ngx_cycle_s ngx_cycle_t;
/* core/ngx_cycle.h */
struct ngx_cycle_s { // ngx_cycle_t结构体定义
void ****conf_ctx; // 模块配置上下文数组
ngx_pool_t *pool; // 使用的内存池指针
ngx_log_t *log; // 使用的日志指针
ngx_log_t *new_log; // 新的日志指针
ngx_array_t listening; // 监听套接字数组
ngx_array_t pathes; // 路径数组
ngx_list_t open_files; // 打开文件链表
ngx_uint_t connection_n; // 最大连接数
ngx_connection_t *connections;
ngx_event_t *read_events;
ngx_event_t *write_events;
ngx_cycle_t *old_cycle; // 旧cycle
ngx_str_t conf_file; // 配置文件
ngx_str_t root; // 工作目录
};
/* core/ngx_connection.c */
/* 为cycle打开监听套接字
param cycle: ngx_cycle_t结构体指针
*/
ngx_int_t ngx_open_listening_sockets(ngx_cycle_t *cycle)
{
ngx_uint_t tries, failed, reuseaddr, i;
ngx_err_t err;
ngx_log_t *log;
ngx_socket_t s;
ngx_listening_t *ls;
reuseaddr = 1;
#if (NGX_SUPPRESS_WARN)
failed = 0;
#endif
log = cycle->log;
// tries用于指定重试次数, 这里为5次
for (tries = /* STUB */ 5; tries; tries--) {
failed = 0;
ls = cycle->listening.elts;
// 遍历cycle的监听套接字数组
for (i = 0; i < cycle->listening.nelts; i++) {
if (ls[i].ignore) {
// 如果监听套接字的ignore成员不为0, 那么就直接忽略
continue;
}
if (ls[i].fd != -1) {
// 如果监听套接字的fd不为-1, 那么说明已经打开
continue;
}
if (ls[i].inherited) {
// 如果监听套接字的inherited不为0, 那么说明是继承而来的, 直接跳过
continue;
}
// ngx_socket宏就是调用socket库函数来创建套接字
s = ngx_socket(ls[i].family, ls[i].type, ls[i].protocol,
ls[i].flags);
if (s == -1) {
ngx_log_error(NGX_LOG_EMERG, log, ngx_socket_errno,
ngx_socket_n " %s failed", ls[i].addr_text.data);
return NGX_ERROR;
}
#if (WIN32)
/*
* Winsock assignes a socket number divisible by 4
* so to find a connection we divide a socket number by 4.
*/
if (s % 4) {
ngx_log_error(NGX_LOG_EMERG, ls->log, 0,
ngx_socket_n " created socket %d", s);
return NGX_ERROR;
}
#endif
// 设置套接字选项SO_REUSEADDR, 即允许端口重用
if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR,
(const void *) &reuseaddr, sizeof(int)) == -1) {
ngx_log_error(NGX_LOG_EMERG, log, ngx_socket_errno,
"setsockopt(SO_REUSEADDR) %s failed",
ls[i].addr_text.data);
return NGX_ERROR;
}
if (!(ngx_event_flags & NGX_USE_AIO_EVENT)) {
// 如果不使用异步IO
// 使用fcntl或者ioctl设置套接字为非阻塞
if (ngx_nonblocking(s) == -1) {
ngx_log_error(NGX_LOG_EMERG, log, ngx_socket_errno,
ngx_nonblocking_n " %s failed",
ls[i].addr_text.data);
return NGX_ERROR;
}
}
#if 0
if (ls[i].nonblocking) {
if (ngx_nonblocking(s) == -1) {
ngx_log_error(NGX_LOG_EMERG, log, ngx_socket_errno,
ngx_nonblocking_n " %s failed",
ls[i].addr_text.data);
return NGX_ERROR;
}
}
#endif
// 绑定套接字到监听套接字的sockaddr成员所指地址结构
if (bind(s, ls[i].sockaddr, ls[i].socklen) == -1) {
err = ngx_socket_errno;
ngx_log_error(NGX_LOG_EMERG, log, err,
"bind() to %s failed", ls[i].addr_text.data);
// 绑定失败的错误不是NGX_EADDRINUSE即EADDRINUSE时, 返回NGX_ERROR
if (err != NGX_EADDRINUSE)
return NGX_ERROR;
// 绑定失败的错误是NGX_EADDRINUSE即EADDRINUSE时, 关闭套接字
if (ngx_close_socket(s) == -1)
ngx_log_error(NGX_LOG_EMERG, log, ngx_socket_errno,
ngx_close_socket_n " %s failed",
ls[i].addr_text.data);
failed = 1;
continue;
}
// 调用listen使套接字转变为一个被动套接字, backlog由ls[i]的backlog成员指定
if (listen(s, ls[i].backlog) == -1) {
ngx_log_error(NGX_LOG_EMERG, log, ngx_socket_errno,