您的位置:首页 > 其它

ngx的初步源码分析1

2014-12-04 09:43 302 查看
typedef struct ngx_list_part_s ngx_list_part_t;

struct ngx_list_part_s {
void *elts;
ngx_unit_t nelts;
ngx_list_part_t *next;
};

typedef struct {
ngx_list_part_t *last;
ngx_list_part_t part;
size_t size;
ngx_unit_t nalloc;
ngx_pool_t *pool;
}ngx_list_t;

{
ngx_list_t *testlist=ngx_list_create(r->pool,4,sizeof(ngx_str_t));
if(testlist==NULL){
return NGX_ERROR;
}
}

{
ngx_str_t*str=ngx_list_push(testlist);
if(str==NULL){
return NGX_ERROR;
}
str->len=sizeof("hello,Wordld");
str->value="hello world";
}
/*遍历ngx_list_t结构*/
{
ngx_list_part_t *part=&testlist.part;
ngx_str_t*str=part->elts;
for(i=0;;i++){
if(i>=part->netls){
if(part->next==NULL){
break;
}
part=part->next;
str=part->elts;
i=0;
}
printf("list elment:%*s\n",str[i].len,str[i].data);
}
}

{
ngx_list_part_t*part=&r->headers_in.headers.part;
ngx_table_elt_t*header=part->elts;

for(i=0;;i++){
if(i>=part->nelts){
if(part->next==NULL)
break;
part=part->next;
header=part->elts;
i=0;
}
if(header[i].hash==0){
continue;
}
if(0==ngx_strncasecmp(header[i].key.data,
(u_char*)"Rpc-Description",
header[i].key.len)){
if(0==ngx_strncmp(header[i].value.data,
"uploadFile",
header[i].value.len)){
//找到了正确的头部,继续执行
}
}
}
}

/*在发送http响应包时,自己构造http头部时*/
{
ngx_table_elt_t*h=ngx_list_push(&r->headers_out.headers);
if(h==NULL){
return NGX_ERROR;
}
h->hask=1;
h->key.len=sizeof("TestHead")-1;
h->key.data=(u_char*) "TestHead";
h->value.len=sizeof("TestValue")-1;
h->value.data=(u_char*)"TestValue";
/*   这样会在响应行中新增一条HTTP首部:
TestHead: TestValued\r\n */
}

/*ngx_buf_t结构的用法*/
{
struct ngx_http_request_s {
...
ngx_pool_t *pool;
...
};
/*从r相关联的缓冲池中分配出一个ngx_buf_t结构*/
ngx_buf_t*b=ngx_pcalloc(r->pool,sizeof(ngx_buf_t));

/*此时已经有buf结构了,现在来完善buf结构,
* 因为要往里面放数据的,故需要空间,
* 这里从pool中分出128字节的空间
*/
b->start=(u_char*)ngx_pcalloc(r->pool,128);
b->pos=b->start;
b->last=b->start;
b->end=b->last+128;
b->temporary=1;

/*还有一种方法可以直接生成ngx_buf_t结构体和它指向的
* 缓冲空间*/
/*ngx_buf_t *b=ngx_create_temp_buf(r->pool,128);
*这句可以当作前面的6句
*/

/*这时候就可以向b中写入数据了,且
* b->last指向数据的末尾
*/
/*最后把上面的ngx_buf_t*b用ngx_chain_t传给ngx_http_output_filter
* 方法就可以发送http响应的包体内容了
*/
ngx_chain_t out;
out.buf=b;
out.next=NULL;
return ngx_http_output_filter(r,&out);
}

/*上面是从内存中发送内容,下面的方法可以直接从磁盘
* 中发送文件而不经过用户态内存
*/
{
ngx_buf_t *b;
b=ngx_palloc(r->pool,sizeof(ngx_buf_t));

u_char*filename=(u_char*)"/tmp/test.txt";
b->in_file=1; /*表明此buf关联文件*/

b->file=ngx_pcalloc(r->pool,sizeof(ngx_file_t));
b->file->fd=ngx_open_file(filena,e,NGX_FILE_RDONLY|NGX_FILE_NONBLOCK,
NGX_FILE_OPEN,0);
b->file->log=r->connection->log;
b->file->name.data=filename;
b->file->name.len=sizeof(filename)-1;
if(b->file->fd<=0){
return NGX_HTTP_NOT_FOUND;
}

if(ngx_file_info(filename,&b->file->info)==NGX_FILE_ERROR){
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}

r->headers_out.content_length_n=b->file->info.st_size;
b->file_pos=0;
b->file_last=b->file->info.st_size;

ngx_chain_t out;
out.buf=b;
out.next=NULL;
return ngx_http_output_filter(r,&out);

}

