Skynet学习一

Skynet学习

skynet 节点通过运行 skynet 主程序启动,必须在启动命令行传入一个 Config 文件名作为启动参数。skynet 会读取这个 config 文件获得启动需要的参数。

./skynet ./examples/config

第一个启动的服务是 logger ,bootstrap 这个配置项关系着 skynet 运行的第二个服务。
在skynet-src:skynet_main.c中

int
main(int argc, char *argv[]) {
	const char * config_file = NULL ;//定义一个const char*指针
	if (argc > 1) {
		config_file = argv[1];//指向输入的./examples/config
	} else {
		fprintf(stderr, "Need a config file. Please read skynet wiki : https://github.com/cloudwu/skynet/wiki/Config\n"
			"usage: skynet configfilename\n");
		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();//lua虚拟机
	luaL_openlibs(L);	// link lua lib

	int err =  luaL_loadbufferx(L, load_config, strlen(load_config), "=[skynet config]", "t");//链接了config的lua脚本
	assert(err == LUA_OK);
	lua_pushstring(L, config_file);//string压栈

	err = lua_pcall(L, 1, 1, 0);//config的lua脚本运行
	if (err) {
		fprintf(stderr,"%s\n",lua_tostring(L,-1));
		lua_close(L);
		return 1;
	}
	_init_env(L);//初始化环境配置

	//初始化config
	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);
	
	//启动log服务 启动snlua服务 启动socket线程 启动minitor线程 启动worker线程(有weight权重影响)
	skynet_start(&config);
	skynet_globalexit();

	return 0;
}

下面剖析skynet_start(&config)函数
在skynet-src:skynet_start.c中

void 
skynet_start(struct skynet_config * config) {
	// register SIGHUP for log file reopen
	struct sigaction sa;
	sa.sa_handler = &handle_hup;
	sa.sa_flags = SA_RESTART;
	sigfillset(&sa.sa_mask);
	sigaction(SIGHUP, &sa, NULL);

	if (config->daemon) {
		if (daemon_init(config->daemon)) {
			exit(1);
		}
	}
	skynet_harbor_init(config->harbor);//初始化harbor
	skynet_handle_init(config->harbor);//初始化handle
	skynet_mq_init();//初始化message queue
	skynet_module_init(config->module_path);//默认是./cservice/?.so 详情见config
	skynet_timer_init();//初始化timer
	skynet_socket_init();//初始化socket
	skynet_profile_enable(config->profile);

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

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

	bootstrap(ctx, config->bootstrap);//bootstrap主要启动了snlua服务

	start(config->thread);//启动socket线程 启动minitor线程 启动worker线程(有weight权重影响)

	// harbor_exit may call socket send, so it should exit before socket_free
	skynet_harbor_exit();
	skynet_socket_free();
	if (config->daemon) {
		daemon_exit(config->daemon);
	}
}

下面剖析bootstrap(ctx, config->bootstrap)函数
在skynet-src:skynet_start.c中

static void
bootstrap(struct skynet_context * logger, const char * cmdline) {
	int sz = strlen(cmdline);//cmdline一般是snlua bootstrap
	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);//初始化snlua服务
	if (ctx == NULL) {
		skynet_error(NULL, "Bootstrap error : %s\n", cmdline);
		skynet_context_dispatchall(logger);
		exit(1);
	}
}

剖析skynet_context_new(name, args)函数
在skynet-src:skynet_server.c中

