skynet luaサービスプロセスの作成
79767 ワード
簡単なtestサービスを例に、skynetがluaサービスを作成する方法について説明します.
まずskynetを呼び出します.newservice(“test”)
インタフェースファイル:skynet.lua name:test arg:…
このインタフェースはlauncherサービスにluaメッセージコマンドを送信します.LAUNCHの後ろにあるのはパラメータです.
インタフェースファイル:***launcher.lua***cmd LAUNCH***パラメータリスト***
次にcmd snlua***を呼び出します...パラメータリスト***
cmd snlua***パラメータリスト***
インタフェースファイル:***manager.lua***
インタフェースファイル:***lua-skynet.c***
インタフェースファイル:***skynet_server.c***
インタフェースファイル:***skynet_module.c***
インタフェースファイル:***service_snlua.c***
インタフェースファイル:***skynet_module.c***
インタフェースファイル:***service_snlua.c***
インタフェースファイル:***skynet_server.c***
インタフェースファイル:***service_snlua.c***
インタフェースファイル:***skynet_start.c***
インタフェースファイル:***skynet_server.c***
まずskynetを呼び出します.newservice(“test”)
インタフェースファイル:skynet.lua name:test arg:…
function skynet.newservice(name, ...)
return skynet.call(".launcher", "lua" , "LAUNCH", "snlua", name, ...)
end
このインタフェースはlauncherサービスにluaメッセージコマンドを送信します.LAUNCHの後ろにあるのはパラメータです.
インタフェースファイル:***launcher.lua***cmd LAUNCH***パラメータリスト***
skynet.dispatch("lua", function(session, address, cmd , ...)
cmd = string.upper(cmd)
local f = command[cmd]
if f then
local ret = f(address, ...)
if ret ~= NORET then
skynet.ret(skynet.pack(ret))
end
else
skynet.ret(skynet.pack {"Unknown command"} )
end
end)
次にcmd snlua***を呼び出します...パラメータリスト***
function command.LAUNCH(_, service, ...)
launch_service(service, ...)
return NORET
end
cmd snlua***パラメータリスト***
local function launch_service(service, ...)
local param = table.concat({...}, " ") --
local inst = skynet.launch(service, param) -- skynet.launch
...
end
インタフェースファイル:***manager.lua***
function skynet.launch(...)
local addr = c.command("LAUNCH", table.concat({...}," ")) -- c
if addr then
return tonumber("0x" .. string.sub(addr , 2))
end
end
インタフェースファイル:***lua-skynet.c***
LUAMOD_API int
luaopen_skynet_core(lua_State *L) {
luaL_checkversion(L);
luaL_Reg l[] = {
{ "send" , lsend },
{ "genid", lgenid },
{ "redirect", lredirect },
{ "command" , lcommand }, // lcommand
{ "intcommand", lintcommand },
{ "addresscommand", laddresscommand },
{ "error", lerror },
{ "harbor", lharbor },
{ "callback", lcallback },
{ "trace", ltrace },
{ NULL, NULL },
};
...
return 1;
}
static int
lcommand(lua_State *L) {
struct skynet_context * context = lua_touserdata(L, lua_upvalueindex(1));
const char * cmd = luaL_checkstring(L,1);
const char * result;
const char * parm = NULL;
if (lua_gettop(L) == 2) {
parm = luaL_checkstring(L,2);
}
result = skynet_command(context, cmd, parm); //cmd LAUNCH parm
if (result) {
lua_pushstring(L, result);
return 1;
}
return 0;
}
インタフェースファイル:***skynet_server.c***
static struct command_func cmd_funcs[] = {
{ "TIMEOUT", cmd_timeout },
{ "REG", cmd_reg },
{ "QUERY", cmd_query },
{ "NAME", cmd_name },
{ "EXIT", cmd_exit },
{ "KILL", cmd_kill },
{ "LAUNCH", cmd_launch }, // cmd_launch
{ "GETENV", cmd_getenv },
{ "SETENV", cmd_setenv },
{ "STARTTIME", cmd_starttime },
{ "ABORT", cmd_abort },
{ "MONITOR", cmd_monitor },
{ "STAT", cmd_stat },
{ "LOGON", cmd_logon },
{ "LOGOFF", cmd_logoff },
{ "SIGNAL", cmd_signal },
{ NULL, NULL },
};
//cmd LAUNCH
const char *
skynet_command(struct skynet_context * context, const char * cmd , const char * param) {
struct command_func * method = &cmd_funcs[0];
while(method->name) {
if (strcmp(cmd, method->name) == 0) {
return method->func(context, param); // cmd_launch
}
++method;
}
return NULL;
}
static const char *
cmd_launch(struct skynet_context * context, const char * param) {
size_t sz = strlen(param);
char tmp[sz+1];
strcpy(tmp,param);
char * args = tmp;
char * mod = strsep(&args, " \t\r
"); // mod = "snlua"
args = strsep(&args, "\r
"); // args = "test"
struct skynet_context * inst = skynet_context_new(mod,args);
if (inst == NULL) {
return NULL;
} else {
id_to_hex(context->result, inst->handle);
return context->result;
}
}
struct skynet_context *
skynet_context_new(const char * name, const char *param) {
// snlua
struct skynet_module * mod = skynet_module_query(name); //name = snlua param = test
if (mod == NULL)
return NULL;
//
void *inst = skynet_module_instance_create(mod);
if (inst == NULL)
return NULL;
struct skynet_context * ctx = skynet_malloc(sizeof(*ctx));
CHECKCALLING_INIT(ctx)
// ctx
...
//
int r = skynet_module_instance_init(mod, inst, ctx, param);
...
}
インタフェースファイル:***skynet_module.c***
struct skynet_module *
skynet_module_query(const char * name) {
// snlua , snlua
struct skynet_module * result = _query(name);
if (result)
return result;
SPIN_LOCK(M)
result = _query(name); // double check
if (result == NULL && M->count < MAX_MODULE_TYPE) {
int index = M->count;
void * dl = _try_open(M,name);
if (dl) {
M->m[index].name = name;
M->m[index].module = dl;
if (open_sym(&M->m[index]) == 0) {
M->m[index].name = skynet_strdup(name);
M->count ++;
result = &M->m[index];
}
}
}
SPIN_UNLOCK(M)
return result;
}
// snlua(snlua.so) ,
static struct skynet_module *
_query(const char * name) {
int i;
for (i=0;i<M->count;i++) {
if (strcmp(M->m[i].name,name)==0) {
return &M->m[i];
}
}
return NULL;
}
static void *
_try_open(struct modules *m, const char * name) {
const char *l;
// config cpath = "./cservice/?.so"
const char * path = m->path;
size_t path_size = strlen(path);
size_t name_size = strlen(name);
// sz = path_size + name_size + 1, path , name
int sz = path_size + name_size;
//search path
void * dl = NULL;
char tmp[sz];
do
{
memset(tmp,0,sz);
while (*path == ';') path++;
if (*path == '\0') break;
l = strchr(path, ';');
if (l == NULL) l = path + strlen(path);
int len = l - path;
// '?' tmp = "./cservice/snlua.so"
int i;
for (i=0;path[i]!='?' && i < len ;i++) {
tmp[i] = path[i];
}
memcpy(tmp+i,name,name_size);
if (path[i] == '?') {
strncpy(tmp+i+name_size,path+i+1,len - i - 1);
} else {
fprintf(stderr,"Invalid C service path
");
exit(1);
}
//
dl = dlopen(tmp, RTLD_NOW | RTLD_GLOBAL);
path = l;
}while(dl == NULL);
if (dl == NULL) {
fprintf(stderr, "try open %s failed : %s
",name,dlerror());
}
return dl;
}
static int
open_sym(struct skynet_module *mod) {
mod->create = get_api(mod, "_create");// snlua_create
mod->init = get_api(mod, "_init");// snlua_init
mod->release = get_api(mod, "_release");// snlua_release
mod->signal = get_api(mod, "_signal");// snlua_signal
return mod->init == NULL;
}
static void *
get_api(struct skynet_module *mod, const char *api_name) {
size_t name_size = strlen(mod->name); // mod->name = "snlua"
size_t api_size = strlen(api_name);
char tmp[name_size + api_size + 1];
memcpy(tmp, mod->name, name_size);
memcpy(tmp+name_size, api_name, api_size+1);
char *ptr = strrchr(tmp, '.');
if (ptr == NULL) {
ptr = tmp;
} else {
ptr = ptr + 1;
}
return dlsym(mod->module, ptr);
}
void *
skynet_module_instance_create(struct skynet_module *m) {
if (m->create) {
return m->create(); // snlua_create()
} else {
return (void *)(intptr_t)(~0);
}
}
インタフェースファイル:***service_snlua.c***
struct snlua *
snlua_create(void) {
struct snlua * l = skynet_malloc(sizeof(*l));
memset(l,0,sizeof(*l));
l->mem_report = MEMORY_WARNING_REPORT;
l->mem_limit = 0;
l->L = lua_newstate(lalloc, l);
return l;
}
struct snlua {
lua_State * L;
struct skynet_context * ctx;
size_t mem;
size_t mem_report;
size_t mem_limit;
};
インタフェースファイル:***skynet_module.c***
int
skynet_module_instance_init(struct skynet_module *m, void * inst, struct skynet_context *ctx, const char * parm) {
return m->init(inst, ctx, parm); // snlua_init()
}
インタフェースファイル:***service_snlua.c***
snlua_init(struct snlua *l, struct skynet_context *ctx, const char * args) {
int sz = strlen(args);
char * tmp = skynet_malloc(sz);
memcpy(tmp, args, sz);
// callback launch_cb, ( )
skynet_callback(ctx, l , launch_cb);
const char * self = skynet_command(ctx, "REG", NULL);
uint32_t handle_id = strtoul(self+1, NULL, 16);
// tmp = "test", launch_cb msg = "test"
// it must be first message
skynet_send(ctx, 0, handle_id, PTYPE_TAG_DONTCOPY,0, tmp, sz);
return 0;
}
インタフェースファイル:***skynet_server.c***
void
skynet_callback(struct skynet_context * context, void *ud, skynet_cb cb) {
context->cb = cb;
context->cb_ud = ud;
}
int
skynet_send(struct skynet_context * context, uint32_t source, uint32_t destination , int type, int session, void * data, size_t sz) {
if ((sz & MESSAGE_TYPE_MASK) != sz) {
skynet_error(context, "The message to %x is too large", destination);
if (type & PTYPE_TAG_DONTCOPY) {
skynet_free(data);
}
return -1;
}
_filter_args(context, type, &session, (void **)&data, &sz);
if (source == 0) {
source = context->handle;
}
if (destination == 0) {
return session;
}
if (skynet_harbor_message_isremote(destination)) {
struct remote_message * rmsg = skynet_malloc(sizeof(*rmsg));
rmsg->destination.handle = destination;
rmsg->message = data;
rmsg->sz = sz & MESSAGE_TYPE_MASK;
rmsg->type = sz >> MESSAGE_TYPE_SHIFT;
skynet_harbor_send(rmsg, source, session);
} else {
struct skynet_message smsg;
smsg.source = source;
smsg.session = session;
smsg.data = data;
smsg.sz = sz;
//
if (skynet_context_push(destination, &smsg)) {
skynet_free(data);
return -1;
}
}
return session;
}
インタフェースファイル:***service_snlua.c***
static int
launch_cb(struct skynet_context * context, void *ud, int type, int session, uint32_t source , const void * msg, size_t sz) {
assert(type == 0 && session == 0);
struct snlua *l = ud;
// callback , launch_cb ,callback skynet.start skynet.dispatch_message
skynet_callback(context, NULL, NULL);
// , config lualoader( loader.lua) msg = "test"
int err = init_cb(l, context, msg, sz);
if (err) {
skynet_command(context, "EXIT", NULL);
}
return 0;
}
static int
init_cb(struct snlua *l, struct skynet_context *ctx, const char * args, size_t sz) {
...
// config lualoader
const char * loader = optstring(ctx, "lualoader", "./lualib/loader.lua");
int r = luaL_loadfile(L,loader);
if (r != LUA_OK) {
skynet_error(ctx, "Can't load %s : %s", loader, lua_tostring(L, -1));
report_launcher_error(ctx);
return 1;
}
// args = "test"
lua_pushlstring(L, args, sz);
// lualoader
r = lua_pcall(L,1,0,1);
...
return 0;
}
インタフェースファイル:***skynet_start.c***
static void *
thread_worker(void *p) {
struct worker_parm *wp = p;
int id = wp->id;
int weight = wp->weight;
struct monitor *m = wp->m;
struct skynet_monitor *sm = m->m[id];
skynet_initthread(THREAD_WORKER);
struct message_queue * q = NULL;
while (!m->quit) {
// worker
q = skynet_context_message_dispatch(sm, q, weight);
...
}
return NULL;
}
インタフェースファイル:***skynet_server.c***
struct message_queue *
skynet_context_message_dispatch(struct skynet_monitor *sm, struct message_queue *q, int weight) {
...
dispatch_message(ctx, &msg);
...
}
static void
dispatch_message(struct skynet_context *ctx, struct skynet_message *msg) {
...
// ctx->cb launch_cb, msg->data = "test"
reserve_msg = ctx->cb(ctx, ctx->cb_ud, type, msg->session, msg->source,
...
}