/*HTTP配置模型*/

当nginx检测到http{...}这个配置项时候,HTTP配置模型就启动了,
这时候会首先建立一个ngx_http_conf_ctx_t结构

typedef struct {
void **main_conf;
/*指针数组,数组中的每个元素指向所有http模块create_main_conf方法
* 产生的结构体
* 即在自定义模块的时候,在ctx结构中完成的方法所返回的
* 生成的保存配置信息的结构体指针
*/
void **srv_conf;
void **loc_conf;//同上
}ngx_http_conf_ctx_t;
/*下面看如何实现*/
{
ngx_http_module_t	*module;
ngx_http_conf_ctx_t*ctx;
ctx=ngx_pcalloc(cf->pool,sizeof(ngx_http_conf_ctx_t));
if(ctx==NULL)
return NGX_CONF_ERROR;
ctx->loc_conf=ngx_pcalloc(cf->pool,sizeof(void*)*ngx_http_max_module);
/*生成数组,保存指针*/
if(ctx->loc_conf==NULL)
return NGX_CONF_ERROR;
for(m=0;ngx_modules[m];m++){
if(ngx_module[m]->type!=NGX_HTTP_MODULE)
continue;
module=ngx_modules[m]->ctx;
mi=ngx_module[m]->ctx_index;

if(module->create_loc_conf){
ctx->loc_conf[mi]=module->create_loc_conf(cf);
/*这里调用了模块的create_loc_conf函数*/
if(ctx->loc_conf[mi]==NULL)
return NGX_CONF_ERROR;
}
}
}

/*在事件的代表模块ngx_core_event_module中,
* 其ngx_event_process_init函数
* 完成了大量的初始化工作,包括初始化
* 连接池
*/
static ngx_int_t
ngx_event_process_init(ngx_cycle_t*cycle)
{
ngx_uint_t		m,i;
ngx_event_t		*rev,*wev;
ngx_listening_t	*ls;
ngx_connection_t	*c,*next,*old;
ngx_core_conf_t	*ccf;
ngx_event_conf_t	*ecf;
ngx_event_module_t	*module;

ccf=();

}

/*在epoll模块中,看看是怎样利用epoll的*/

static ngx_int_t
ngx_epoll_init(ngx_cycle_t *cycle,ngx_msec_t timer)
{
ngx_epoll_conf_t *epcf;
epcf=ngx_event_get_conf(cycle->conf_ctx,ngx_poll_module);
/*先得到ngx_poll_moudle创建的自身的配置结构*/

if(ep==-1){/*ep是在此文件中的static变量,它
全程用来做epoll的描述符
*/
ep=epoll_create(cycle->connection_n/2);
if(ep==-1){
return NGX_ERROR;
}
}

if(nevents<epcf->events){/*nevents也是static全局变量,
这里表示需要处理的事件总数,
这个epcf->events表示的就是
在配置结构中写的数字
*/
if(events_list){
ngx_free(event_list);/*events_list表示的是用户态
存放返回事件的数组,也是static的
*/
}
event_list=ngx_alloc(sizeof(struct epoll_event)*epcf->events,
cycle->log);
if(event_list==NULL){
return NGX_ERROR;
}

}
nevents=epcf->events;

ngx_event_action=ngx_poll_module_ctx.actions;/*ngx_event_action
是全局变量,它
简单地复制了这里
的actions,这样
操作更方便
*/
ngx_event_flags=NGX_USE_CLEAR_EVENT;/*ET模式,
ngx_event_flags为
全局变量
*/
return NGX_OK;
}
/*这样,init就建立了epoll句柄,event_list数组,nevents最大处理数目
* 还初始化了两个全局变量ngx_event_action ,ngx_event_flags
*/

