看了skynet加载配置的地方,竟然发现好多知识点,记录下.
skynet的配置文件是通过命令行传入到进程的,这个比较明了. 仔细读了一下这块代码,发现有不少细节,而且,有些lua函数竟然很少用过,下面就这块代码分析一下.
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;
}
加载配置文件通过luaL_loadstring,lua_pushstring,lua_pcall三个lua api来完成.
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的参数是...,其实是通过lua_pushstring(config_file)传递进去的.lua_pcall就是执行这段代码,并有一个返回值.
执行在上面的代码,读取配置文件的内容. string.gsub()用来查找形如'XXX',一般是系统的环境变量,例如HOME,然后用getenv()函数执行的结果来替换. 这一点有时也挺有用,比如写路径时可以直接引用系统的变量路径值.
再后面就是load,他的作用是把code字符串作为一个函数体,然后执行.这样说还是不清楚,我们看下一个例子:
b = 100
f = load('return b')
print(f())
f就相当于f = function() return b end,区别是如果b是个local变量,那么在load中则不可见.
第二个参数'=(load)'在错误消息和调试消息中,用于代码块的名字。 如果不提供此参数,它默认为字符串chunk 。 chunk 不是字符串时,则为 "=(load)" 。字符串't'用于控制代码块是文本还是二进制(即预编译代码块)。 它可以是字符串 "b" (只能是二进制代码块), "t" (只能是文本代码块), 或 "bt" (可以是二进制也可以是文本)。 默认值为 "bt"。result用来接收函数的上值. 所谓上值就是函数里的全局变量,即配置文件里的形如'thread = 8'的值,都保存在result里, 并且返回result,供下面的c环境中使用.
接下来就是将上面的result通过skynet_setenv写入到全局的lua虚拟机中.这样全局虚拟机中就有了配置文件中的键值对.skynet_start()函数有个skynet_config类型的参数,他是通过optint,optstring函数从前面说的全局lua虚拟机中获取的. 就这样skynet入口获取到了配置文件中的值.