skynet源码阅读系列01从main函数开始
skynet 是C语言写的框架,我们采用学习过程中最基本的方式去阅读skynet,从C语言的main函数开始。
首先我们找到框架的入口 main 函数,在 skynet/skynet-src/skynet_main.c 文件内。
main函数的代码如下: 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 " "usage: skynet configfilename "); return 1; } skynet_globalinit(); skynet_env_init(); sigign(); struct skynet_config config; #ifdef LUA_CACHELIB // init the lock of code cache luaL_initcodecache(); #endif 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 ",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"); config.profile = optboolean("profile", 1); lua_close(L); skynet_start(&config); skynet_globalexit(); return 0; }
我们一段一段查看 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 " "usage: skynet configfilename "); return 1; } //... }
定义了一个指针, 指针指向常量, const char* config_file , config_file 赋值为启动时的第二个参数,也就是配置文件的路径。
skynet_globalinit(); // skynet/skynet-src/skynet_server.c struct 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,可参考文章: https://www.jianshu.com/p/d78d93d46fc2
skynet_initthread(THREAD_MAIN); 将当前线程状态由 THREAD_MAIN 切换为 THREAD_WORKER 状态并记录在handle_key。
skynet_env_init(); // skynet/skynet-src/skynet_env.c struct skynet_env { struct spinlock lock; lua_State *L; }; static struct skynet_env *E = NULL; void skynet_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 int 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.c static struct codecache CC; struct codecache { struct spinlock lock; lua_State *L; }; LUALIB_API void luaL_initcodecache(void) { SPIN_INIT(&CC); } static const char * load_config = " local result = {} local function getenv(name) return assert(os.getenv(name), [[os.getenv() failed: ]] .. name) end local sep = package.config:sub(1,1) 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 -- root current_path = path else current_path = current_path .. path 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() assert(load(code,[[@]]..filename,[[t]],result))() current_path = last_path end setmetatable(result, { __index = { include = include } }) local config_name = ... include(config_name) setmetatable(result, nil) return result "; 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 ",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 "); 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 ", key); exit(1); } skynet_setenv(key,value); } lua_pop(L,1); } lua_pop(L,1); } // skynet/skynet-src/skynet_env.c 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) } // 从堆栈上弹出一个值,并将其设为全局变量 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 int optint(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.c const 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 中定义的特殊的线程数据。
河南一家5口4人被杀,凶手是夫妻,妻子被判12年引发争议最近一段时间,媒体报道了河南一家五口一年前惨遭灭门的消息,其中一家五口,有四人被杀,而凶手竟然是另一个家庭的夫妻俩。看到这个消息,很多网友都感到非常震惊有什么深仇大恨,非要闹得如此
60岁岳父灭了女婿一家三口,二审时死刑变死缓,啥仇这么大?今天我们要说的这个故事,发生在东三省的吉林一个60岁的老翁,竟然杀死了自己的女婿,以及女婿的父母,而女婿的父母也就是老翁的亲家,都是教师。而老翁的女婿只有33岁,正是人生的黄金年龄
苏黎世联邦理工学院研究通过12个小型无线电磁传感器追踪人体姿态(映维网2021年10月12日)苏黎世联邦理工学院的先进交互技术实验室(AdvancedInteractiveTechnologiesAIT)专注于而研究机器学习计算机视觉和人机交
比手机更靠谱的考生礼物?vivoTWS2成释放压力首选转瞬间已至年中六月,除了天气炎热外,这两天的高考氛围又为这炎炎夏日增添了一份热度。与很多家庭一样,我家里老妹也是今年的考生之一。回想起当年高考结束之后的我,仿佛心中还存有一丝惬意。
武新制冷天然气管网压力能回收及冷能综合利用项目初步调试成功余压余冷等新常规能源的回收利用,是实现碳达峰碳中和的解决方案之一。2021年7月,顶着火热的夏日骄阳,冰山集团武新制冷设计并投资承建的武汉市三金潭调压站天然气管网压力能回收及冷能综
音频剪辑无压力,分分钟教你搞定大家好,我是小编浩克,音频转换不会怎么办,相信很多小伙伴们都遇到过这种烦恼,迅捷音频转换器轻松好用,支持多种格式互换,还包括音频剪辑音频合并和音频提取功能分割方式多样功能丰富的软件
大数据机构培训班学习效果好吗?学习之路是如此的漫长,有的人总想着还是要多掌握一门技能,大数据一词又是最近比较火热的词条之一,一部分人想着学也是学,不如学一学这大数据技术,那么问题又来了,当选择去大数据培训机构培
大数据学习SparkRDD操作入门在Spark框架组件当中,核心部分不得不提到一个重要的概念,叫做RDD。而这个RDD,本身来说,也是Spark框架相比早期的HadoopMapReduce框架实现了性能提升的重要一
猪队友促震荡利布局上周判断本周金融三兄弟将换岗式护盘,但证券却故意制造震荡,造成大盘摇摇欲坠之感,从而利于主力布局明春行情。上周说银行和证券是本周主角,但证券却成了猪样的队友,打击小散一致看多的心理
大数据开发基础之SQL语句基本操作昨天我们把MySQL的基础知识大概说了一遍,所以接下来几篇文章我们都会细化的讲讲MySQL的一些内容,而今天就给大家带来大数据开发基础之SQL语句基本操作,在有了理论知识后也必须动
大数据开发zookeeper命令操作本期又是大家最喜欢的命令合集,今天给大家带来的是大数据开发zookeeper命令操作。作为大数据开发学习的基础,最基本的可不能落下,下面就开始zookeeper的命令操作的学习吧!