/*下面看ngx_epoll_module是如何ctl添加fd的*/
static ngx_int_t
ngx_epoll_add_event(ngx_event_t *ev,ngx_int_t event,ngx_uint_t flags)
{
/*ev表示要添加的事件,(在普通的epoll编程中,
* 一般直接传递fd添加进去,在nginx中它把fd封装在了事件中了
* event表示其事件类型(IN,OUT之类)
* flags应该是全局变量ngx_epoll_flags的值
*/
int 		op;
uint32_t 		events,prev;
ngx_event_t         *e;
ngx_connection_t	*c;
struct epoll_event	ee; /*用于在ctl中的最后一个参数,
即事件
*/
c=ev->data;/*事件ev的data成员一般指向一个connection连接*/
events=(uint32_t)event;/*复制事件类型*/

/*为了避免把同一个fd两次加入到epoll中,nginx这里处理:
* 如果是读事件,那么,我先看看这个fd的写事件的状态,
* 如果写事件的状态是active的,那么表明这个fd是已经加入
* 了epoll的,这时候我只需要简单的mod一下事件的类型即可
* 否则,就加入(表明还没有加入过fd).
* 同理,写事件也一样
*/
if(event==NGX_READ_EVENT){
e=c->write;
prev=EPOLLOUT;
} else {
e=c->read;
prev=EPOLLIN;
}

if(e->active){  /*表明fd已经加入epoll,不需要重复加入,
这里op就定义为mod,事件类型修改(添加读或写)
*/
op=EPOLL_CTL_MOD;
events|=prev;
}else{
op=EPOLL_CTL_ADD;
}

ee.events=events|(uint32_t)flags;
ee.data.ptr=(void*)((uintptr_t)c|ev->instance);/*ptr指向
事件所对应的
connection结构体
且利用地址最有一位为
0 的事实来存放了
instance变量
*/
if(epoll_ctl(ep,op,c->fd,&ee)==-1){
return ENGX_ERROR;
}

ev->active=1;

return NGX_OK;
}

/*上面说了加入事件到epoll,del也和它的逻辑一样
* 下面说如何处理这些事件,即wait
*/
static ngx_int_t
ngx_epoll_process_events(ngx_cycle_t *cycle,ngx_msec_t timer,
ngx_uint_t flags)
{
int 		events;
uint32_t 		revents;
ngx_int_t		instance,i;
ngx_uint_t		level;
ngx_err_t		err;
ngx_event_t		*rev,*wev,**queue;
ngx_connection_t	*c;

events=epoll_wait(ep,event_list,(int)nevents,timer);
/*收集事件*/

ngx_mutex_lock(ngx_posted_events_mutext);/*加锁*/

for(i=0;i<events;i++){
c=event_list[i].data.ptr;/*在add时候,把事件所对应的connect赋值
给ptr了,所以,很多信息可以从
这里来获取
*/
instance=(uintptr_t)c&1;/*注意,这里获取和事件相关的instance标志,
来区分是否是过期事件,
这个instance的存储位置比较独特,
它是存储在c的地址的最后一位的,
这样的话,会不会破坏了c的地址呢?
首先要知道c是一个大的结构体,它在内存
中的存储是要遵循内存对齐规则的,即
它的起始地址肯定是以2,4,或者8为倍数的
,这样,至少它的地址的最后一位一定为0,
那么这个位就可以利用起来存储instance,因为
反正为0嘛,这个过程是在add的时候完成的
*/
c=(ngx_connection_t*)((uintptr_t)c&(uintptr_t)~1);
/*这样就还原了c的地址,因为~1=1111111.....1110;*/

rev=c->read;/*取出读事件*/
if(c->fd==-1||rev->instance!=instance){
/*判断过期的方法
* 1,fd==-1;
* 2,现在的instance和刚刚取得的instance不相等额
* instance相当于一个开关,在ngx_get_connection
* 函数中可以看到
*
*/
continue;/*过期的事件不处理*/;
}
revents=event_list[i].events;/*取出事件类型*/

if((revents&EPOLLIN)&&rev->active){
/*如果是读类型且是活跃事件*/
if(flags&NGX_POST_EVENTS){
/*如果flags标志中有POST_THREAD,则需要延后处理*/
ngx_locked_post_event(rev,queue);
}else{
rev->handler(rev);/*调用其事件handler处理*/
}
}

wev=c->write;/*取出写事件*/
if((revents&EPOLLOUT)&&wev->active){
if(c->fd==-1||wev->instance!=instance){
continue;/*如果事件已经过期*/
}

if(flags&NGX_POST_EVENTS){
ngx_locked_post_event(wev,&ngx_posted_events);
}else{
wev->handler(wev);
}
}
}
ngx_mutex_unlock(ngx_posted_events_mutex);

return NGX_OK;
}

