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のファイルを指します.
上記のコードから、各モジュールは4つの最も基本的な関数、create/init/release/signalを実現する必要があることがわかります.ここでは関数名をこれと呼ぶわけではありませんが、関数名の具体的な名前は以下に説明します.
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信号などの信号を送信し、モジュールに停止すべきだと伝えます.
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信号などの信号を送信し、モジュールに停止すべきだと伝えます.