skynet源码浏览系列从main函数开始(net源码使用教程)

汽水音乐 429 0

skynet 是C语言编辑 的框架,人类 选用 学习过程中最基本 的方法 去浏览 skynet,从C语言的main函数开始。

首先人类 找到 框架的入口main函数,在 skynet/skynet-src/skynet_main.c 文件内。

main函数的代码如下:

intmain(int argc, char *argv[]) {    const char * config_file = NULL ;    if (argc  1) {        config_file = argv[1];    } else {        fprintf(stderr, "Need a config file. Please read skynet wiki : ;}人类 一段一段查就这样看

intmain(int argc, char *argv[]) {    const char * config_file = NULL ;    if (argc  1) {        config_file = argv[1];    } else {        fprintf(stderr, "Need a config file. Please read skynet wiki :  赋值为启动时的第二个参数,也就是配置文件的路径。

skynet_globalinit();

// skynet/skynet-src/skynet_server.cstruct skynet_node {    ATOM_INT total;    int init;    uint32_t monitor_exit;    pthread_key_t handle_key;    bool profile;   // default is off};static struct skynet_node G_NODE;void skynet_globalinit(void) {    ATOM_INIT(&G_NODE.total , 0);    G_NODE.monitor_exit = 0;    G_NODE.init = 1;    if (pthread_key_create(&G_NODE.handle_key, NULL)) {        fprintf(stderr, "pthread_key_create failed");        exit(1);    }    // skynet/skynet-src/skynet_imp.h    /*        #define THREAD_WORKER 0        #define THREAD_MAIN 1        #define THREAD_SOCKET 2        #define THREAD_TIMER 3        #define THREAD_MONITOR 4    */    skynet_initthread(THREAD_MAIN);}skynet_initthread(int m) {    // skynet/skynet-src/atomic.h    // #define ATOM_POINTER volatile uintptr_t    uintptr_t v = (uint32_t)(-m);    pthread_setspecific(G_NODE.handle_key, (void *)v);}初始化全局节点信息 ,total 为0,monitor_exit 为0,init 1,

pthread_key_create(&G_NODE.handle_key, NULL) 创建了一个多线程私有资料 handle_key:

skynet_initthread(THREAD_MAIN); 将目前 线程情况 由 THREAD_MAIN 更换 为 THREAD_WORKER 情况 并记录在handle_key。

skynet_env_init();

// skynet/skynet-src/skynet_env.cstruct skynet_env {    struct spinlock lock;    lua_State *L;};static struct skynet_env *E = NULL;voidskynet_env_init() {    E = skynet_malloc(sizeof(*E));    SPIN_INIT(E)    E-L = luaL_newstate();}E 一个skynet_env结构体,结构体内包含一个 spinlock 自旋锁,一个lua虚拟机指针。

skynet_malloc 为结构体E分配内存,skynet_malloc内部暂时不细究。

SPIN_INIT(E)

通过查找代码得知, 这是在 skynet/skynet-src/spinlick.h 中定义的一个宏。#define SPIN_INIT(q) spinlock_init(&(q)-lock);对E中的lock 进行初始化。

E-L = luaL_newstate(); L绑定了一个lua虚拟机。

sigign();

#include signal.hint sigign() {    struct sigaction sa;    sa.sa_handler = SIG_IGN;    sa.sa_flags = 0;    sigemptyset(&sa.sa_mask);    sigaction(SIGPIPE, &sa, 0);    return 0;}main 函数同文件下的 sigign() 函数。

定义了一个 sigaction 结构体,将 sa_handler 设置为 SIG_IGN,表示要忽略信号的发生 的动作。

sigaction(SIGPIPE, &sa, 0); 将 SIGPIPE的行为代替 为 sa 结构体定义的形式,表示目前 进程忽略 SIGPIPE 信号。

这里简单记录了一下 sigaction 的资料。 

01ext_sigaction

struct skynet_config config;

定义了结构体 config

struct skynet_config {    int thread;    int harbor;    int profile;    const char * daemon;    const char * module_path;    const char * bootstrap;    const char * logger;    const char * logservice;};luaL_initcodecache();

// skynet/skynet-src/skynet_main.c#ifdef LUA_CACHELIB    luaL_initcodecache();#endif// skynet/3rd/lauxlib.cstatic struct codecache CC;struct codecache {    struct spinlock lock;    lua_State *L;};LUALIB_API voidluaL_initcodecache(void) {    SPIN_INIT(&CC);}static const char * load_config = "\    local result = {}\n\    local function getenv(name) return assert(os.getenv(name), [[os.getenv() failed: ]] .. name) end\n\    local sep = package.config:sub(1,1)\n\    local current_path = [[.]]..sep\n\    local function include(filename)\n\        local last_path = current_path\n\        local path, name = filename:match([[(.*]]..sep..[[)(.*)$]])\n\        if path then\n\            if path:sub(1,1) == sep then    -- root\n\                current_path = path\n\            else\n\                current_path = current_path .. path\n\            end\n\        else\n\            name = filename\n\        end\n\        local f = assert(io.open(current_path .. name))\n\        local code = assert(f:read [[*a]])\n\        code = string.gsub(code, [[%$([%w_%d]+)]], getenv)\n\        f:close()\n\        assert(load(code,[[@]]..filename,[[t]],result))()\n\        current_path = last_path\n\    end\n\    setmetatable(result, { __index = { include = include } })\n\    local config_name = ...\n\    include(config_name)\n\    setmetatable(result, nil)\n\    return result\n\&#34struct lua_State *L = luaL_newstate();luaL_openlibs(L);   // link lua libint err =  luaL_loadbufferx(L, load_config, strlen(load_config), "=[skynet config]", "t");assert(err == LUA_OK);lua_pushstring(L, config_file);err = lua_pcall(L, 1, 1, 0);if (err) {    fprintf(stderr,"%s\n",lua_tostring(L,-1));    lua_close(L);    return 1;}luaL_loadbufferx(L, load_config, strlen(load_config), "=[skynet config]", "t");

加载了一段lua代码到内存里,并压入lua栈内。

load_config 这段代码实现的功能: 将配置文件内的 $var 代替 成了环境变量的内容, 并返回了一个result表。

lua_pcall(L, 1, 1, 0);

执行压入的 load_config 代码块,第二个参数1 表示压入的栈的个数为1,lua_pushstring(L, config_file); 被压栈的配置文件名。 执行完函数之后,函数和参数全自动 出栈,此时 栈为空。 函数的返回值被压栈,此时 栈内只有一个表 result, result 内内含了 配置在 config_file 内的键值对。

_init_env(L);

static void_init_env(lua_State *L) {    lua_pushnil(L);  /* first key */    while (lua_next(L, -2) != 0) {        int keyt = lua_type(L, -2);        if (keyt != LUA_TSTRING) {            fprintf(stderr, "Invalid config table\n");            exit(1);        }        const char * key = lua_tostring(L,-2);        if (lua_type(L,-1) == LUA_TBOOLEAN) {            int b = lua_toboolean(L,-1);            skynet_setenv(key,b ? "true" : "false" );        } else {            const char * value = lua_tostring(L,-1);            if (value == NULL) {                fprintf(stderr, "Invalid config table key = %s\n", key);                exit(1);            }            skynet_setenv(key,value);        }        lua_pop(L,1);    }    lua_pop(L,1);}// skynet/skynet-src/skynet_env.cvoid skynet_setenv(const char *key, const char *value) {    SPIN_LOCK(E)        lua_State *L = E-L;    lua_getglobal(L, key);    assert(lua_isnil(L, -1));    lua_pop(L,1);    lua_pushstring(L,value);    lua_setglobal(L,key);    SPIN_UNLOCK(E)}// 从堆栈上弹出一个值,并将其设为全局变量 name 的新值。void lua_setglobal (lua_State *L, const char *name);// 把全局变量 name 里的值压栈,返回该值的类别 。int lua_getglobal (lua_State *L, const char *name);将lua栈表内的键值对设置到 &E-L 的全局环境中。

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);static intoptint(const char *key, int opt) {    const char * str = skynet_getenv(key);    if (str == NULL) {        char tmp[20];        sprintf(tmp,"%d",opt);        skynet_setenv(key, tmp);        return opt;    }    return strtol(str, NULL, 10);}// skynet/skynet-src/skynet_env.cconst char * skynet_getenv(const char *key) {    SPIN_LOCK(E)    lua_State *L = E-L;        lua_getglobal(L, key);    const char * result = lua_tostring(L, -1);    lua_pop(L, 1);    SPIN_UNLOCK(E)    return result;}optint, optstring, optboolean 从 &E-L 的全局环境中取得对应键的值,如果全局环境内未定义,则第二个参数 opt 设为 key的默认值。

lua_close(L);

关闭main函数内创建的 lua 虚拟机。

skynet_start(&config);

下一节的内容。

skynet_globalexit();

void skynet_globalexit(void) {    pthread_key_delete(G_NODE.handle_key);}删除在 skynet_initthread 中定义的固定 的线程资料 。

标签: 函数

抱歉,评论功能暂时关闭!