1、以下是我閱讀nginx(1.13.2版本)源碼的一些心得,個人覺得學一個東西最好學它的思想,學會舉一反三。因爲互聯網的東西實在太多 了。
上一章我對nginx啓動流程做了一個大概的描述,這一張我詳細進入初始化的核心方法進行探索。文章尾部會附上一張nginx中核心結構體
nginx_cycle_s的圖,方便在閱讀源代碼的時候進行對比和理解。
函數名稱:
ngx_cycle_t * ngx_init_cycle(ngx_cycle_t *old_cycle)
//第一步:聲明這些變量,變量的意義在初始化的時候進行說明
void *rv;
char **senv;
ngx_uint_t i, n;
ngx_log_t *log;
ngx_time_t *tp;
ngx_conf_t conf;
ngx_pool_t *pool;
ngx_cycle_t *cycle, **old;
ngx_shm_zone_t *shm_zone, *oshm_zone;
ngx_list_part_t *part, *opart;
ngx_open_file_t *file;
ngx_listening_t *ls, *nls;
ngx_core_conf_t *ccf, *old_ccf;
ngx_core_module_t *module;
char hostname[NGX_MAXHOSTNAMELEN];
//第二步:開始初始化這些變量把指針賦值給cycle結構體
ngx_timezone_update();
ngx_time_update(); //調用ngx_timezone_update()更新時區,調用ngx_time_update()更新時間
pool = ngx_create_pool(NGX_CYCLE_POOL_SIZE, log);//申請一個空間內存池,NGX_CYCLE_POOL_SIZE的值爲16*1024
cycle = ngx_pcalloc(pool, sizeof(ngx_cycle_t));//從內存池中申請內存存放cycle結構體,這個是核心的結構體
//這些已經在main函數中獲取到的值直接賦值給新的核心結構體cycle,不需要再次去解析
cycle->pool = pool;
cycle->log = log;
cycle->old_cycle = old_cycle;
cycle->conf_prefix.len = old_cycle->conf_prefix.len;
cycle->conf_prefix.data = ngx_pstrdup(pool, &old_cycle->conf_prefix);
cycle->prefix.len = old_cycle->prefix.len;
cycle->prefix.data = ngx_pstrdup(pool, &old_cycle->prefix);
cycle->conf_file.len = old_cycle->conf_file.len;
cycle->conf_file.data = ngx_pnalloc(pool, old_cycle->conf_file.len + 1);
cycle->conf_param.len = old_cycle->conf_param.len;
cycle->conf_param.data = ngx_pstrdup(pool, &old_cycle->conf_param);
ngx_array_init(&cycle->paths, pool, n, sizeof(ngx_path_t *);//初始化一個大小爲n,可動態擴展的數組,cycle的paths用來存儲的是路徑,比如配置很多配置文件的路徑
ngx_array_init(&cycle->config_dump, pool, 1, sizeof(ngx_conf_dump_t);
ngx_rbtree_init(&cycle->config_dump_rbtree, &cycle->config_dump_sentinel, ngx_str_rbtree_insert_value);//紅黑樹,平衡二叉樹一種,用來坐二分搜索用的
ngx_list_init(&cycle->open_files, pool, n, sizeof(ngx_open_file_t); //打開文件的list,具體list結構參考:http://blog.csdn.net/chen19870707/article/details/40400719
ngx_list_init(&cycle->shared_memory, pool, n, sizeof(ngx_shm_zone_t);//共享內存的初始化
ngx_array_init(&cycle->listening, pool, n, sizeof(ngx_listening_t);//連接監聽的數組,具體結構附錄圖
ngx_queue_init(&cycle->reusable_connections_queue);//隊列初始化,這個結構體比較簡單就兩個屬性, prev,next
cycle->conf_ctx = ngx_pcalloc(pool, ngx_max_module * sizeof(void *));//給每個模塊都分配一個配置屬性指針,後面nginx.conf以及其他配置文件中的配置會放到這裏
第三步:for循環初始化核心的module,讀取配置文件,給cycle中已經初始化了的變量進行賦值。
1、函數名稱:
ngx_int_t ngx_cycle_modules(ngx_cycle_t *cycle){
cycle->modules = ngx_pcalloc(cycle->pool, (ngx_max_module + 1) * sizeof(ngx_module_t *));//申請內存空間,存放module的結構體
ngx_memcpy(cycle->modules, ngx_modules, ngx_modules_n * sizeof(ngx_module_t *));//把全局靜態變量的內存信息拷貝過來,這個ngx_modules是在編譯的時候確定好的,即你安裝的時候指定需要使用的擴展,具體可以查看上一章的內容
cycle->modules_n = ngx_modules_n; //賦值擴展的數量
}
2、核心模塊配置信息預處理:
for (i = 0; cycle->modules[i]; i++) {
if (cycle->modules[i]->type != NGX_CORE_MODULE) {//非核心模塊不處理
continue;
}
module = cycle->modules[i]->ctx; //模塊的上下文
if (module->create_conf) {
rv = module->create_conf(cycle);
if (rv == NULL) {
ngx_destroy_pool(pool);
return NULL;
}
cycle->conf_ctx[cycle->modules[i]->index] = rv;
}
}
conf.ctx = cycle->conf_ctx;
conf.cycle = cycle;
conf.pool = pool;
conf.log = log;
conf.module_type = NGX_CORE_MODULE;
conf.cmd_type = NGX_MAIN_CONF;
3、準備了這些之後就可以解析配置文件了:
if (ngx_conf_param(&conf) != NGX_CONF_OK) {
environ = senv;
ngx_destroy_cycle_pools(&conf);
return NULL;
}
if (ngx_conf_parse(&conf, &cycle->conf_file) != NGX_CONF_OK) {
environ = senv;
ngx_destroy_cycle_pools(&conf);
return NULL;
environ = senv;
ngx_destroy_cycle_pools(&conf);
return NULL;
}
if (ngx_conf_parse(&conf, &cycle->conf_file) != NGX_CONF_OK) {
environ = senv;
ngx_destroy_cycle_pools(&conf);
return NULL;
}
第一個if解析nginx命令行參數’-g’加入的配置。第二個if解析nginx配置文件。好的設計就體現在接口極度簡化,模塊之間的耦合非常低。這裏只使用區區10行完成了配置的解析。在這裏,我們先淺嘗輒止,具體nginx如何解析配置,我們將在後面的小節做細緻的介紹。-g是指在命令行加入臨時的配置信息,下一章直接解析ngx_conf_parse的詳細實現,分析是如何對配置文件進行解析的。
附圖(從網上找的,自己畫太麻煩):