1.作为skynet的启动文件,主要完成了一些初始化和读取并存取配置文件内容的工作. 在这里只将代码读取配置文件的部分抽取出来,就算没有skynet环境,这些代码也是可以运行的,了解以后再对照源码进行分析,希望能对理解skynet带来一些帮助

#include "lua.h"
#include "lualib.h"
#include "lauxlib.h" #include <signal.h>
#include <assert.h>
#include "env.h" struct config
{
int thread;
int harbor;
const char *deamon;
const char *module_path;
const char *bootstrap;
const char *logger;
const char *logservice;
}; void popn(lua_State *L, int n); static int
optint(const char *key, int opt)
{
const char *str = env_getenv(key);
if (str == NULL)
{
char tmp[];
sprintf(tmp, "%d", opt);
env_setenv(key, tmp);
return opt;
}
return strtol(str, NULL, );
} static const char *
optstring(const char *key, const char *opt)
{
const char *str = env_getenv(key);
if (str == NULL)
{
if (opt)
{
env_setenv(key, opt);
opt = env_getenv(key);
}
return opt;
}
return str;
} static const char * load_config = "\
local config_name = ...\
local f = assert(io.open(config_name))\
local code = assert(f:read \'*a\')\
print(\"code is \", code)\
local function getenv(name) return assert(os.getenv(name), \'os.getenv() failed: \' .. name) end\
code = string.gsub(code, \'%$([%w_%d]+)\', getenv)\
f:close()\
print(\"code after replace is \", code)\
local result = {}\
assert(load(code,\'=(load)\',\'t\',result))()\
return result\
"; static void
_init_env(lua_State *L)
{
lua_pushnil(L);
while(lua_next(L, -) != )
{
int keyt = lua_type(L, -);
if (keyt != LUA_TSTRING)
{
fprintf(stderr, "Invalid, config table\n");
exit();
} const char *key = lua_tostring(L, -);
if (lua_type(L, -) == LUA_TBOOLEAN)
{
int b = lua_toboolean(L, -);
env_setenv(key, b ? "true" : "false");
}
else
{
const char *value = lua_tostring(L, -);
if (value == NULL)
{
fprintf(stderr, "Invalud config table key = %s\n", key);
exit();
}
env_setenv(key, value);
}
lua_pop(L, );
}
lua_pop(L, );
} void popn(lua_State *L, int n)
{
lua_pop(L, -(n) - );
} int
sigign()
{
struct sigaction sa;
sa.sa_handler = SIG_IGN;
sigaction(SIGPIPE, &sa, );
return ;
} void
init_conf(struct config *conf)
{
conf->thread = optint("thread", );
conf->module_path = optstring("cpath", "./cservice/?.so");
conf->harbor = optint("harbor", );
conf->bootstrap = optstring("bootstrap", "snlua bootstrap");
conf->deamon = optstring("deamon", NULL);
conf->deamon = optstring("logger", NULL);
conf->logservice = optstring("logservice", "logger");
} void test_env()
{
printf("thread: %s\n", env_getenv("thread"));
printf("harbor: %s\n", env_getenv("harbor"));
} int
main(int argc, char *argv[])
{
const char *config_file = NULL;
if (argc > )
{
config_file = argv[];
}
else
{
fprintf(stderr, "Need a config file. Please read skynet wiki : https://github.com/cloudwu/skynet/wiki/Config\n"
"usage: skynet configfilename\n");
} sigign();
env_env_init(); struct config conf;
struct lua_State *L = luaL_newstate();
luaL_openlibs(L);
int err = luaL_loadstring(L, load_config);
assert(err == LUA_OK);
lua_pushstring(L, config_file); err = lua_pcall(L, , , );
if (err)
{
fprintf(stderr, "%s,\n", lua_tostring(L, -));
lua_close(L);
return ;
}
_init_env(L); init_conf(&conf); test_env(); lua_close(L); return ;
}

首先看main函数:

