skynet luaサービスプロセスの作成


簡単なtestサービスを例に、skynetがluaサービスを作成する方法について説明します.
まず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, 
		...
}