/*在event_core_module的初始化函数ngx_event_process_init中
* 完成了连接池connections的生成,读事件read_events,写事件
* write_events的生成,部分片段如下:
*/
static ngx_int_t
ngx_event_process_init(ngx_cycle_t*cycle)
{
......;

cycle->connections=
ngx_alloc(sizeof(ngx_connection_t)*cycle->connection_n,cycle->log);
if(cycle->connections==NULL)
return NGX_ERROR;

c=cycle->connections;

cycle->read_events=
ngx_alloc(sizeof(ngx_event_t)*cycle->connection_n,cycle->log);
if(cycle->read_events==NULL)
return NGX_ERROR;

rev=cycle->read_events;

cycle->write_events=
ngx_alloc(sizeof(ngx_event_t)*cycle->connection_n,cycle->log);
if(cycle->write_events==NULL)
return NGX_ERROR;

wev=cycle->write_events;

/*然后是要为每个监听套接字从connections数组分配一个连接*
* 首先看看cycle->listening字段是如何初始化的
* 在ngx_init_cycle函数中初始化listening字段
*/
{
/*ngx_init_cycle*/
cycle->listening.elts=ngx_pcalloc(pool,n*sizeof(ngx_listening_t));
if(cycle->listening.elts==NULL){
ngx_destroy_pool(pool);
reutrn NULL;
}
cycle->listening.netls=0;
cycle->listening.size=sizeof(ngx_listening_t);
cycle->listening.nalloc=n;
cycle->listen.pool=pool;
/*注意:listening字段是ngx_array_t动态数组类型
* 其中elts指向数组起始地址
* size为每个元素的大小
* netls为已经使用了多少个空间
*
* 在这个动态数组中,元素为ngx_listeing_t类型,
* 说简单点,就是存放监听套接字的,每个空间
* 放个监听套接字
*/
}

ngx_listening_t *ls=cycle->listenint.elts;
for(i=0;i<cycle->listening.netls;i++) {
/*这里 遍历listen数组,且只是到已经
* 有元素的地方
*/
c=ngx_get_connection(ls[i].fd,cycle->log);
/*这个函数即为从connections数组中取出一个连接,
* 即传入的参数之一为listening套接字的fd
* 看看这个函数如何实现的
*/
{
/*ngx_get_connection*/
ngx_get_connection(ngx_socket_t s,ngx_log_t *log)
{
ngx_connection_t *c;

c=ngx_cycle->free_connections;
if(c==NULL){
ngx_log_error();
}

ngx_cycle->free_connections=c->data;
ngx_cycle->free_connection_n--;

rev=c->read;
wev=c->write;

mngx_memzero(c,sizeof(ngx_connection_t));
/*把这个连接清空*/
c->read=rev;
c->write=wev;
c->fd=s;
c->log=log;/*重新给连接池赋值,只保留先前的read,write事件*/

instance=rev->instance;/*取出原先的标志*/

ngx_memzero(rev,sizeof(ngx_event_t));
ngx_memzero(wev,sizeof(ngx_event_t));

rev->instance=!instance;
wev->instance=!instance;

rev->data=c;
wev->data=c;

/*上面的一系列的工作可以总结为:
* 1,因为是获取新的连接,所以,该连接要被清洗,
* 达到效果:处理保留 读写事件的 联系外,其他都要清0
* 2,相应的读写事件:也要清洗,除了保留instance标志为
* 做判断过期标志外
* 3,最后,获取练级池使得连接和读写事件有了双向的指向。
* (在没有获取连接时,只是connection指向rev和wev,
* 这时候,rev和wev的data都指向了connection
*/

wev->write=1;
return c;
}
}//end of ngx_get_connection
/*上面的ngx_get_connection具体在哪机个地方调用呢?
* 1,创建listen socket的时候,需要和一个connection关联,
* 即上面的情况
* 2,当accept生成新的fd时候,需要建立连接,这个在ngx_event_accept
* 中调用。
* 3,也即一个fd对应着一个connection
*/
/*释放ngx_free_connection(ngx_connection_t*c)
*/
{
c->data=ngx_cycle->free_connections;
ngx_cycle->free_connections=c;
ngx_cycle->free_connection_n++;
if(ngx_cycle->files) {
ngx_cycle->files[c->fd]=NULL;
}
}//end of free connection;

/*紧接上面为listen套接字分配了一个connection*/

if(c==NULL)
return NGX_ERROR;
/*继续为申请到的connection赋值*/
c->log=&ls[i].log;
c->listening=&ls[i];
ls[i].connection=c;
rev=c->read;
rev->log=c->log;
rev->accept=1;

/*注册了监听套接口读事件的回调函数
* ngx_event_accept
*
* 这么看来,一个connect对应一个listen套接字
* 也对应这一个rev wev。
*/
rev->handler=ngx_event_accept;/*监听套接子的读事件处理函数,也即有连接到来*/

/*使用了accpet_mutex,即代表会有惊群现象发生,
* 那么暂时不将监听套接字放入epoll,而是等到
* worker抢到accept互斥体后,再放入epoll,避免惊群
*/
if(ngx_use_accept_mutex)
continue; /*为另外的listen套接字分配connection*/

if(ngx_event_flags&NGX_USE_RTSIG_EVENT) {
xxxx;budong;
} else {/*将监听套接子放入epoll中*/
if(ngx_add_event(rev,NGX_READ_EVENT,0)==NGX_ERROR)
return NGX_ERROR;
}

}

return NGX_OK;
}

