skynetソース分析(1)--モジュールロード

7456 ワード

作成者:[email protected]、転載は作者を明記してください
2ヶ月前にskynetに接触して、最初に使ったときの過程はかなり苦痛で、ネット上で見つけられる学習資料は多くありませんでした.当時はskynet関連の文章を書くことにしていましたが、最近やっと暇になって、これを書き始めました.
skynetは雲風開源のゲームフレームワークで、下層はcで、中間層と上層はluaです.actorモデルに基づいて,メッセージキューを用いて内部通信を行う.万丈のビルが平らになったら、まず最下層の内容を見ましょう.上層のものはいくつかの業務にかかわるので、最下層のものはいくつかのシステム呼び出しにしか及ばないので、理解するのはもっと簡単です.
コードを読むためのツールはeclipse cdtです.コードコミットtagはf 94 ca 6 f
skynetの下位コードはskynet/skynet-srcの下にあり、モジュールロードはskynet-moduleに関連する.c skynet-module.hこの2つのファイルにあります.ここでのモジュールはlinuxではso、windowsではdll、skynetではconfigで構成されたcpathのファイルを指します.
//           
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);

//        
struct skynet_module {
    const char * name; //   
    void * module; //    
    skynet_dl_create create;  //create  
    skynet_dl_init init; //init  
    skynet_dl_release release; //release  
    skynet_dl_signal signal; //signal  
};

//      
void skynet_module_insert(struct skynet_module *mod);
//      
struct skynet_module * skynet_module_query(const char * name);
//      create    
void * skynet_module_instance_create(struct skynet_module *);
//      init    
int skynet_module_instance_init(struct skynet_module *, void * inst, struct skynet_context *ctx, const char * parm);
//      release    
void skynet_module_instance_release(struct skynet_module *, void *inst);
//      signal    
void skynet_module_instance_signal(struct skynet_module *, void *inst, int signal);
//       
void skynet_module_init(const char *path);

上記のコードから、各モジュールは4つの最も基本的な関数、create/init/release/signalを実現する必要があることがわかります.ここでは関数名をこれと呼ぶわけではありませんが、関数名の具体的な名前は以下に説明します.

#define MAX_MODULE_TYPE 32

//             
struct modules {
    int count;
    struct spinlock lock;
    const char * path;
    struct skynet_module m[MAX_MODULE_TYPE]; //      32   
};

static struct modules * M = NULL;

//    ,       
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
"); 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
",name,dlerror()); } return dl; } // static struct skynet_module * _query(const char * name) { int i; for (i=0;icount;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 , ( ) 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; // init } // 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) // 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(); // , create } 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); // , init } void skynet_module_instance_release(struct skynet_module *m, void *inst) { if (m->release) { m->release(inst); // , release } } void skynet_module_instance_signal(struct skynet_module *m, void *inst, int signal) { if (m->signal) { m->signal(inst, signal); // , release } } // 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で呼び出され、転送されたpathは実行時にconfigで構成され、configファイルにcpathが構成されていない場合は、デフォルトでcpathの値を./cservice/?.so,cpathディレクトリの下のsoファイルをロードする.
get_からapiはskynetがモジュールのcreate/init/release/signalメソッドの命名を要求するのは、モジュール名に下線を付け、create/init/release/signalを付けることであることを示している.skynet/service-srcディレクトリの下に既成の例がありますので、見てみてください.
ここまで、モジュール全体のロード機能は分析済みです.起動プロセスから分析すると、まずconfigファイルにcpathを構成します.これには、ロードしたいsoのパスが含まれています.そしてskynet-main.c起動時にcpathを読み出しmoduls->pathに設定します.skynet-server.cのskynet_context_newでskynet_が呼び出されますmodule_query,skynet_module_queryはまずリストでsoがロードされたかどうかをクエリーし、なければ直接ロードします.
モジュールには必ず4つの関数init/create/release/signalが含まれています.その名前のフォーマットは、モジュール名がxxxであると仮定すると、xxx_です.create/xxx_init/xxx_release/xxx_signal.この4つの関数は何に使いますか.
createはメモリ割り当てをします.Initは初期化され、ネットワークを開く、ファイルを開く、関数コールバックマウントなど、他のことをする可能性があります.relaseは、メモリリソース、ファイルリソース、ネットワークリソースなど、リソースの回収を行い、signalはkill信号などの信号を送信し、モジュールに停止すべきだと伝えます.