skynet启动时读取配置文件
新入门skynet系列视频b站网址 https://www.bilibili.com/video/BV19d4y1678X
skynet启动时读取配置文件
行11 skynet启动时,我们启动了一个lua虚拟机
,这个虚拟机是在整个skynet生命周期中存在的。我们把他叫做skynet的环境虚拟机
。
行13 之后启动了一个临时虚拟机
,把配置信息里面的配置选项都读取出来,然后保存到skynet的环境虚拟机中。之后关闭这个临时虚拟机
void
skynet_env_init() {
E = skynet_malloc(sizeof(*E));
SPIN_INIT(E)
E->L = luaL_newstate();//启动一个环境虚拟机
}
int
main(int argc, char *argv[]) {
//...
skynet_env_init();
//...
struct lua_State *L = luaL_newstate();//启动一个临时虚拟机
luaL_openlibs(L); // link lua lib
int err = luaL_loadbufferx(L, load_config, strlen(load_config), "=[skynet config]", "t");
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);
}
我们下面看配置项都是怎么读取出来的。
行16 是 load_config表示的 字符串加载,然后编译成一个匿名函数
行18 是给这个匿名函数提供一个参数 参数就是我们skynet启动时提供的 配置文件的路径+文件名 比如 ./example/config
行20 是执行这个匿名函数 把配置项都存入一个表result中
行26 最终把这个result表中的键值对都保存到 环境虚拟机
中
我们先看 load_config 表示的lua代码
local result = {} --收集配置项 即键值对
local function getenv(name) return assert(os.getenv(name), [[os.getenv() failed: ]] .. name) end
local sep = package.config:sub(1,1) --sep是 /
local current_path = [[.]]..sep
local function include(filename)
local last_path = current_path
local path, name = filename:match([[(.*]]..sep..[[)(.*)$]])--这里表示字符串的形式是 [[字符串]]
if path then
if path:sub(1,1) == sep then --绝对路径 即path是 /xxx/yyy/zzz/
current_path = path
else
current_path = current_path .. path --相对路径 即path是 aaa/bbb/
end
else
name = filename
end
local f = assert(io.open(current_path .. name)) --打开配置文件
local code = assert(f:read [[*a]]) --读取
code = string.gsub(code, [[%$([%w_%d]+)]], getenv)--把系统的环境变量替换成对应的值
f:close() --注意下面的代码 每次都把键值对放到result中保存
assert(load(code,[[@]]..filename,[[t]],result))() --把配置文件编译成匿名函数并执行 注意环境
current_path = last_path
end
setmetatable(result, { __index = { include = include } })
local config_name = ...--这里就是我们启动skynet时的配置文件 比如 ./example/config
include(config_name) --开始处理配置文件
setmetatable(result, nil)
return result --最终返回配置项的集合
行5->行23 是我们主要讨论的读取配置文件的函数 local function include(filename) --dosomething end
我们给一个配置文件的样子
include(/one/two/config1) --这里是绝对路径
include(three/config2) --这里是config2是相对于当前配置文件的位置 也就是 ./three/config2
key1 = "xxxxx"
key2 = "yyyyy"
最终读取到全局虚拟机中的函数是 _init_env(L);
这个函数实际上就是把result里面的键值对遍历出来。
static void
_init_env(lua_State *L) {//L是临时的lua虚拟机
lua_pushnil(L); /* first key */ //此时-2的位置起始就是result表
while (lua_next(L, -2) != 0) {//此时已经把result表中的一对键值对读取出来,
int keyt = lua_type(L, -2);//key放在-2的位置
if (keyt != LUA_TSTRING) {
fprintf(stderr, "Invalid config table\n");
exit(1);
}
const char * key = lua_tostring(L,-2);//key放在-2的位置
if (lua_type(L,-1) == LUA_TBOOLEAN) {//如果value是lua的布尔类型
int b = lua_toboolean(L,-1);
skynet_setenv(key,b ? "true" : "false" );
} else {
const char * value = lua_tostring(L,-1);//value放在-1的位置
if (value == NULL) {
fprintf(stderr, "Invalid config table key = %s\n", key);
exit(1);
}
skynet_setenv(key,value);
}
lua_pop(L,1);
}
lua_pop(L,1);
}
我们看skynet_setenv(key,value);
就是最终把键值对保存到环境虚拟机
void
skynet_setenv(const char *key, const char *value) {
SPIN_LOCK(E)
lua_State *L = E->L;
lua_getglobal(L, key);
assert(lua_isnil(L, -1));//注意 重复设置键值对是会报错的
lua_pop(L,1);//下面两行设置键值对
lua_pushstring(L,value);
lua_setglobal(L,key);
SPIN_UNLOCK(E)
}
take it easy