/*ngx_event_accept建立连接*/
void ngx_event_accept(ngx_event_t*ev)
{
/*知道 事件 连接 listen中的其中一个,就能找到相应的另外两个*/

ngx_socket_t     s;
ngx_connection_t *c,*lc;
ngx_listening_t  *ls;
u_char	     sa[NGX_SOCKADDRLEN];/*存放外来sockaddr地址*/

lc=ev->data;
ls=lc->listening;

s=accept(lc->fd,(struct sockaddr*)sa,&socklen);

if(s==-1)
return ;

/*该全局变量主要是用来实现均衡负载的,
* 在该worker进程的所有连接数目的1/8-空闲连接数目
* >0时候,即空闲连接过小,那么放弃竞争accept锁
*/
ngx_accept_disabled=ngx_cycle->connection_n/8-
ngx_cycle->free_connection_n;

c=ngx_get_connection(s,ev->log);
/*为新生成的套接字分配一个连接*/

if(c==NULL) {
if( ngx_close(socket(s))==-1) {
ngx_log_error();
}
return ;
}

/*给新分配的连接分配资源*/
c->pool=ngx_create_poll(ls->pool_size,ev->log);/*创建pool池*/
c->sockaddr=ngx_palloc(c->pool,sockelen);
ngx_memecpy(c->sockaddr,sa,socklen);

log=ngx_palloc(c->pool,sizeof(ngx_log_t));
if(log==NULL)
return ;
/*set a blocking mode for aio and non-blocking mode for others*/
if() {

} else {
if(ngx_nonblocking(s)==-1) {
ngx_log_error(NGX_LOG_ALERT,ev->log,ngx_socket_errno,
ngx_nonblocking_n "falied");
ngx_close_accepted_connection(c);
return ;
/*设置为non-blocking模型*/
int ngx_nonblocking(ngx_socket_t s) {
int nb;
nb=1;
return ioctl(s,FIONBIO,&nb);
}
/*设置为blocking模型*/
int ngx_blocking(ngx_socket_t s) {
int nb;
nb=0;
return ioctl(s,FIONBIO,&nb);
}
}
}

/*继续给生成的连接赋值*/
*log=ls->log;

c->rev=ngx_recv;
c->send=ngx_send;
c->recv_chain=ngx_recv_chain;
c->send_chain=ngx_send_chain;

c->log=log;/*关于log,监听和连接不是使用的同一个log内存*/
c->pool->log=log;

c->socklen=socklen;
c->listening=ls;
c->local_sockaddr=ls->sockaddr;

......;

ngx_log_debug3();

.....;

ls->handler(c);/*这个listen handler很重要,它将完成新连接的最后
初始化工作,同时将accept到的最新连接放入到epoll中,
挂在这个handler伤的函数是 ngx_http_init_connection
在之后http模块中详细介绍
*/
}

