skynet源碼分析(1)--模塊加載

作者:shihuaping0918@163.com,轉(zhuǎn)載請(qǐng)注明作者

兩個(gè)月前接觸skynet,最初使用的時(shí)候過程是相當(dāng)痛苦的,而且網(wǎng)絡(luò)上可以找到的學(xué)習(xí)資料并不多。當(dāng)時(shí)決定寫一些skynet相關(guān)的文章,最近終于有空了,開始寫這個(gè)東西。

skynet是云風(fēng)開源的一個(gè)游戲框架,底層是c,中間層和上層都是lua。基于actor模型,使用消息隊(duì)列進(jìn)行內(nèi)部通信。萬丈高樓平地起,先開始看最底層的內(nèi)容吧,因?yàn)樯蠈拥臅?huì)涉及一些業(yè)務(wù),而最底層的只涉及一些系統(tǒng)調(diào)用,理解起來更簡(jiǎn)單。

閱讀代碼使用的工具是eclipse cdt。代碼提交tag是f94ca6f

skynet底層代碼位于skynet/skynet-src下,模塊加載相關(guān)在skynet-module.c skynet-module.h這兩個(gè)文件里。這里的模塊在linux下指的是so,在windows下指的是dll,在skynet中指的是config中配置的cpath下的文件。

//以下四行為函數(shù)指針聲明
typedef void * (*skynet_dl_create)(void);
typedef int (*skynet_dl_init)(void * inst, struct skynet_context *, const char * parm);
typedef void (*skynet_dl_release)(void * inst);
typedef void (*skynet_dl_signal)(void * inst, int signal);

//單個(gè)模塊的結(jié)構(gòu)體
struct skynet_module {
    const char * name; //模塊名
    void * module; //模塊指針
    skynet_dl_create create;  //create函數(shù)
    skynet_dl_init init; //init函數(shù)
    skynet_dl_release release; //release函數(shù)
    skynet_dl_signal signal; //signal函數(shù)
};

//添加一個(gè)模塊
void skynet_module_insert(struct skynet_module *mod);
//查詢一個(gè)模塊
struct skynet_module * skynet_module_query(const char * name);
//某個(gè)模塊中的create函數(shù)調(diào)用
void * skynet_module_instance_create(struct skynet_module *);
//某個(gè)模塊中的init函數(shù)調(diào)用
int skynet_module_instance_init(struct skynet_module *, void * inst, struct skynet_context *ctx, const char * parm);
//某個(gè)模塊中的release函數(shù)調(diào)用
void skynet_module_instance_release(struct skynet_module *, void *inst);
//某個(gè)模塊中的signal函數(shù)調(diào)用
void skynet_module_instance_signal(struct skynet_module *, void *inst, int signal);
//初始化模塊管理
void skynet_module_init(const char *path);

從上面的代碼可以看出,每個(gè)模塊需要實(shí)現(xiàn)四個(gè)最基本的函數(shù),create/init/release/signal。注意這里并不是說函數(shù)名字叫這個(gè),函數(shù)名字具體叫什么下面會(huì)講到。


#define MAX_MODULE_TYPE 32

//這里定義了模塊列表數(shù)據(jù)結(jié)構(gòu)
struct modules {
    int count;
    struct spinlock lock;
    const char * path;
    struct skynet_module m[MAX_MODULE_TYPE]; //最多只能加載32個(gè)模塊
};

static struct modules * M = NULL;

//內(nèi)部函數(shù),打開一個(gè)動(dòng)態(tài)庫(kù)
static void *
_try_open(struct modules *m, const char * name) {
    const char *l;
    const char * path = m->path;
    size_t path_size = strlen(path);
    size_t name_size = strlen(name);

    int sz = path_size + name_size;
    //search path
    void * dl = NULL;
    char tmp[sz];
    //遍歷路徑查找so,路徑以;分隔
    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;
        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\n");
            exit(1);
        }
        //dlope打開so
        dl = dlopen(tmp, RTLD_NOW | RTLD_GLOBAL);
        path = l;
    }while(dl == NULL);

    if (dl == NULL) {
        fprintf(stderr, "try open %s failed : %s\n",name,dlerror());
    }

    return dl;
}

//根據(jù)模塊名在模塊列表中查找
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 *
get_api(struct skynet_module *mod, const char *api_name) {
    size_t name_size = strlen(mod->name);
    size_t api_size = strlen(api_name);
    char tmp[name_size + api_size + 1];
        //將模塊名附到tmp中
    memcpy(tmp, mod->name, name_size);
        //將方法名附到tmp中
    memcpy(tmp+name_size, api_name, api_size+1);
    char *ptr = strrchr(tmp, '.');
    if (ptr == NULL) {
        ptr = tmp;
    } else {
        ptr = ptr + 1;
    }
        // dlsym是一個(gè)系統(tǒng)函數(shù),根據(jù)函數(shù)名字獲取函數(shù)地址(指針)
    return dlsym(mod->module, ptr);
}

static int
open_sym(struct skynet_module *mod) {
    mod->create = get_api(mod, "_create");  //獲取create方法
    mod->init = get_api(mod, "_init");  //獲取init方法
    mod->release = get_api(mod, "_release");  //獲取release方法
    mod->signal = get_api(mod, "_signal");  //獲取signal方法

    return mod->init == NULL;  //然而這里只判定只要實(shí)現(xiàn)了init就可以了
}