struct skynet_context * 
skynet_context_new(const char * name, const char *param) {
	struct skynet_module * mod = skynet_module_query(name);//通过name去加载模块.so文件 在这里是snlua.so文件 加载成skynet的模块(create init signal release加载这四个函数指针 赋值给模块)

	if (mod == NULL)
		return NULL;

	void *inst = skynet_module_instance_create(mod);//调用模块的create函数
	if (inst == NULL)
		return NULL;
	struct skynet_context * ctx = skynet_malloc(sizeof(*ctx));//定义一个skynet_context指针
	CHECKCALLING_INIT(ctx)

	//复制给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);//生成唯一的handle标记
	struct message_queue * queue = ctx->queue = skynet_mq_create(ctx->handle);//初始化message queue 记录了handle
	// init function maybe use ctx->handle, so it must init at last
	context_inc();//context的数量自增1 

	CHECKCALLING_BEGIN(ctx)
	int r = skynet_module_instance_init(mod, inst, ctx, param);//调用module的init函数
	CHECKCALLING_END(ctx)
	if (r == 0) {
		struct skynet_context * ret = skynet_context_release(ctx);//这里使用引用计数管理内存  调用release函数  释放mq 释放ctx context数量自减
		if (ret) {
			ctx->init = true;
		}
		skynet_globalmq_push(queue);//压入全局message queue
		if (ret) {
			skynet_error(ret, "LAUNCH %s %s  1", 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;
	}
}

如上链接了.so库 以及入消息队列
主要有如下.so文件链接

find ./ -name "*.so"
./cservice/gate.so
./cservice/snlua.so
./cservice/harbor.so
./cservice/logger.so
./3rd/jemalloc/lib/libjemalloc.so
./luaclib/bson.so
./luaclib/client.so
./luaclib/sproto.so
./luaclib/lpeg.so
./luaclib/md5.so
./luaclib/skynet.so

下面剖析start(config->thread);//启动socket线程 启动minitor线程 启动worker线程(有weight权重影响)
在在skynet-src:skynet_start.c中

static void
start(int thread) {
	pthread_t pid[thread+3];//3是因为monitor socket timer线程

	struct monitor *m = skynet_malloc(sizeof(*m));
	memset(m, 0, sizeof(*m));
	m->count = thread;
	m->sleep = 0;

	m->m = skynet_malloc(thread * sizeof(struct skynet_monitor *));
	int i;
	for (i=0;i<thread;i++) {
		m->m[i] = skynet_monitor_new();//定义skynet_monitor数组 数量为thread个
	}
	if (pthread_mutex_init(&m->mutex, NULL)) {//初始和mutex
		fprintf(stderr, "Init mutex error");
		exit(1);
	}
	if (pthread_cond_init(&m->cond, NULL)) {//初始化cond
		fprintf(stderr, "Init cond error");
		exit(1);
	}

	create_thread(&pid[0], thread_monitor, m);//设置thread_monitor
	create_thread(&pid[1], thread_timer, m);//设置thread_timer
	create_thread(&pid[2], thread_socket, m);//设置thread_socket

	static int weight[] = { //权重
		-1, -1, -1, -1, 0, 0, 0, 0,
		1, 1, 1, 1, 1, 1, 1, 1, 
		2, 2, 2, 2, 2, 2, 2, 2, 
		3, 3, 3, 3, 3, 3, 3, 3, };
	struct worker_parm wp[thread];
	for (i=0;i<thread;i++) {
		wp[i].m = m;
		wp[i].id = i;
		if (i < sizeof(weight)/sizeof(weight[0])) {
			wp[i].weight= weight[i];
		} else {
			wp[i].weight = 0;
		}
		create_thread(&pid[i+3], thread_worker, &wp[i]);//设置work thread
	}

	for (i=0;i<thread+3;i++) {
		pthread_join(pid[i], NULL); 
	}

	free_monitor(m);
}

下面主要剖析work thread
在在skynet-src:skynet_start.c中

static void *
thread_worker(void *p) {
	struct worker_parm *wp = p;//转换传入的p 在create传入
	int id = wp->id;//id weight monitor skynet_monitor message_queue赋值
	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) {
		q = skynet_context_message_dispatch(sm, q, weight);//消息转发
		if (q == NULL) {
			if (pthread_mutex_lock(&m->mutex) == 0) {
				++ m->sleep;
				// "spurious wakeup" is harmless,
				// because skynet_context_message_dispatch() can be call at any time.
				if (!m->quit)
					pthread_cond_wait(&m->cond, &m->mutex);
				-- m->sleep;
				if (pthread_mutex_unlock(&m->mutex)) {
					fprintf(stderr, "unlock mutex error");
					exit(1);
				}
			}
		}
	}
	return NULL;
}

下面见skynet_context_message_dispatch(sm, q, weight);函数
在skynet-src:skynet_server.c中

struct message_queue * 
skynet_context_message_dispatch(struct skynet_monitor *sm, struct message_queue *q, int weight) {
	if (q == NULL) {//进入时一般为空 不为空是因为sleep过
		q = skynet_globalmq_pop();//获取全局message queue的一个数据
		if (q==NULL)//全局消息都为空
			return NULL;
	}

	uint32_t handle = skynet_mq_handle(q);//获取唯一handle

	struct skynet_context * ctx = skynet_handle_grab(handle);//根据handle获取context 并且引用计数减一
	if (ctx == NULL) {
		struct drop_t d = { handle };
		skynet_mq_release(q, drop_message, &d);
		return skynet_globalmq_pop();
	}

	int i,n=1;
	struct skynet_message msg;

	for (i=0;i<n;i++) {
		if (skynet_mq_pop(q,&msg)) {//message出一个数据
			skynet_context_release(ctx);
			return skynet_globalmq_pop();
		} else if (i==0 && weight >= 0) {//根据权重改变n 循环次数
			n = skynet_mq_length(q);//根据长度和weight改变循环次数
			n >>= weight;
		}
		int overload = skynet_mq_overload(q);//是否过长
		if (overload) {
			skynet_error(ctx, "May overload, message queue length = %d", overload);
		}

		skynet_monitor_trigger(sm, msg.source , handle);//触发监视器 并且版本号自增

		if (ctx->cb == NULL) {
			skynet_free(msg.data);
		} else {
			dispatch_message(ctx, &msg);//分发消息
		}

		skynet_monitor_trigger(sm, 0,0);//重制source 和 destination
	}

	assert(q == ctx->queue);
	struct message_queue *nq = skynet_globalmq_pop();
	if (nq) {
		// If global mq is not empty , push q back, and return next queue (nq)
		// Else (global mq is empty or block, don't push q back, and return q again (for next dispatch)
		skynet_globalmq_push(q);//这一句将q重新放入全局message queue不知道为什么
		q = nq;//这个跳过函数开头的空判断
	} 
	skynet_context_release(ctx);

	return q;
}

下面剖析 dispatch_message(ctx, &msg);//分发消息 通过回调函数分发消息

static void
dispatch_message(struct skynet_context *ctx, struct skynet_message *msg) {
	assert(ctx->init);
	CHECKCALLING_BEGIN(ctx)
	pthread_setspecific(G_NODE.handle_key, (void *)(uintptr_t)(ctx->handle));
	int type = msg->sz >> MESSAGE_TYPE_SHIFT;
	size_t sz = msg->sz & MESSAGE_TYPE_MASK;
	if (ctx->logfile) {
		skynet_log_output(ctx->logfile, msg->source, type, msg->session, msg->data, sz);//日志输出
	}
	++ctx->message_count;
	int reserve_msg;
	if (ctx->profile) {
		ctx->cpu_start = skynet_thread_time();
		reserve_msg = ctx->cb(ctx, ctx->cb_ud, type, msg->session, msg->source, msg->data, sz);//回调
		uint64_t cost_time = skynet_thread_time() - ctx->cpu_start;
		ctx->cpu_cost += cost_time;//记录消耗时长
	} else {
		reserve_msg = ctx->cb(ctx, ctx->cb_ud, type, msg->session, msg->source, msg->data, sz);//触发回调
	}
	if (!reserve_msg) {
		skynet_free(msg->data);
	}
	CHECKCALLING_END(ctx)
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值