/*在分析ngx_init_cycle之前,首先对old_cycle的赋值有个大概的
* 了解,因为ngx_init_cycle中,很多的值都是从old_cyle中得到的
* 如果没有这个过程,就不好理解
* old_cycle的赋值大部分都来自于函数ngx_process_options;
*/
static ngx_int_t
ngx_process_options(ngx_cycle_t *cycle)
{
u_char *p;
size_t len;

cycle->conf_prefix.data=p; //从静态变量ngx_prefix中获得的
cycle->prefix.data=p;

.....;//基本上conf_prefix,prefix,conf_file,conf_params这几个参数都是从
// 静态变量中获取的
}
/*分析ngx_init_cycle
* 这个函数绝对是重点,在启动nginx时候第一个执行的大函数
*/
ngx_cycle_t *
ngx_init_cycle(ngx_cyle_t *old_cycle)
{
void  		*rv;
ngx_log_t		*log;
ngx_uint 		i,n;
ngx_conf_t 		conf;
ngx_pool_t 		*pool;
ngx_cycle_t		*cycle,**old;
ngx_listening_t	*ls,*nls;
ngx_core_conf_t	*ccf,*old_ccf;
ngx_core_module_t	*module;

log=old_cycle->log;

pool=ngx_create_pool(NGX_CYCLE_POOL_SIZE,log);

pool->log=log;

cycle=ngx_pcalloc(pool,sizeof(ngx_cycle_t));

cycle->pool=pool;
cycle->log=log;
cycle->new_log.log_level=NGX_LOG_ERR;
cycle->old_cyle=old_cycle;

/*都从old_cycle那边复制过来(有的可能是新赋值的)*/
cycle->conf_prefix;
cycle->prefix;
cycle->conf_file;
cycle->conf_param;
cycle->pathes;
cycle->open_files;
cycle->shared_memory;
cycle->listening;
cycle->reusable_connections_queue;

cycle->listening.elts=ngx_pcalloc(pool,n*sizeof(ngx_listening_t));
cycle->listening.netls=0;
cycle->listening.size=sizeof(ngx_listening_t);
cycle->listening.nalloc=n;
cycle->listening.pool=poo;/*listening套接字数组的分配*/

cycle->conf_ctx=ngx_pcalloc(pool,ngx_max_module*sizeof(void*));

for(i=0;ngx_modules[i];i++) {
if(ngx_modules[i]->type!=NGX_CORE_MODULE) {
continue;
}

module=ngx_modules[i]->ctx; /*moudle是ngx_core_module_t类型
即为core类型模块的上下文类型
*/
if(module->create_conf) {
rv=module->create_conf(cycle);

cycle->conf_ctx[ngx_module[i]->index]=rv; /*这里把
结构体ngx_core_conf_t
的指针放入了ctx数组
的第一个位置
*/
}/*这里调用的core类型模块的create_conf函数,
这里开始追踪一下
起始core类型模块存在creae_conf函数的只有1个模块
即ngx_core_module,,这让我怀疑,ctx数组里面
好像只有1个指针,不管怎样,先看看它的create_conf实现吧
*/
/*
* 私以为,结构体ngx_core_conf_t是用来存放全局配置参数的
* 结构体,比如damon,worker_processes,user等
* static void *
* ngx_core_module_create_conf(ngx_cycle_t *cycle)
* {
* 	   ngx_core_conf_t *ccf;
* 	   ccf=ngx_pcalloc(cycle->pool,sizeof(ngx_core_conf_t));
* 	   if(ccf==NULL)
* 	     return NULL;
* 	   ccf->daemon=NGX_CONF_UNSET;
* 	   ccf->master=NGX_CONF_UNSET;
* 	   ccf->timer_resolution=NGX_CONF_UNSET_MSEC;
* 	   ccf->worker_process=NGX_CONF_UNSET;
* 	   ........;
*
* 	   return ccf;
*  }
*/
ngx_memzero(&conf,sizeof(ngx_conf_t));
/*这里初始化了一个ngx_conf_t的变量,这个变量后来
* 和cycle变量有着很多的联系
* 即这个conf变量
*/
/*初始化这个conf变量,其中很多成员都牵涉到cycle中去了*/
conf.args=ngx_array_create(pool,10,sizeof(ngx_str_t));
conf.temp_pool=ngx_create_pool(NGX_CYCLE_POOL_SIZE,log);
conf.ctx=cycle->conf_ctx;
conf.pool=pool;
conf.log=log;
conf.module_type=NGX_CORE_MODULE;
conf.cmd_type=NGX_MAIN_CONF;

/*敢肯定这两个函数设计到了很多细节,需要认真弄清*/
if(ngx_conf_param(&conf) !=NGX_CONF_OK) {
return NULL; //处理在命令行携带的参数,一般是-g选项的参数
}
if(ngx_conf_parse(&conf,&cycle->conf_file)!=NGX_CONF_OK) {
return NULL ;
}/*在ngx_init_cycle的ngx_conf_parse函数是个引擎,牵一发
而动全身
*/
/*
* 这里将完成对配置文件的解析工作,解析配置文件时候,将会
* 完成每个指令的set回调,这个set回调函数一般都是用于将
* 配置数据填写到配置结构中的。
* nginx通过解析配置文件,回调一些列的函数来完成各个模块的衔接
* 总之这部放是相当重要的一个初始化环节
*/
/*char *
* ngx_conf_param(ngx_conf_t *cf)
* { 传递过来cf,我觉得主要就是为了获得cycle
* 	char 		 	*rv;
* 	ngx_str_t		*param;
* 	ngx_buf_t		b;
* 	ngx_conf_file_t		conf_file;
*
* 	param=&cf->cycle->conf_param;
* 	ngx_memzero(&conf_file,sizeof(ngx_conf_file_t));
* 	ngx_memzero(&b,sizeof(ngx_buf_t));
* 	....;初始化b和conf_file。
* 	cf->conf_file=&conf_file
* 	cf->conf_file->buffer=&b; 给cf的成员赋值了,
* 	为了调用parse函数做进一步解析
* 	rv=ngx_conf_parse(cf,NULL);
* 	cf->conf_file=NULL;
* 	return rv;
* } //可以看出最终还是调用parse来解析的
* ngx_conf_parse有点复杂,没看懂...
* 在ngx_conf_parse中
* {
* 	for(;;) {
* 	    rc=ngx_conf_read_token(cf);
* 	    if(cf->handler) {
* 	    }
*
* 	    rc=ngx_conf_handler(cf,rc);
* 	    ..
* 	    }
*  } 主要就是看看ngx_conf_handler这个函数,这个函数
*  起到了重要着用
*/

for(i=0;ngx_modules[i];i++) {
if(ngx_modules[i]->type!=NGX_CORE_MODULE) {
continue;
}

module=ngx_modules[i]->ctx;

if(module->init_conf) {
if(module->init_conf(cycle,cycle->conf_ctx[ngx_modules[i]->index])
==NGX_CONF_ERROR)
{
return NULL;
}/*这里又只是ngx_core_module模块才实现了该方法
即初始化后面的参数,也即create_conf创建的
ngx_core_conf_t 结构体
*/
}
}

/*后面是文件创建open_files链表的初始化
* 共享内存shared_memory的初始化
*/
。。。。;

if(ngx_open_listening_sockets(cycle)!=NGX_OK) {
goto failed;
} /*遍历listening数组,打开所有的监听套接口(以此
进行socket,bind,listen),同时设置一些
socket选项以及文件描述符属性,如非阻塞
*/

for(i=0;ngx_modules[i];i++) {
if(ngx_modules[i]->init_module) {
if(ngx_modules[i]->init_module(cycle)!=NGX_OK) {
exit(1);
}
}
}/*执行所有模块的init_moudle操作,但你找遍所有的模块
发现绝大多数都没有这个方法(NULL),最终,有一个
你看到了,ngx_event_core_module模块,它的init_module
方法为ngx_event_module_init,
这个到事件驱动分析时候,在看
*/

}

static ngx_int_t
ngx_conf_handler(ngx_conf_t *cf,ngx_int_t last)
{
char 		*rv;
void 		*conf,**confp;
ngx_uint_t	i,multi;
ngx_str_t	*name;
ngx_command_t 	*cmd;

for(i=0;ngx_modules[i];i++) {
if(ngx_modules[i]->type!=NGX_CONF_MODULE
&&ngx_modules[i]->type!=cf->module_type)
//跳过不是conf或者core类型的模块
continue;
cmd=ngx_modules[i]->commands;
if(cmd==NULL)
continue;
for(;cmd->name.len;cmd++) {
if(name->len != cmd->name.len)
continue;
if(ngx_strcmp(name->data,cmd->name.data)!=0)
continue;
...;
rv=cmd->set(cf,cmd,conf);
...;
}
/*这里set调用有几个函数
* ngx_core_moudle 模块中有几个,但都比较简单
* ngx_errlog_moudle有一个,ngx_error_log
* ngx_conf_module有一个,ngx_conf_incoude
* ngx_events_module有一个,ngx_events_block
* 这个set就比较重要了,它是event的初始化引擎,
* 且在前面说的,cycle->ctx数组中,到目前为止
* 只有一个ngx_core_conf_t的指针,即ngx_core_module
* 模块的,那么现在,就要继续往ctx指针中加入指针了
* 而在event类型中,是这个函数完成的(指出的是ngx_event_module
* 模块本身的create_conf函数指针是为空的,它是调用其他模块来
* 填充的ctx数组
* ngx_http_module模块只有一个,ngx_http_block。
* 也比较复杂,基本上和上个有一拼。
*/

}

static char*
ngx_events_block(ngx_conf_t*cf,ngx_command_t*cmd,void*conf)
{

char 		*rv;
void		***ctx;//这个就是要插入cycle->ctx数组的指针
ngx_uint_t		i;
ngx_conf_t		pcf;
ngx_event_module_t	*m;

ngx_event_max_module=0;
for(i=0;ngx_modules[i];i++) {
if(ngx_modules[i]->type!=NGX_EVENT_MODULE) {
continue;
}
ngx_modules[i]->ctx_index=ngx_event_max_module++;

}

ctx=ngx_pcalloc(cf->pool,sizeof(void*));

*ctx=ngx_pcalloc(cf->pool,ngx_event_max_module*sizeof(void*));
/*分配ngx_event_max_module个指针的数组*/
*(void**)conf=ctx;/*这一步有着神奇的意义,
conf这里实际上和cycle->conf_ctx数组
相关连了,而这里的ctx是event类型模块
的conf的总入口,这个ctx没有指向conf结构
而是指向的指针数组,所以说,在cycle->conf_ctx
数组中,这时候就存放了两个指针了,一个
是先前ngx_core_module的指向结构ngx_core_conf_t
的结构指针,现在就多了一个这里的ctx指针
*/
for(i=0;ngx_modules[i];i++) {
if(ngx_modules[i]->type!=NGX_EVENT_MODULE) {
continue;
}
/*对每个模块的操作
* 1,取得上下文,调用create_conf生成配置结构体
* 2,调用解析上下文函数ngx_parse_conf,
* 为配置结构体从配置文件中赋值一些成员
* 这个函数可以递归嵌套地调用各类模块的下一级模块
* 3,再此取得上下文,调用init_conf再次对配置结构体
* 进行赋值
*/
m=ngx_modules[i]->ctx;/*取得event类型模块的上下文结构*/

if(m->create_conf) {
(*ctx)[ngx_modules[i]->ctx_index]=m->create_conf(cf->cycle);
}/*这里就把event类型模块的conf指针插入到了ctx数组中
(如ngx_event_core_module的ngx_event_conf_t结构
再如ngx_epoll_module的ngx_epoll_conf_t结构
等等
*/
pcf=*cf;/*这里是结构体的复制呀,把整个ngx_conf_t结构体
全部放入了pcf中
*/
/*还记得在ngx_init_cycle中,也对ngx_conf_t结构体进行赋值
* 这是为了解析配置文件,现在,这里也对配置文件解析,
* 不过这里解析的不同范围的配置文件,所以重新对cf赋值
*/
cf->ctx=ctx;
cf->module_type=NGX_EVENT_MODULE;
cf->cmd_type=NGX_EVENT_CONF;

rv=ngx_conf_parse(cf,NULL);/*开始解析event{}下的配置文件了
,这时候type=parse_block,
接着又进入了了ngx_conf_handler
(感觉好像是递归调用,这函数可以重入的
吗?),于是又调用相符和的commands的
set函数,这时候的模块类型变为了
NGX_EVENT_MODULE,这是通过这里的cf传递
过去的
*/
{
/*这里就是一个个调用event类型的模块的各个comands的set了
* 比如在ngx_event_core_module里面,又很多的set,
* 重要的是ngx_event_connnections,获取每个进程能够处理
* 的最大连接数,保存在cf->cycle->connection_n=ecf->connections
* 中
* 在ngx_epoll_module里面,set为ngx_conf_set_num_slot,
* 获取每个进程处理的最大事件数量,赋值给ngx_epoll_conf_t
* 里面的events变量
* (可以看到,生成结构体是在模块的create_conf函数,
* 给结构体赋值是在模块commnad结构中的各个set函数)
*/
}

/*解析完配置后,恢复cf*/
*cf=pcf;

/* 调用完set后,接着对各个event模块的配置结构进一步
* 初始化(在set中只是少量的赋值了部分成员)
* 即调用各个模块的init_conf函数
*/
for(i=0;ngx_modules[i];i++) {
if(ngx_modules[i]->type!=NGX_EVENT_MODULE)
continue;

m=ngx_modules[i]->ctx;

if(m->inif_conf) {
rv=m->init_conf(cf->cycle,(*ctx)[ngx_modules[i]->ctx_index]);
/*参数传递是cycle和其相应的配置结构体指针*/
}
}
}/*至此,event模块的解析工作已经做完*/
}

/*至此,开始的core模块中的set函数含有ngx_http_block是个大块头,
* 基本上工作流程和ngx_event_block差不多(可能还要复杂),
* 这里先不讲
* 然后回到ngx_init_cycle中(我们是从ngx_conf_parse函数
* 开始断开的)
*/
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: