Read the skynet source code from the beginning (2) The pre-task bootstrap and skynte.newservice for server startup

      After the analysis of the previous article, the second part of the analysis was originally intended to analyze the logic after start, so that people can understand the skynet framework more quickly. But thinking about it, it would be better to write down the idea of ​​starting the code, I think it will be easier for me to understand.

      Bootstrap is the meaning of bootstrap, in skynet, it is indeed the pre-task of doing server work.

In skynet_start.c

//启动logger服务
struct skynet_context *ctx = skynet_context_new(config->logservice, config->logger);
    if (ctx == NULL) {
	fprintf(stderr, "Can't launch %s service\n", config->logservice);
	exit(1);
    }

skynet_handle_namehandle(skynet_context_handle(ctx), "logger");

//启动配置中的bootstrap服务
bootstrap(ctx, config->bootstrap);

//调用start传入配置线程数量
start(config->thread);

Query configuration

It can be seen that the parameter passed in is "snlua bootsrtap", and then see the realization of the function

static void
bootstrap(struct skynet_context * logger, const char * cmdline) {
    int sz = strlen(cmdline);
    char name[sz+1];
    char args[sz+1];
    sscanf(cmdline, "%s %s", name, args);
    //name = snlua, args = bootstrap
    struct skynet_context *ctx = skynet_context_new(name, args);
    if (ctx == NULL) {
	skynet_error(NULL, "Bootstrap error : %s\n", cmdline);
	skynet_context_dispatchall(logger);
	exit(1);
    }
}

Next, see the skynet_context_new method, in skynet_server.c

struct skynet_context * 
skynet_context_new(const char * name, const char *param) {
        //先查询snlua模块,这是一个C写的服务,在skynet_start.c中
        //skynet_statc方法,skynet_module_init(config->module_path)进行初始化
	struct skynet_module * mod = skynet_module_query(name);

	if (mod == NULL)
		return NULL;
        
        //分析1
        //这里创建一个新的lua虚拟机,所以会有各个服务的隔离
	void *inst = skynet_module_instance_create(mod);
	if (inst == NULL)
		return NULL;
	struct skynet_context * ctx = skynet_malloc(sizeof(*ctx));
	CHECKCALLING_INIT(ctx)

	ctx->mod = mod;
	ctx->instance = inst;
	ctx->ref = 2;
	ctx->cb = NULL;
	ctx->cb_ud = NULL;
	ctx->session_id = 0;
	ctx->logfile = NULL;

	ctx->init = false;
	ctx->endless = false;

	ctx->cpu_cost = 0;
	ctx->cpu_start = 0;
	ctx->message_count = 0;
	ctx->profile = G_NODE.profile;
	// Should set to 0 first to avoid skynet_handle_retireall get an uninitialized handle
	ctx->handle = 0;	
	ctx->handle = skynet_handle_register(ctx);
	struct message_queue * queue = ctx->queue = skynet_mq_create(ctx->handle);
	// init function maybe use ctx->handle, so it must init at last
	context_inc();

	CHECKCALLING_BEGIN(ctx)
        //分析2
        //这里使用snlua,启动传入的bootstrap.lua
	int r = skynet_module_instance_init(mod, inst, ctx, param);
	CHECKCALLING_END(ctx)
	if (r == 0) {
		struct skynet_context * ret = skynet_context_release(ctx);
		if (ret) {
			ctx->init = true;
		}
		skynet_globalmq_push(queue);
		if (ret) {
			skynet_error(ret, "LAUNCH %s %s", name, param ? param : "");
		}
		return ret;
	} else {
		skynet_error(ctx, "FAILED launch %s", name);
		uint32_t handle = ctx->handle;
		skynet_context_release(ctx);
		skynet_handle_retire(handle);
		struct drop_t d = { handle };
		skynet_mq_release(queue, drop_message, &d);
		return NULL;
	}
}

Follow the order of analysis 1, analysis 2. First see skynet_module_instance_create, analysis 1

void * 
skynet_module_instance_create(struct skynet_module *m) {
	if (m->create) {
		return m->create(); // 调用c模块的create
	} else {
		return (void *)(intptr_t)(~0);
	}
}

See the create of snlua, and then service_snlua.c to build a new lua virtual machine, so there is isolation between services.

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); // 这里构建了新的lua虚拟机
	return l;
}

After creating the virtual machine, see Analysis 2 and go to skynet_module_instance_init. In skynet_module.c, you can see that it is actually what the above code comment says. Use snlua's init to start bootstarp

int
skynet_module_instance_init(struct skynet_module *m, void * inst, struct skynet_context *ctx, const char * parm) {
	return m->init(inst, ctx, parm);
}

Next, we go to service_snlua.lua to see how snlua is used to start a lua script. In skynet, the definition of the C module must have some methods, such as init, the above m->init, and finally the following function

int
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);
        //给需要初始化的服务设置回调函数为上面的luanch_cb
	skynet_callback(ctx, l , launch_cb);
	const char * self = skynet_command(ctx, "REG", NULL);
	uint32_t handle_id = strtoul(self+1, NULL, 16);
	// it must be first message
        // 给服务发送消息触发上面的launch_cb
        // 这里先知道触发上面,具体的消息处理我们的分析会说明
        // 给服务发送消息之后,具体的服务是怎么去处理的
	skynet_send(ctx, 0, handle_id, PTYPE_TAG_DONTCOPY,0, tmp, sz);
	return 0;
}