const char *config_file = NULL;
if (argc > )
{
config_file = argv[];
}
else
{
fprintf(stderr, "Need a config file. Please read skynet wiki : https://github.com/cloudwu/skynet/wiki/Config\n"
"usage: skynet configfilename\n");
}

这句话是读取配置文件路径:正如运行skynet的命令行为:./skynet ./examples/config.我将config拿出来放到和可执行文件用级,运行方式为: ./test ./config

于是config_file = argv[1]便保存了配置文件名.

下面两句: sigign(); env_env_init();

设置信号处理函数,调用env_env_init()初始化保存配置文件的环境,该函数在env.c中,对应的是skynet_env.c(这里内容是一样的),代码如下:

env.c:

#include "env.h"
#include "spinlock.h"
#include "lauxlib.h"
#include "lua.h"
#include <stdlib.h>
#include <assert.h>
#include <error.h>
#include <string.h> struct env
{
struct spinlock lock;
lua_State *L;
}; static struct env *E = NULL; const char*
env_getenv(const char *key)
{
SPIN_LOCK(E);
lua_State *L = E->L;
lua_getglobal(L, key);
const char *result = lua_tostring(L, -);
//nil may return if there is no such a key-pair.
lua_pop(L, );
SPIN_UNLOCK(E); return result;
} void
env_setenv(const char *key, const char *value)
{
SPIN_LOCK(E);
lua_State *L = E->L;
lua_getglobal(L, key);
//after getglobal , if there is a value crosponds to the key , then it will be pushed onto stack, otherwise nil is pushed
assert(lua_isnil(L, -));
lua_pop(L, ); //pop nil from the stack
lua_pushstring(L, value);
lua_setglobal(L, key); //after setglobal the value on the stack will be poped. SPIN_UNLOCK(E);
} void
env_env_init()
{
E = (struct env*)malloc(sizeof(*E));
SPIN_INIT(E);
E->L = luaL_newstate();
}

由此可见env.c创建了静态变量 static struct env *E 来保存配置。

继续main函数:

struct config conf;
struct lua_State *L = luaL_newstate();
luaL_openlibs(L);
int err = luaL_loadstring(L, load_config);
assert(err == LUA_OK);
lua_pushstring(L, config_file); err = lua_pcall(L, , , );
if (err)
{
fprintf(stderr, "%s,\n", lua_tostring(L, -));
lua_close(L);
return ;
}
_init_env(L);

这几句话便完成了读取配置文件并保存到env.c中的E的lua虚拟机里.

int err = luaL_loadstring(L, load_config);

这句话将load_config作为一个chunk加载到打开的L的函数堆栈里,我们看一下load_config:

static const char * load_config = "\
local config_name = ...\
local f = assert(io.open(config_name))\
local code = assert(f:read \'*a\')\
print(\"code is \", code)\
local function getenv(name) return assert(os.getenv(name), \'os.getenv() failed: \' .. name) end\
code = string.gsub(code, \'%$([%w_%d]+)\', getenv)\
f:close()\
print(\"code after replace is \", code)\
local result = {}\
assert(load(code,\'=(load)\',\'t\',result))()\
return result\
";

发现load_config其实是一段lua代码字符串,load到函数栈上以后,便可以执行,

但是根据 local config_name = ...\ 看出,需要传入一个参数,也就是配置文件的路径 那么如何传呢?继续看main函数 lua_pushstring(L, config_file); 这句话便将config_file这个字符串放到了栈上供

int err = luaL_loadstring(L, load_config); 产生的chunk调用.

于是执行 err = lua_pcall(L, 1, 1, 0); if (err) { fprintf(stderr, "%s,\n", lua_tostring(L, -1)); lua_close(L); return 1; }

这句话便是开始执行lua函数栈上的chunk,参数为刚才push进去的config_file,那么chunk做了哪些事情呢?

local f = assert(io.open(config_name))\

