1.作爲skynet的啓動文件,主要完成了一些初始化和讀取並存取配置文件內容的工作. 在這裏只將代碼讀取配置文件的部分抽取出來,就算沒有skynet環境,這些代碼也是可以運行的,瞭解以後再對照源碼進行分析,希望能對理解skynet帶來一些幫助
#include "lua.h" #include "lualib.h" #include "lauxlib.h" #include <signal.h> #include <assert.h> #include "env.h" struct config { int thread; int harbor; const char *deamon; const char *module_path; const char *bootstrap; const char *logger; const char *logservice; }; void popn(lua_State *L, int n); static int optint(const char *key, int opt) { const char *str = env_getenv(key); if (str == NULL) { char tmp[20]; sprintf(tmp, "%d", opt); env_setenv(key, tmp); return opt; } return strtol(str, NULL, 10); } static const char * optstring(const char *key, const char *opt) { const char *str = env_getenv(key); if (str == NULL) { if (opt) { env_setenv(key, opt); opt = env_getenv(key); } return opt; } return str; } static const char * load_config = "\ local config_name = ...\ local f = assert(io.open(config_name))\ local code = assert(f:read \'*a\')\ print(\"code is \", code)\ local function getenv(name) return assert(os.getenv(name), \'os.getenv() failed: \' .. name) end\ code = string.gsub(code, \'%$([%w_%d]+)\', getenv)\ f:close()\ print(\"code after replace is \", code)\ local result = {}\ assert(load(code,\'=(load)\',\'t\',result))()\ return result\ "; static void _init_env(lua_State *L) { lua_pushnil(L); while(lua_next(L, -2) != 0) { int keyt = lua_type(L, -2); if (keyt != LUA_TSTRING) { fprintf(stderr, "Invalid, config table\n"); exit(1); } const char *key = lua_tostring(L, -2); if (lua_type(L, -1) == LUA_TBOOLEAN) { int b = lua_toboolean(L, -1); env_setenv(key, b ? "true" : "false"); } else { const char *value = lua_tostring(L, -1); if (value == NULL) { fprintf(stderr, "Invalud config table key = %s\n", key); exit(1); } env_setenv(key, value); } lua_pop(L, 1); } lua_pop(L, 1); } void popn(lua_State *L, int n) { lua_pop(L, -(n) - 1); } int sigign() { struct sigaction sa; sa.sa_handler = SIG_IGN; sigaction(SIGPIPE, &sa, 0); return 0; } void init_conf(struct config *conf) { conf->thread = optint("thread", 8); conf->module_path = optstring("cpath", "./cservice/?.so"); conf->harbor = optint("harbor", 1); conf->bootstrap = optstring("bootstrap", "snlua bootstrap"); conf->deamon = optstring("deamon", NULL); conf->deamon = optstring("logger", NULL); conf->logservice = optstring("logservice", "logger"); } void test_env() { printf("thread: %s\n", env_getenv("thread")); printf("harbor: %s\n", env_getenv("harbor")); } 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"); } sigign(); env_env_init(); struct config conf; struct lua_State *L = luaL_newstate(); luaL_openlibs(L); 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); init_conf(&conf); test_env(); lua_close(L); return 0; }
首先看main函數:
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"); }
這句話是讀取配置文件路徑:正如運行skynet的命令行爲:./skynet ./examples/config.我將config拿出來放到和可執行文件用級,運行方式爲: ./test ./config
於是config_file = argv[1]便保存了配置文件名.
下面兩句: sigign(); env_env_init();
設置信號處理函數,調用env_env_init()初始化保存配置文件的環境,該函數在env.c中,對應的是skynet_env.c(這裏內容是一樣的),代碼如下:
env.c:
#include "env.h" #include "spinlock.h" #include "lauxlib.h" #include "lua.h" #include <stdlib.h> #include <assert.h> #include <error.h> #include <string.h> struct env { struct spinlock lock; lua_State *L; }; static struct env *E = NULL; const char* env_getenv(const char *key) { SPIN_LOCK(E); lua_State *L = E->L; lua_getglobal(L, key); const char *result = lua_tostring(L, -1); //nil may return if there is no such a key-pair. lua_pop(L, 1); SPIN_UNLOCK(E); return result; } void env_setenv(const char *key, const char *value) { SPIN_LOCK(E); lua_State *L = E->L; lua_getglobal(L, key); //after getglobal , if there is a value crosponds to the key , then it will be pushed onto stack, otherwise nil is pushed assert(lua_isnil(L, -1)); lua_pop(L, 1); //pop nil from the stack lua_pushstring(L, value); lua_setglobal(L, key); //after setglobal the value on the stack will be poped. SPIN_UNLOCK(E); } void env_env_init() { E = (struct env*)malloc(sizeof(*E)); SPIN_INIT(E); E->L = luaL_newstate(); }
由此可見env.c創建了靜態變量 static struct env *E 來保存配置。
繼續main函數:
struct config conf; struct lua_State *L = luaL_newstate(); luaL_openlibs(L); 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);
這幾句話便完成了讀取配置文件並保存到env.c中的E的lua虛擬機裏.
int err = luaL_loadstring(L, load_config);
這句話將load_config作爲一個chunk加載到打開的L的函數堆棧裏,我們看一下load_config:
static const char * load_config = "\ local config_name = ...\ local f = assert(io.open(config_name))\ local code = assert(f:read \'*a\')\ print(\"code is \", code)\ local function getenv(name) return assert(os.getenv(name), \'os.getenv() failed: \' .. name) end\ code = string.gsub(code, \'%$([%w_%d]+)\', getenv)\ f:close()\ print(\"code after replace is \", code)\ local result = {}\ assert(load(code,\'=(load)\',\'t\',result))()\ return result\ ";
發現load_config其實是一段lua代碼字符串,load到函數棧上以後,便可以執行,
但是根據 local config_name = ...\ 看出,需要傳入一個參數,也就是配置文件的路徑 那麼如何傳呢?繼續看main函數 lua_pushstring(L, config_file); 這句話便將config_file這個字符串放到了棧上供
int err = luaL_loadstring(L, load_config); 產生的chunk調用.
於是執行 err = lua_pcall(L, 1, 1, 0); if (err) { fprintf(stderr, "%s,\n", lua_tostring(L, -1)); lua_close(L); return 1; }
這句話便是開始執行lua函數棧上的chunk,參數爲剛纔push進去的config_file,那麼chunk做了哪些事情呢?
local f = assert(io.open(config_name))\
local code = assert(f:read \'*a\')\
首先打開配置文件,並讀取所有內容,接下來定義了一個函數:
local function getenv(name) return assert(os.getenv(name), \'os.getenv() failed: \' .. name) end\
這個函數執行返回 os.getenv(name), 也就是name爲環境變量的名稱,這裏得到其值例如: print(os.getenv("HOME")),便會輸出$(HOME)對應的值,
接下來:code = string.gsub(code, \'%$([%w_%d]+)\', getenv)\
可以看到用string.gsub來講code(配置文件內容)中的$(...)環境變量用getenv中得到的來替換
.但是skynet中得config 沒有類似的環境變量,所以得到的code沒有變化.
接下來
f:close()\
local result = {}\
assert(load(code,\'=(load)\',\'t\',result))()\
return result\
關閉配置文件,將code load到result表中,然後返回result表,
我們注意到我們調用lua_pcall(L, 1,1,0),因此,result表此時在棧頂的位置. 到此爲止,配置文件的內容已經存放在result表中,並且這個表在lua調用棧的棧頂
,接下來便是從棧上的表中讀取表中的內容,然後存放到env中: 緊接着調用
_init_env(L);
static void _init_env(lua_State *L) { lua_pushnil(L); while(lua_next(L, -2) != 0) { int keyt = lua_type(L, -2); if (keyt != LUA_TSTRING) { fprintf(stderr, "Invalid, config table\n"); exit(1); } const char *key = lua_tostring(L, -2); if (lua_type(L, -1) == LUA_TBOOLEAN) { int b = lua_toboolean(L, -1); env_setenv(key, b ? "true" : "false"); } else { const char *value = lua_tostring(L, -1); if (value == NULL) { fprintf(stderr, "Invalud config table key = %s\n", key); exit(1); } env_setenv(key, value); } lua_pop(L, 1); } lua_pop(L, 1); }
這個函數是一個典型的讀取table中以字符串作爲鍵的table的內容,尤其是不知道table中的具體內容是什麼的時候.
這裏我們只知道是以鍵值對形式,且鍵是字符串,table爲非數組。
上邊我們知道此時lua函數棧的棧頂爲存放了配置文件內容的table,那麼這個函數就是挨個遍歷table內容,並調用env_env_set()來存放。
首先lua_next(L, -2)先將棧頂元素彈出然後將table中的一個鍵值對放到棧上,鍵在-2位置上,值在-1位置上。因爲lua_next(L, -2)先彈棧頂元素,因此在調用之前先pushnil,放進一個nil在棧頂,調用lua_next() nil 被彈出 然後table中的第一個鍵值對依次放到棧上,然後獲得鍵值,調用 env_setenv()來存放內容到E。循環讀取table的值,直到讀完跳出循環。至此,config文件中的內容全部存放到env中的全局變量E中的虛擬機中,並可在其他地方調用來獲得配置文件內容. 最後: init_conf(&conf); test_env(); lua_close(L); 其實就是仿照skynet_main.c來測試,test_env()可以替換成skynet_start(&conf)來繼續啓動skynet,這裏簡單的用來測試。 到此爲止,讀取config配置文件的工作就做完了,並保存到了env.c中的局部靜態變量裏。供其它地方使用配置。
基礎有限,研究skynet時間有限,難免講述不清或出現錯誤,望指正。 最後貼出全部測試文件代碼:
----------------------------------------------------------------------------------------------------------
start.c, env.c的代碼見上
env.h
#ifndef __ENV_H_ #define __ENV_H_ const char* env_getenv(const char*key); void env_setenv(const char *key, const char *value); void env_env_init(); #endif
spinlock.h
#ifndef SKYNET_SPINLOCK_H #define SKYNET_SPINLOCK_H #define SPIN_INIT(q) spinlock_init(&(q)->lock); #define SPIN_LOCK(q) spinlock_lock(&(q)->lock); #define SPIN_UNLOCK(q) spinlock_unlock(&(q)->lock); #define SPIN_DESTROY(q) spinlock_destroy(&(q)->lock); #ifndef USE_PTHREAD_LOCK struct spinlock { int lock; }; static inline void spinlock_init(struct spinlock *lock) { lock->lock = 0; } static inline void spinlock_lock(struct spinlock *lock) { while (__sync_lock_test_and_set(&lock->lock,1)) {} } static inline int spinlock_trylock(struct spinlock *lock) { return __sync_lock_test_and_set(&lock->lock,1) == 0; } static inline void spinlock_unlock(struct spinlock *lock) { __sync_lock_release(&lock->lock); } static inline void spinlock_destroy(struct spinlock *lock) { (void) lock; } #else #include <pthread.h> // we use mutex instead of spinlock for some reason // you can also replace to pthread_spinlock struct spinlock { pthread_mutex_t lock; }; static inline void spinlock_init(struct spinlock *lock) { pthread_mutex_init(&lock->lock, NULL); } static inline void spinlock_lock(struct spinlock *lock) { pthread_mutex_lock(&lock->lock); } static inline int spinlock_trylock(struct spinlock *lock) { return pthread_mutex_trylock(&lock->lock) == 0; } static inline void spinlock_unlock(struct spinlock *lock) { pthread_mutex_unlock(&lock->lock); } static inline void spinlock_destroy(struct spinlock *lock) { pthread_mutex_destroy(&lock->lock); } #endif #endif
config
root = "./" thread = 8 --logger = nil logger = "userlog" logservice = "snlua" --logservice = "catlogger" logpath = "./../../log/cat/" harbor = 1 address = "127.0.0.1:2401" master = "127.0.0.1:2013" start = "main" -- main script bootstrap = "snlua bootstrap" -- The service for bootstrap standalone = "0.0.0.0:2013" luaservice = "./../../service/logind/?.lua;./../../service/db/?.lua;./../../service/web/?.lua;./../../service/cat/?.lua;./../../service/?.lua;./service/?.lua" lualoader = "lualib/loader.lua" preload = "./../../lualib/preload.lua" -- run preload.lua before every lua service run snax = root.."../logind/?.lua;"..root.."test/?.lua" -- snax_interface_g = "snax_g" cpath = "./../../cservice/?.so;./cservice/?.so" -- daemon = "./skynet.pid"
makefile:
CC= gcc CFLAGS= -g -O2 -Wall LUA_INC= /usr/include/lua5.2 all: main main : start.c env.c env.h spinlock.h $(CC) $(CFLAGS) -o $@ $^ -I$(LUA_INC) -llua5.2 -lpthread -lm clean : rm -f main