function skynet.launch(...) c.command("LAUNCH", table.concat({...}, " ")) ...... end lua-skynet.c | _command | skynet_command skynet_server.c | cmd_launch | skynet_context_new ==================================================================== function skynet.newservice(name, ...) return skynet.call(".launcher", "lua" , "LAUNCH", "snlua", name, ...) end service/launcher.lua 返回 "RESPONSE", skynet.pack newservice 收到 "RESPONSE" launch 服务就有所有服务的地址 可以统计所有服务,gc某个服务,stats某个服务 ==================================================================== 再来看 skynet_context_new 1. 获取 skynet_module struct skynet_module { const char * name; 模块名 void * module; 模块对应的动态库(snlua, 或者其他动态库) skynet_dl_create create; 创建函数 skynet_dl_init init; 初始化函数 skynet_dl_release release; 释放函数 skynet_dl_signal signal; 信号函数 }; 2. 调用 M->create() 函数 创建 lua vm struct snlua { lua_State * L; struct skynet_context * ctx; }; 构建 snlua 结构 3. 创建并初始化 上下文 struct skynet_context { void * instance; snlua结构 struct skynet_module * mod; void * cb_ud; skynet_cb cb; callback函数指针 struct message_queue *queue; 消息队列 FILE * logfile; 日志输出文件 char result[32]; uint32_t handle; int session_id; int ref; bool init; bool endless; CHECKCALLING_DECL }; 创建消息队列 4. 调用 M->init()函数 设置回调函数 skynet_callback(ctx, l , _launch); ctx->cb 回调函数 _launch ctx->cb_ud snlua结构 "REG" 注册 发送第一个消息 skynet_send(ctx, 0, handle_id, PTYPE_TAG_DONTCOPY, 0, tmp, sz); 调用 _launch 函数 skynet_callback(context, NULL, NULL); 将回调函数清空 接着调用 _init 函数 将 skynet_context 的指针地址 存入 lua 环境中 (通过轻量用户数据来实现) 加载常用库 将 LUA_PATH LUA_CPATH LUA_SERVICE LUA_PRELOAD 写入全局table中 通过 loader.lua 加载所有的lua库 5. 将次级消息队列压入全局消息队列 skynet_globalmq_push 6. 在服务主模块中调用 skynet.start 设置回调函数 c.callback 将 _cb作为键,值lua 回调函数 写入lua环境中 skynet_callback lua_rawgeti(L, LUA_REGISTRYINDEX, LUA_RIDX_MAINTHREAD); lua_State *gL = lua_tothread(L,-1); cb_ud 保存 主线程,主线程不会被回收,生命周期同vm 主线程 栈上永远保存着两个值 traceback 回调函数 调用 回调函数 r = lua_pcall(L, 5, 0, 1); 栈上第一个函数作为 错误处理函数 主线程 调用回调函数 回调函数分配线程去处理 每条消息 7. 错误处理 在 发生错误的 服务 处理 回调函数发生在主线程