看了skynet加載配置的地方,竟然發(fā)現(xiàn)好多知識(shí)點(diǎn),記錄下.
skynet的配置文件是通過(guò)命令行傳入到進(jìn)程的,這個(gè)比較明了. 仔細(xì)讀了一下這塊代碼,發(fā)現(xiàn)有不少細(xì)節(jié),而且,有些lua函數(shù)竟然很少用過(guò),下面就這塊代碼分析一下.
int
main(int argc, char *argv[]) {
const char * config_file = NULL ;
if (argc > 1) {
config_file = argv[1];
} else {
fprintf(stderr, "Need a config file. Please read skynet wiki : https://github.com/cloudwu/skynet/wiki/Config\n"
"usage: skynet configfilename\n");
return 1;
}
luaS_initshr();
skynet_globalinit();
skynet_env_init();
sigign();
struct skynet_config config;
struct lua_State *L = lua_newstate(skynet_lalloc, NULL);
luaL_openlibs(L); // link lua lib
int err = luaL_loadstring(L, load_config);
assert(err == LUA_OK);
lua_pushstring(L, config_file);
err = lua_pcall(L, 1, 1, 0);
if (err) {
fprintf(stderr,"%s\n",lua_tostring(L,-1));
lua_close(L);
return 1;
}
_init_env(L);
config.thread = optint("thread",8);
config.module_path = optstring("cpath","./cservice/?.so");
config.harbor = optint("harbor", 1);
config.bootstrap = optstring("bootstrap","snlua bootstrap");
config.daemon = optstring("daemon", NULL);
config.logger = optstring("logger", NULL);
config.logservice = optstring("logservice", "logger");
lua_close(L);
skynet_start(&config);
skynet_globalexit();
luaS_exitshr();
return 0;
}
加載配置文件通過(guò)luaL_loadstring,lua_pushstring,lua_pcall三個(gè)lua api來(lái)完成.
luaL_loadstring(load_config)加載一段lua字符串代碼,整理后如下:
local f = assert(io.open(...))
local code = assert(f:read('*a'))
local function getenv(name)
return assert(os.getenv(name), 'os.getenv() failed: config')
end
code = string.gsub(code, '%$([%w_%d]+)', getenv)
f:close()
local result = {}
assert(load(code, '=(load)', 't', result))()
return result
我們注意到io.open的參數(shù)是...,其實(shí)是通過(guò)lua_pushstring(config_file)傳遞進(jìn)去的.lua_pcall就是執(zhí)行這段代碼,并有一個(gè)返回值.
執(zhí)行在上面的代碼,讀取配置文件的內(nèi)容. string.gsub()用來(lái)查找形如'XXX',一般是系統(tǒng)的環(huán)境變量,例如HOME,然后用getenv()函數(shù)執(zhí)行的結(jié)果來(lái)替換. 這一點(diǎn)有時(shí)也挺有用,比如寫(xiě)路徑時(shí)可以直接引用系統(tǒng)的變量路徑值.
再后面就是load,他的作用是把code字符串作為一個(gè)函數(shù)體,然后執(zhí)行.這樣說(shuō)還是不清楚,我們看下一個(gè)例子:
b = 100
f = load('return b')
print(f())
f就相當(dāng)于f = function() return b end,區(qū)別是如果b是個(gè)local變量,那么在load中則不可見(jiàn).
第二個(gè)參數(shù)'=(load)'在錯(cuò)誤消息和調(diào)試消息中仗阅,用于代碼塊的名字。 如果不提供此參數(shù)章钾,它默認(rèn)為字符串chunk 。 chunk 不是字符串時(shí)视搏,則為 "=(load)" 歹茶。字符串't'用于控制代碼塊是文本還是二進(jìn)制(即預(yù)編譯代碼塊)。 它可以是字符串 "b" (只能是二進(jìn)制代碼塊)穆壕, "t" (只能是文本代碼塊)死陆, 或 "bt" (可以是二進(jìn)制也可以是文本)招拙。 默認(rèn)值為 "bt"。result用來(lái)接收函數(shù)的上值. 所謂上值就是函數(shù)里的全局變量,即配置文件里的形如'thread = 8'的值,都保存在result里, 并且返回result,供下面的c環(huán)境中使用.
接下來(lái)就是將上面的result通過(guò)skynet_setenv寫(xiě)入到全局的lua虛擬機(jī)中.這樣全局虛擬機(jī)中就有了配置文件中的鍵值對(duì).skynet_start()函數(shù)有個(gè)skynet_config類型的參數(shù),他是通過(guò)optint,optstring函數(shù)從前面說(shuō)的全局lua虛擬機(jī)中獲取的. 就這樣skynet入口獲取到了配置文件中的值.