//根據(jù)模塊名查找模塊
struct skynet_module * 
skynet_module_query(const char * name) {
        //先到列表里查
    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;
        //打開so
        void * dl = _try_open(M,name);
        if (dl) {
            M->m[index].name = name;
            M->m[index].module = dl;
            //獲取so中的init/create/release/signal方法地址
            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;
}

//添加模塊到模塊列表
void 
skynet_module_insert(struct skynet_module *mod) {
    SPIN_LOCK(M)

        //模塊是不是已經(jīng)在列表中了
    struct skynet_module * m = _query(mod->name);
    assert(m == NULL && M->count < MAX_MODULE_TYPE);
    int index = M->count;
    M->m[index] = *mod;
    ++M->count;

    SPIN_UNLOCK(M)
}

void * 
skynet_module_instance_create(struct skynet_module *m) {
    if (m->create) {
        return m->create(); //對(duì)應(yīng)上文說的,調(diào)用模塊的create函數(shù)
    } else {
        return (void *)(intptr_t)(~0);
    }
}

int
skynet_module_instance_init(struct skynet_module *m, void * inst, struct skynet_context *ctx, const char * parm) {
    return m->init(inst, ctx, parm); //對(duì)應(yīng)上文說的,調(diào)用模塊的init函數(shù)
}

void 
skynet_module_instance_release(struct skynet_module *m, void *inst) {
    if (m->release) {
        m->release(inst); //對(duì)應(yīng)上文說的,調(diào)用模塊的release函數(shù)
    }
}

void
skynet_module_instance_signal(struct skynet_module *m, void *inst, int signal) {
    if (m->signal) {
        m->signal(inst, signal); //對(duì)應(yīng)上文說的,調(diào)用模塊的release函數(shù)
    }
}

//初始化模塊列表數(shù)據(jù)結(jié)構(gòu)
void 
skynet_module_init(const char *path) {
    struct modules *m = skynet_malloc(sizeof(*m));
    m->count = 0;
    m->path = skynet_strdup(path);

    SPIN_INIT(m)

    M = m;
}

skynet_module_init在skynet-main.c中被調(diào)用,傳進(jìn)來的path是在運(yùn)行時(shí)config中配置的,如果config文件中沒有配置cpath,默認(rèn)將cpath的值設(shè)為./cservice/?.so,加載cpath目錄下的so文件。

從get_api可以看出來,skynet要求模塊的create/init/release/signal方法的命名是模塊名加一個(gè)下劃線,后面帶create/init/release/signal。在skynet/service-src目錄下有現(xiàn)成的例子,大家可以去看一下。

到這里,整個(gè)模塊加載功能就分析完了。從啟動(dòng)流程來分析是,首先在config文件中配置一個(gè)cpath,它包含了你想要加載的so的路徑。然后skynet-main.c在啟動(dòng)的時(shí)候會(huì)把cpath讀出來,設(shè)進(jìn)moduls->path中。在skynet-server.c中的skynet_context_new中會(huì)調(diào)用skynet_module_query,skynet_module_query首先會(huì)在列表中查詢so是否已經(jīng)加載,如果沒有就直接加載它。

模塊一定要包含有四個(gè)函數(shù)init/create/release/signal,它的命名格式為,假定模塊名為xxx,那么就是xxx_create/xxx_init/xxx_release/xxx_signal。這四個(gè)函數(shù)是干嘛用的?

create做內(nèi)存分配。init做初始化,它可能會(huì)做一些其它的事情,比如打開網(wǎng)絡(luò),打開文件,函數(shù)回調(diào)掛載等等。relase做資源回收,包括內(nèi)存資源,文件資源,網(wǎng)絡(luò)資源等等,signal是發(fā)信號(hào),比如kill信號(hào),告訴模塊該停了。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,698評(píng)論 6 539
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 99,202評(píng)論 3 426
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 177,742評(píng)論 0 382
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,580評(píng)論 1 316
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 72,297評(píng)論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,688評(píng)論 1 327
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,693評(píng)論 3 444
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,875評(píng)論 0 289
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,438評(píng)論 1 335
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 41,183評(píng)論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 43,384評(píng)論 1 372
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,931評(píng)論 5 363
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,612評(píng)論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,022評(píng)論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,297評(píng)論 1 292
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 52,093評(píng)論 3 397
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 48,330評(píng)論 2 377

推薦閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,702評(píng)論 25 708
  • 1三個(gè)相關(guān)數(shù)據(jù)結(jié)構(gòu). 關(guān)于socket的創(chuàng)建,首先需要分析socket這個(gè)結(jié)構(gòu)體,這是整個(gè)的核心。 104 str...
    ice_camel閱讀 2,867評(píng)論 1 8
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 134,816評(píng)論 18 139
  • 動(dòng)態(tài)鏈接,在可執(zhí)行文件裝載時(shí)或運(yùn)行時(shí),由操作系統(tǒng)的裝載程序加載庫(kù)。大多數(shù)操作系統(tǒng)將解析外部引用(比如庫(kù))作為加載過...
    小5筒閱讀 5,548評(píng)論 0 3
  • 作者:魚腸劍 落雨紛紛,為濕幾重。晴云叆叇,卻有離情。繁花蔭里,烏鵲悄聲。早蟬未鳴,難免清冷。戶有南牖,所隔萬重。...
    heyeyes閱讀 548評(píng)論 0 1