skynet 是C语言编辑 的框架,人类 选用 学习过程中最基本 的方法 去浏览 skynet,从C语言的main函数开始。
首先人类 找到 框架的入口main函数,在 skynet/skynet-src/skynet_main.c 文件内。
main函数的代码如下:
intmain(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 : ;}人类 一段一段查就这样看
intmain(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 : 赋值为启动时的第二个参数,也就是配置文件的路径。
skynet_globalinit();
// skynet/skynet-src/skynet_server.cstruct skynet_node { ATOM_INT total; int init; uint32_t monitor_exit; pthread_key_t handle_key; bool profile; // default is off};static struct skynet_node G_NODE;void skynet_globalinit(void) { ATOM_INIT(&G_NODE.total , 0); G_NODE.monitor_exit = 0; G_NODE.init = 1; if (pthread_key_create(&G_NODE.handle_key, NULL)) { fprintf(stderr, "pthread_key_create failed"); exit(1); } // skynet/skynet-src/skynet_imp.h /* #define THREAD_WORKER 0 #define THREAD_MAIN 1 #define THREAD_SOCKET 2 #define THREAD_TIMER 3 #define THREAD_MONITOR 4 */ skynet_initthread(THREAD_MAIN);}skynet_initthread(int m) { // skynet/skynet-src/atomic.h // #define ATOM_POINTER volatile uintptr_t uintptr_t v = (uint32_t)(-m); pthread_setspecific(G_NODE.handle_key, (void *)v);}初始化全局节点信息 ,total 为0,monitor_exit 为0,init 1,
pthread_key_create(&G_NODE.handle_key, NULL) 创建了一个多线程私有资料 handle_key:
skynet_initthread(THREAD_MAIN); 将目前 线程情况 由 THREAD_MAIN 更换 为 THREAD_WORKER 情况 并记录在handle_key。
skynet_env_init();
// skynet/skynet-src/skynet_env.cstruct skynet_env { struct spinlock lock; lua_State *L;};static struct skynet_env *E = NULL;voidskynet_env_init() { E = skynet_malloc(sizeof(*E)); SPIN_INIT(E) E-L = luaL_newstate();}E 一个skynet_env结构体,结构体内包含一个 spinlock 自旋锁,一个lua虚拟机指针。
skynet_malloc 为结构体E分配内存,skynet_malloc内部暂时不细究。
SPIN_INIT(E)
通过查找代码得知, 这是在 skynet/skynet-src/spinlick.h 中定义的一个宏。#define SPIN_INIT(q) spinlock_init(&(q)-lock);对E中的lock 进行初始化。
E-L = luaL_newstate(); L绑定了一个lua虚拟机。
sigign();
#include signal.hint sigign() { struct sigaction sa; sa.sa_handler = SIG_IGN; sa.sa_flags = 0; sigemptyset(&sa.sa_mask); sigaction(SIGPIPE, &sa, 0); return 0;}main 函数同文件下的 sigign() 函数。
定义了一个 sigaction 结构体,将 sa_handler 设置为 SIG_IGN,表示要忽略信号的发生 的动作。
sigaction(SIGPIPE, &sa, 0); 将 SIGPIPE的行为代替 为 sa 结构体定义的形式,表示目前 进程忽略 SIGPIPE 信号。
这里简单记录了一下 sigaction 的资料。
01ext_sigaction
struct skynet_config config;
定义了结构体 config
struct skynet_config { int thread; int harbor; int profile; const char * daemon; const char * module_path; const char * bootstrap; const char * logger; const char * logservice;};luaL_initcodecache();
// skynet/skynet-src/skynet_main.c#ifdef LUA_CACHELIB luaL_initcodecache();#endif// skynet/3rd/lauxlib.cstatic struct codecache CC;struct codecache { struct spinlock lock; lua_State *L;};LUALIB_API voidluaL_initcodecache(void) { SPIN_INIT(&CC);}static const char * load_config = "\ local result = {}\n\ local function getenv(name) return assert(os.getenv(name), [[os.getenv() failed: ]] .. name) end\n\ local sep = package.config:sub(1,1)\n\ local current_path = [[.]]..sep\n\ local function include(filename)\n\ local last_path = current_path\n\ local path, name = filename:match([[(.*]]..sep..[[)(.*)$]])\n\ if path then\n\ if path:sub(1,1) == sep then -- root\n\ current_path = path\n\ else\n\ current_path = current_path .. path\n\ end\n\ else\n\ name = filename\n\ end\n\ local f = assert(io.open(current_path .. name))\n\ local code = assert(f:read [[*a]])\n\ code = string.gsub(code, [[%$([%w_%d]+)]], getenv)\n\ f:close()\n\ assert(load(code,[[@]]..filename,[[t]],result))()\n\ current_path = last_path\n\ end\n\ setmetatable(result, { __index = { include = include } })\n\ local config_name = ...\n\ include(config_name)\n\ setmetatable(result, nil)\n\ return result\n\"struct lua_State *L = luaL_newstate();luaL_openlibs(L); // link lua libint 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;}luaL_loadbufferx(L, load_config, strlen(load_config), "=[skynet config]", "t");
加载了一段lua代码到内存里,并压入lua栈内。
load_config 这段代码实现的功能: 将配置文件内的 $var 代替 成了环境变量的内容, 并返回了一个result表。
lua_pcall(L, 1, 1, 0);
执行压入的 load_config 代码块,第二个参数1 表示压入的栈的个数为1,lua_pushstring(L, config_file); 被压栈的配置文件名。 执行完函数之后,函数和参数全自动 出栈,此时 栈为空。 函数的返回值被压栈,此时 栈内只有一个表 result, result 内内含了 配置在 config_file 内的键值对。
_init_env(L);
static void_init_env(lua_State *L) { lua_pushnil(L); /* first key */ 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); skynet_setenv(key,b ? "true" : "false" ); } else { const char * value = lua_tostring(L,-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/skynet-src/skynet_env.cvoid 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)}// 从堆栈上弹出一个值,并将其设为全局变量 name 的新值。void lua_setglobal (lua_State *L, const char *name);// 把全局变量 name 里的值压栈,返回该值的类别 。int lua_getglobal (lua_State *L, const char *name);将lua栈表内的键值对设置到 &E-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");config.profile = optboolean("profile", 1);static intoptint(const char *key, int opt) { const char * str = skynet_getenv(key); if (str == NULL) { char tmp[20]; sprintf(tmp,"%d",opt); skynet_setenv(key, tmp); return opt; } return strtol(str, NULL, 10);}// skynet/skynet-src/skynet_env.cconst char * skynet_getenv(const char *key) { SPIN_LOCK(E) lua_State *L = E-L; lua_getglobal(L, key); const char * result = lua_tostring(L, -1); lua_pop(L, 1); SPIN_UNLOCK(E) return result;}optint, optstring, optboolean 从 &E-L 的全局环境中取得对应键的值,如果全局环境内未定义,则第二个参数 opt 设为 key的默认值。
lua_close(L);
关闭main函数内创建的 lua 虚拟机。
skynet_start(&config);
下一节的内容。
skynet_globalexit();
void skynet_globalexit(void) { pthread_key_delete(G_NODE.handle_key);}删除在 skynet_initthread 中定义的固定 的线程资料 。
标签: 函数