浏览 427
图一:nginx 启动及内存申请过程分析
任何程序都离不开启动和配置解析。ngx 的代码离不开 ngx_cycle_s 和
内存申请过程分为 3 步
内存分配过程图解如下
(图片来自网络)
为了更好理解上面的图,可以参看文末附 2 的几个数据结构:
知道了这两个核心数据结构之后,我们正式进入 main 函数,
在
图二:master 进程工作原理及工作工程
以下过程都在
master 进程工作过程
图三:worker 进程工作原理
启动通过执行
接下来是
master 和
Nginx 事件机制介绍
先看几个主要方法
n = sendmsg(s, &msg, 0);
Top of Form
Bottom of Form
接下来分析事件模块工作流程
ngx_event模块结构
ngx_events_module 的数据结构如下:
ngx_module_t
ngx_events_module = {
NGX_MODULE_V1,
&ngx_events_module_ctx, /* module context */
ngx_events_commands, /* module directives */
NGX_CORE_MODULE, /*
module type */
NULL, /* init master */
NULL, /* init module */
NULL, /* init process */
NULL, /* init thread */
NULL, /* exit thread */
NULL, /* exit process */
NULL, /* exit master */
NGX_MODULE_V1_PADDING
};
ngx_event 模块初始化
static ngx_command_t
ngx_events_commands[] = {
{
ngx_string("events") ,
NGX_MAIN_CONF|NGX_CONF_BLOCK|NGX_CONF_NOARGS ,
ngx_events_block, 0, 0,
NULL
},
ngx_null_command
};
通过
ngx_core_event模块初始化
ngx_core_event_module 是在
for (i = 0;
ngx_modules[i]; i++) {
if
(ngx_modules[i]->init_module)
if
(ngx_modules[i]->init_module(cycle) != NGX_OK)
exit(1);
}
}
}
我们先来看下
ngx_module_t
ngx_event_core_module = {
NGX_MODULE_V1,
&ngx_event_core_module_ctx, /* module context */
ngx_event_core_commands, /* module directives */
NGX_EVENT_MODULE, /*
module type */
NULL, /* init master */
ngx_event_module_init, /* init module */
ngx_event_process_init,
/* init process */
NULL, /* init thread */
NULL, /* exit thread */
NULL, /* exit process */
NULL, /* exit master */ NGX_MODULE_V1_PADDING
};
ngx_event_module_init 实现了初始化过程,该过程分以下几个步骤:
事件进程初始化
在工作线程初始化的时候,将会调用 ngx_event_process_init:
for (i = 0;
ngx_modules[i]; i++)
if
(ngx_modules[i]->init_process)
if (ngx_modules[i]->init_process(cycle) == NGX_ERROR) { /*fatal */
exit(2);
}
}
}
ngx_event_process_init 该过程分以下几步:
ngx_process_events_and_timers 事件处理开始工作
工作流程如下:
ngx 定时器实现
ngx 的定时器利用了红黑树的实现
ngx 惊群处理
accept_mutex 解决了惊群问题,虽然linux的新内核已经解决了这个问题,但是ngx 是为了兼容。
整体原理图:
Nginx 配置解析
再补充一下配置解析,Nginx 配置解析最大的亮点是用一个三级指针和 ctx 关联了起来,然后每个模块关注各自的配置专注解析和初始化就行了。
配置文件解析
ngx 在
·
并且把根据模块号存入了 cycle
这个函数一共有以下几个过程:
struct ngx_conf_s {
char *name;
ngx_array_t *args;
ngx_cycle_t *cycle;
ngx_pool_t *pool;
ngx_pool_t *temp_pool;
ngx_conf_file_t *conf_file;
ngx_log_t *log;
void *ctx;
ngx_uint_t module_type;
ngx_uint_t cmd_type;
ngx_conf_handler_pt handler;
char *handler_conf;
};
rv = ngx_conf_parse(cf,
NULL) ; 在初始化完 http 的上下文之后,继续进行内部的解析逻辑。这样就会调用到 ngx_conf_handler 的下面部分逻辑:
core 模块将会按照配置项的值在这个阶段进行初始化。ngx 的配置架构如下:
整体架构
serv_conf 结构
loc_conf 结构
附1:Nginx 主要数据结构
我们可以参考
6 . ngx_hash_t
ngx 的
ngx 对内存非常扣,假设了 hash 表不会占用太多的数据和空间,所以采用了这样的方式。
附2:内存分配的数据结构
ngx_pool_s 是
struct ngx_pool_s {
ngx_pool_data_t d ; // 数据块
size_t max ; // 小块内存的最大值
ngx_pool_t *current ; //
指向当前内存池
ngx_chain_t *chain ;
ngx_pool_large_t *large;
// 分配大块内存用,即超过max的内存请求
ngx_pool_cleanup_t *cleanup ; // 挂载一些内存池释放的时候,同时释放的资源
ngx_log_t *log;
} ;
ngx_pool_data_t 数据结构:
typedef struct {
u_char *last ; // 当前数据块分配结束位置
u_char *end ; // 数据块结束位置
ngx_pool_t *next ; // 链接到下一个内存池
ngx_uint_t failed ; // 统计该内存池不能满足分配请求的次数
} ngx_pool_data_t ;
然后我们结合
void * ngx_palloc (ngx_pool_t *pool, size_t size) {
u_char *m; ngx_pool_t *p
;
if (size <=
pool->max) {
p = pool->current ;
do {
m =
ngx_align_ptr(p->d.last, NGX_ALIGNMENT) ;
if ((size_t) (p->d.end - m) >= size) {
p->d.last = m + size ;
return m ;
}
p = p->d.next ;
} while (p) ;
return ngx_palloc_block(pool, size) ;
}
return ngx_palloc_large(pool, size) ;
}
ngx_cycle_s 每个工作进程都会维护一个:
struct ngx_cycle_s {
void ****conf_ctx ; // 配置上下文数组
ngx_pool_t *pool ; // 内存池
ngx_log_t *log ; // 日志
ngx_log_t new_log ;
ngx_connection_t **files ; // 连接文件
ngx_connection_t *free_connections ; // 空闲连接
ngx_uint_t free_connection_n ; // 空闲连接个数
ngx_queue_t reusable_connections_queue
;
ngx_array_t listening ; // 监听数组
ngx_array_t pathes ; // 路径数组
ngx_list_t open_files ; // 打开文件链表
ngx_list_t shared_memory ; // 共享内存链表
ngx_uint_t connection_n ; // 连接个数
ngx_uint_t iles_n ; // 打开文件个数
ngx_connection_t *connections ; // 连接
ngx_event_t *read_events ; // 读事件
ngx_event_t *write_events ; // 写事件
ngx_cycle_t *old_cycle; //old cycle指针
ngx_str_t conf_file; //配置文件
ngx_str_t conf_param; //配置参数
ngx_str_t conf_prefix; //配置前缀
ngx_str_t prefix; //前缀
ngx_str_t lock_file; //锁文件
ngx_str_t hostname; //主机名
};
附3:Nginx 内存管理 & 内存对齐
内存的申请最终调用的是 malloc 函数,
pool = ngx_create_pool(cscf->request_pool_size, c->log) ;
if (pool == NULL) {
return NULL;
}
r = ngx_pcalloc(pool, sizeof(ngx_http_request_t));
if (r == NULL) {
ngx_destroy_pool(pool) ;
return NULL ;
}
r->pool = pool ;
在
数据的对齐 ( alignment ) 是指数据的地址和由硬件条件决定的内存块大小之间的关系。一个变量的地址是它大小的倍数的时候,这就叫做自然对齐 ( naturally aligned )。例如,对于一个
预对齐内存的分配在大多数情况下,编译器和 C 库透明地帮你处理对齐问题。POSIX 标明了通过
调用
指针对齐:
例如:计算宏
进行内存池管理的时候,对于小于64字节的内存,给分配64字节,使之总是
由于公众号文章篇幅关系,以上就是陈科分享的 nginx 源码分析前半部分,关注本公众号可收到后半部分内容。
按点赞数排序
按时间排序