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函数 * 开始断开的) */
相关文章推荐
- Nginx源码分析---hash结构ngx_hash_t(v1.0.4)
- 第二人生的源码分析(101)脚本的初步知识
- Nginx源码分析---数组结构ngx_array_t
- nginx源码分析—链表结构ngx_list_t
- Struts2源码分析 初步2--Dispatcher初始化细节(1)
- Openshift源码与运作方式初步分析
- Nginx源码分析---队列结构ngx_queue_t
- nginx源码分析—数组结构ngx_array_t
- Nginx源码分析---内存池结构ngx_pool_t及内存管理
- 第二人生的源码分析(101)脚本的初步知识
- nginx源码分析—链表结构ngx_list_t
- nginx源码分析—内存池结构ngx_pool_t及内存管理
- Nginx源码分析---链表结构ngx_list_t
- Android Jamendo开源在线音乐播放器源码分析一 jamendo初步认识
- 第二人生的源码分析(101)脚本的初步知识
- Linux网桥源码框架分析初步
- nginx源码分析—hash结构ngx_hash_t(v1.0.4)
- Nginx 源码分析-- ngx_array、ngx_list基本数据结构
- nginx源码分析—内存池结构ngx_pool_t及内存管理
- 微软AJax.net源码初步分析(1)--序言