After sending the message, call to luanch_cb

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;
	skynet_callback(context, NULL, NULL);
	int err = init_cb(l, context, msg, sz);//再这进行服务的最后初始化,分析在下面
	if (err) {
		skynet_command(context, "EXIT", NULL);
	}

	return 0;
}

Finally, init_cb is called, the following is a simplified code, just to illustrate the calling process

static int
init_cb(struct snlua *l, struct skynet_context *ctx, const char * args, size_t sz) {
        ......
        
        //这里,就是使用loader.lua去加载我们的bootstrap
        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;
	}
	lua_pushlstring(L, args, sz);
	r = lua_pcall(L,1,0,1); // 这里,就又通过C语言的lua接口,调用回了lua层面。
	if (r != LUA_OK) {
		skynet_error(ctx, "lua loader error : %s", lua_tostring(L, -1));
		report_launcher_error(ctx);
		return 1;
	}
	lua_settop(L,0);
	if (lua_getfield(L, LUA_REGISTRYINDEX, "memlimit") == LUA_TNUMBER) {
		size_t limit = lua_tointeger(L, -1);
		l->mem_limit = limit;
		skynet_error(ctx, "Set memory limit to %.2f M", (float)limit / (1024 * 1024));
		lua_pushnil(L);
		lua_setfield(L, LUA_REGISTRYINDEX, "memlimit");
	}
	lua_pop(L, 1);

	lua_gc(L, LUA_GCRESTART, 0);

	return 0;
}

What if bootstrap is started, look down bootstrap.lua

skynet.start(function()
	local sharestring = tonumber(skynet.getenv "sharestring" or 4096)
	memory.ssexpand(sharestring)

	local standalone = skynet.getenv "standalone"

        // 这里又用snlua去启动了launcher.lua,启动过程和bootstrap一样
        // 这个launcher服务先记住,待会儿就知道干嘛的了
	local launcher = assert(skynet.launch("snlua","launcher"))
	skynet.name(".launcher", launcher)

        // 这下面还有一些是bootstrap这个前置任务做的
        // 这里skynet.newservice是啥?
	......
        skynet.newservice "service_mgr"
	pcall(skynet.newservice,skynet.getenv "start" or "main")
	skynet.exit()// 启动完上面的,完成了任务,这个服务就退出了

end)

See skynet.newservice, this in skynet is used by the lua layer to start new services. In skynet.lua, you can see that call.launcher uses the launcher.lua started above to start a service

function skynet.newservice(name, ...)
        // 这个参数 "LAUNCH", "snlua", name, ...特别注意一下
        // 最后也是用调用snlua去启动一个lua服务
	return skynet.call(".launcher", "lua" , "LAUNCH", "snlua", name, ...)
end

I saw LAUNCH in the launcher, in the launcher.lua

require "skynet.manager"	-- import manager apis

local function launch_service(service, ...)
	local param = table.concat({...}, " ")
	local inst = skynet.launch(service, param)
	local session = skynet.context()
	local response = skynet.response()
	if inst then
		services[inst] = service .. " " .. param
		instance[inst] = response
		launch_session[inst] = session
	else
		response(false)
		return
	end
	return inst
end

function command.LAUNCH(_, service, ...)
	launch_service(service, ...)
	return NORET
end

See skyne.launch below, in manager.lua

local c = require "skynet.core"

function skynet.launch(...)
	local addr = c.command("LAUNCH", table.concat({...}," "))
	if addr then
		return tonumber("0x" .. string.sub(addr , 2))
	end
end

Call to c.command,

Here skynet.core is a C language module, so far, we will enter the C language implementation part, call skynet.core.command ("LAUNCH", "snlua...").

Let's summarize the content of the lua part first:

newservice–>skynet.call .launcher–>.launcher=skynet.launch(“snlua”, “launcher”)–>skynet.core.command(“LAUNCH”, “snlua ...”)

skynet.core is actually defined in lua_skynet.c, and its command corresponds to the lcommand function. The parameters at this time are actually pressed into lua_State.

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);
	}

        // 这里就是调用skynet_server.c
	result = skynet_command(context, cmd, parm);
	if (result) {
		lua_pushstring(L, result);
		return 1;
	}
	return 0;
}

It is finally called skynet_command in 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 }, //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 },
};

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);
		}
		++method;
	}

	return NULL;
}

When I see cmd_launch again, I'm very familiar with it here. Connect back to the analysis of bootstrap above

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\n");
	args = strsep(&args, "\r\n");
    
        // 这里这几行是不是特别熟悉
        // 没错,就是和上面的bootstrap的启动一模一样
        // 就是使用snlua去启动另外的服务
	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;
	}
}

 

As you can see, the skynet.newservice we will use on the lua layer later use launcher.lua to start the new server.

Finally, one of the important things that bootstrap does is to start the launcher.lua service, and then skynet.newservice in the framework is to call the launcher to start the lua service.

So call him the pre-task of the server. After starting the new service, how to hook the callback function, etc., will be analyzed later.

Next articleWhat did skynet_start do after sharing bootstrap

Guess you like

Origin blog.csdn.net/banfushen007/article/details/111102233