local code = assert(f:read \'*a\')\

首先打开配置文件,并读取所有内容,接下来定义了一个函数:

local function getenv(name) return assert(os.getenv(name), \'os.getenv() failed: \' .. name) end\

这个函数执行返回 os.getenv(name), 也就是name为环境变量的名称,这里得到其值例如: print(os.getenv("HOME")),便会输出$(HOME)对应的值,

接下来:code = string.gsub(code, \'%$([%w_%d]+)\', getenv)\

可以看到用string.gsub来讲code(配置文件内容)中的$(...)环境变量用getenv中得到的来替换

.但是skynet中得config 没有类似的环境变量,所以得到的code没有变化.

接下来

f:close()\

local result = {}\

assert(load(code,\'=(load)\',\'t\',result))()\

return result\

关闭配置文件,将code load到result表中,然后返回result表,

我们注意到我们调用lua_pcall(L, 1,1,0),因此,result表此时在栈顶的位置. 到此为止,配置文件的内容已经存放在result表中,并且这个表在lua调用栈的栈顶

,接下来便是从栈上的表中读取表中的内容,然后存放到env中: 紧接着调用

_init_env(L);

static void
_init_env(lua_State *L)
{
lua_pushnil(L);
while(lua_next(L, -) != )
{
int keyt = lua_type(L, -);
if (keyt != LUA_TSTRING)
{
fprintf(stderr, "Invalid, config table\n");
exit();
} const char *key = lua_tostring(L, -);
if (lua_type(L, -) == LUA_TBOOLEAN)
{
int b = lua_toboolean(L, -);
env_setenv(key, b ? "true" : "false");
}
else
{
const char *value = lua_tostring(L, -);
if (value == NULL)
{
fprintf(stderr, "Invalud config table key = %s\n", key);
exit();
}
env_setenv(key, value);
}
lua_pop(L, );
}
lua_pop(L, );
}

这个函数是一个典型的读取table中以字符串作为键的table的内容,尤其是不知道table中的具体内容是什么的时候.

这里我们只知道是以键值对形式,且键是字符串,table为非数组。

上边我们知道此时lua函数栈的栈顶为存放了配置文件内容的table,那么这个函数就是挨个遍历table内容,并调用env_env_set()来存放。

首先lua_next(L, -2)先将栈顶元素弹出然后将table中的一个键值对放到栈上,键在-2位置上,值在-1位置上。因为lua_next(L, -2)先弹栈顶元素,因此在调用之前先pushnil,放进一个nil在栈顶,调用lua_next() nil 被弹出 然后table中的第一个键值对依次放到栈上,然后获得键值,调用 env_setenv()来存放内容到E。循环读取table的值,直到读完跳出循环。至此,config文件中的内容全部存放到env中的全局变量E中的虚拟机中,并可在其他地方调用来获得配置文件内容. 最后: init_conf(&conf); test_env(); lua_close(L); 其实就是仿照skynet_main.c来测试,test_env()可以替换成skynet_start(&conf)来继续启动skynet,这里简单的用来测试。 到此为止,读取config配置文件的工作就做完了,并保存到了env.c中的局部静态变量里。供其它地方使用配置。

基础有限,研究skynet时间有限,难免讲述不清或出现错误,望指正。 最后贴出全部测试文件代码:

----------------------------------------------------------------------------------------------------------

start.c, env.c的代码见上

env.h

#ifndef __ENV_H_
#define __ENV_H_ const char* env_getenv(const char*key);
void env_setenv(const char *key, const char *value);
void env_env_init(); #endif

spinlock.h

#ifndef SKYNET_SPINLOCK_H
#define SKYNET_SPINLOCK_H #define SPIN_INIT(q) spinlock_init(&(q)->lock);
#define SPIN_LOCK(q) spinlock_lock(&(q)->lock);
#define SPIN_UNLOCK(q) spinlock_unlock(&(q)->lock);
#define SPIN_DESTROY(q) spinlock_destroy(&(q)->lock); #ifndef USE_PTHREAD_LOCK struct spinlock {
int lock;
}; static inline void
spinlock_init(struct spinlock *lock) {
lock->lock = ;
} static inline void
spinlock_lock(struct spinlock *lock) {
while (__sync_lock_test_and_set(&lock->lock,)) {}
} static inline int
spinlock_trylock(struct spinlock *lock) {
return __sync_lock_test_and_set(&lock->lock,) == ;
} static inline void
spinlock_unlock(struct spinlock *lock) {
__sync_lock_release(&lock->lock);
} static inline void
spinlock_destroy(struct spinlock *lock) {
(void) lock;
} #else #include <pthread.h> // we use mutex instead of spinlock for some reason
// you can also replace to pthread_spinlock struct spinlock {
pthread_mutex_t lock;
}; static inline void
spinlock_init(struct spinlock *lock) {
pthread_mutex_init(&lock->lock, NULL);
} static inline void
spinlock_lock(struct spinlock *lock) {
pthread_mutex_lock(&lock->lock);
} static inline int
spinlock_trylock(struct spinlock *lock) {
return pthread_mutex_trylock(&lock->lock) == ;
} static inline void
spinlock_unlock(struct spinlock *lock) {
pthread_mutex_unlock(&lock->lock);
} static inline void
spinlock_destroy(struct spinlock *lock) {
pthread_mutex_destroy(&lock->lock);
} #endif #endif

config

root        = "./"
thread =
--logger = nil
logger = "userlog"
logservice = "snlua"
--logservice = "catlogger"
logpath = "./../../log/cat/"
harbor =
address = "127.0.0.1:2401"
master = "127.0.0.1:2013"
start = "main" -- main script
bootstrap = "snlua bootstrap" -- The service for bootstrap
standalone = "0.0.0.0:2013"
luaservice = "./../../service/logind/?.lua;./../../service/db/?.lua;./../../service/web/?.lua;./../../service/cat/?.lua;./../../service/?.lua;./service/?.lua"
lualoader = "lualib/loader.lua"
preload = "./../../lualib/preload.lua" -- run preload.lua before every lua service run
snax = root.."../logind/?.lua;"..root.."test/?.lua"
-- snax_interface_g = "snax_g"
cpath = "./../../cservice/?.so;./cservice/?.so"
-- daemon = "./skynet.pid"

makefile:

CC= gcc
CFLAGS= -g -O2 -Wall
LUA_INC= /usr/include/lua5. all: main main : start.c env.c env.h spinlock.h
$(CC) $(CFLAGS) -o $@ $^ -I$(LUA_INC) -llua5. -lpthread -lm clean :
rm -f main

skynet启动读取配置文件浅析(skynet_main.c)的更多相关文章

  1. 【转载】Linux启动初始化配置文件浅析(解决source /etc/profile重启后就失效?)

    1)/etc/profile   登录时,会执行. 全局(公有)配置,不管是哪个用户,登录时都会读取该文件. (2)/ect/bashrc   Ubuntu没有此文件,与之对应的是/ect/bash. ...

  2. Linux启动初始化配置文件浅析

    转自:http://blog.51cto.com/19055/1144600 1)/etc/profile   登录时,会执行. 全局(公有)配置,不管是哪个用户,登录时都会读取该文件. (2)/ec ...

  3. MySQL读取配置文件的顺序、启动方式、启动原理

    一.MySQL读取配置文件的顺序 读取顺序:/etc/my.cnf > /etc/mysql/my.cnf > /usr/etc/my.cnf > ~/.my.cnf 命令验证:[r ...

  4. 【一起学源码-微服务】Nexflix Eureka 源码二:EurekaServer启动之配置文件加载以及面向接口的配置项读取

    前言 上篇文章已经介绍了 为何要读netflix eureka源码了,这里就不再概述,下面开始正式源码解读的内容. 如若转载 请标明来源:一枝花算不算浪漫 代码总览 还记得上文中,我们通过web.xm ...

  5. skynet启动流程及调用服务

     3.基本原理 3.1启动流程  1.skynet-src/skynet_main.c 这个是main()函数所在,主要就是设置一下lua的环境.默认的配置.打开config配置文件,并修改默认配置. ...

  6. nova读取配置文件流程

          在我们安装nova的过程中,设置它的配置文件/etc/nova/nova.conf是必不可少的一步.配置好nova.conf文件,nova-compute.nova-network等服务才 ...

  7. skynet启动过程_bootstrap

    这遍摘自skynet 的wiki skynet 由一个或多个进程构成,每个进程被称为一个 skynet 节点.本文描述了 skynet 节点的启动流程. skynet 节点通过运行 skynet 主程 ...

  8. SparkStreaming动态读取配置文件

    SparkStreaming动态读取配置文件 标签: SparkStreaming HDFS 配置文件 MySql 需求 要实现SparkStreaming在流处理过程中能动态的获取到配置文件的改变 ...

  9. .Net Core从命令行读取配置文件

    最近在学习博客园腾飞(jesse)的.Net Core视频教程,收益匪浅,在此作推荐 : http://video.jessetalk.cn/ 言归正传,.Net Core应用程序中如何通过命令行读取 ...

随机推荐

  1. 在javascript中NodeList和Array的区别及转换方法

    随着深入理解javascript 后对于一些小知识的了解慢慢加深,这里说的是关于nodelist和array的区别,相信你一定用过toarray()方法,但是这里通过js 的方法讲解nodelist ...

  2. iOS 导航栏rgb值与设置的有差异

    转:http://b2cloud.com.au/how-to-guides/bar-color-calculator-for-ios7-and-ios8/ 计算:http://htmlpreview. ...

  3. 【VUE】VUE相关学习和知识备份

    一.学习资料参考 1.1.Vue.js 官网:Vue.js https://cn.vuejs.org/ 官方文档:介绍 - Vue.js https://cn.vuejs.org/v2/guide/ ...

  4. 微软MVP Round Table

    2017年7月7日,微软VS圈子的老大兼女神Julia(潘正磊)以及Peter Hu等人,和若干MVP一起在进行了一次Round Table讨论. 讨论过程中主要针对VS和TFS/VSTS相关的功能. ...

  5. 巨蟒python全栈开发-第20天 核能来袭-约束 异常处理 MD5 日志处理

    一.今日主要内容 1.类的约束(对下面人的代码进行限制;项目经理的必备技能,要想走的长远) (1)写一个父类,父类中的某个方法要抛出一个异常 NotImplementedError(重点) (2)抽象 ...

  6. 转!!DNS域名解析使用的是TCP协议还是UDP协议?

    原文地址:https://segmentfault.com/a/1190000006100959 DNS同时占用UDP和TCP端口53是公认的,这种单个应用协议同时使用两种传输协议的情况在TCP/IP ...

  7. 编译Elasticsearch源码

    1.从github上clone  es的源码 git clone https://github.com/elastic/elasticsearch.git 2.如果没有安装gradle的话,需要安装g ...

  8. 编程算法 - 全然背包问题 代码(C)

    全然背包问题 代码(C) 本文地址: http://blog.csdn.net/caroline_wendy 题目: 有n个重量和价值分别为w,v的物品, 从这些物品中挑选出总重量不超过W的物品, 求 ...

  9. 六顶思维帽的思考,敏捷开发?——By Me

    人类的思维可以分为很多种,其中按照思维的深度和广度的侧重,可以分为纵向思维和横向思维两种: 简单的来说,“六顶思维帽”可以简单的理解为下图所示: 如何使用这种思维方式呢?举个例子:先输入一个待讨论的事 ...

  10. 启动一个支持文件上传的HTTP-Server

    Python实现,源码来自网络,代码内部有作者信息. HTTP方式共享文件,对于不需要用户名和密码验证的系统非常方便.通过浏览器就可以实现文件上传和下载.非常适合用作测试系统的脚手架. 对于系统使用c ...