作者:shihuaping0918@163.com,轉(zhuǎn)載請(qǐng)注明作者
這篇文章是分析skynet消息注冊(cè)前的知識(shí)準(zhǔn)備殖卑,skynet的消息注冊(cè)系奉,C服務(wù)和lua服務(wù)設(shè)置回調(diào)走的函數(shù)是不同的昭抒。C的回調(diào)可以直接調(diào),但是lua的回調(diào)不行攒磨,它需要一個(gè)默認(rèn)的回調(diào)C函數(shù),將返回參數(shù)轉(zhuǎn)換為lua能理解的格式汤徽,遵循lua的api協(xié)議娩缰,傳遞到lua層。這個(gè)回調(diào)的名字叫_cb谒府。具體它是怎么工作的我們下一篇再分析拼坎,這一篇要先了解一些必要的lua api的規(guī)范,才能進(jìn)行下一步完疫。這個(gè)lua參考手冊(cè)是隨時(shí)需要翻的:http://www.lua.org/manual/5.3/manual.html
首先是lua調(diào)c泰鸡,lua到C層的參數(shù)是按順序來(lái)的,也就是說(shuō)第一個(gè)參數(shù)就是1壳鹤,第二個(gè)就是2....第n個(gè)就是n盛龄。從C層傳遞到lua層也是一樣,但是會(huì)多一個(gè)參數(shù)個(gè)數(shù)器虾。舉個(gè)栗子吧讯嫂,干講是不好理解。能看懂英文的同學(xué)最好詳細(xì)看一下兆沙,看不懂的呢欧芽,也沒什么大礙。
In order to communicate properly with Lua, a C function must use the following protocol,
which defines the way parameters and results are passed:
a C function receives its arguments from Lua in its stack in direct order (the first argument is pushed first).
So, when the function starts, lua_gettop(L) returns the number of arguments received by the function.
The first argument (if any) is at index 1 and its last argument is at index lua_gettop(L).
To return values to Lua, a C function just pushes them onto the stack,
in direct order (the first result is pushed first), and returns the number of results.
Any other value in the stack below the results will be properly discarded by Lua.
Like a Lua function, a C function called by Lua can also return many results.
As an example, the following function receives a variable number of numeric arguments and returns their average and their sum:
這是一個(gè)lua調(diào)C以后葛圃,C返回的例子
static int foo (lua_State *L) {
int n = lua_gettop(L); /* number of arguments */ 參數(shù)個(gè)數(shù)
lua_Number sum = 0.0;
int i;
for (i = 1; i <= n; i++) {
if (!lua_isnumber(L, i)) { //參數(shù)是不是數(shù)字
lua_pushliteral(L, "incorrect argument"); //不是數(shù)字報(bào)錯(cuò)
lua_error(L);
}
sum += lua_tonumber(L, i); //取第i個(gè)參數(shù)
}
lua_pushnumber(L, sum/n); /* first result */ 第一個(gè)返回值
lua_pushnumber(L, sum); /* second result */ 第二個(gè)返回值
return 2; /* number of results */ 一共兩個(gè)返回值
}
然后是C層調(diào)LUA的函數(shù)千扔,它的具體過程就是把參數(shù)填好,然后調(diào)LUA的函數(shù)库正。為了防止異常導(dǎo)致服務(wù)不穩(wěn)定曲楚,skynet中調(diào)用lua回調(diào)函數(shù)的時(shí)候使用的是lua_pcall,這個(gè)函數(shù)在lua層也是有的褥符,LUA層就叫pcall龙誊。
lua_pcall的介紹請(qǐng)大家到參考手冊(cè)里去找,我這里貼個(gè)參數(shù)列表就好了喷楣。
int lua_pcall (lua_State *L, int nargs, int nresults, int msgh);
lua_state就是lua環(huán)境了
nargs表示有多少個(gè)參數(shù)
nresults表示有多少個(gè)返回值
msgh為0表示返回初始錯(cuò)誤對(duì)象趟大,其它值表示消息處理函數(shù)所在的棧索引鹤树。
下面以skynet中的_cb函數(shù)來(lái)說(shuō)明一下C調(diào)用lua函數(shù)的場(chǎng)景。
static int
_cb(struct skynet_context * context, void * ud, int type, int session, uint32_t source, const void * msg, size_t sz) {
lua_State *L = ud;
int trace = 1;
int r;
int top = lua_gettop(L); //獲取棧參數(shù)個(gè)數(shù)
if (top == 0) {
lua_pushcfunction(L, traceback); //index為1逊朽,是一個(gè)錯(cuò)誤處理函數(shù)
lua_rawgetp(L, LUA_REGISTRYINDEX, _cb); //index為2罕伯,是lua的回調(diào)函數(shù)
} else {
assert(top == 2);
}
lua_pushvalue(L,2); //lua回調(diào)函數(shù)入棧
lua_pushinteger(L, type); //回調(diào)參數(shù)1,類型
lua_pushlightuserdata(L, (void *)msg); //回調(diào)參數(shù)2叽讳,消息體
lua_pushinteger(L,sz); //回調(diào)參數(shù)3追他,消息長(zhǎng)度
lua_pushinteger(L, session); //回調(diào)參數(shù)4,session
lua_pushinteger(L, source); //回調(diào)參數(shù)5岛蚤,source
//nargs為5邑狸,表示有5個(gè)參數(shù)
//nresults為0,表示返回值為0
//trace為1涤妒,表示出錯(cuò)時(shí)調(diào)用前面設(shè)置的traceback函數(shù)
r = lua_pcall(L, 5, 0 , trace);
if (r == LUA_OK) {
return 0;
}
const char * self = skynet_command(context, "REG", NULL);
switch (r) {
case LUA_ERRRUN:
skynet_error(context, "lua call [%x to %s : %d msgsz = %d] error : " KRED "%s" KNRM, source , self, session, sz, lua_tostring(L,-1));
break;
case LUA_ERRMEM:
skynet_error(context, "lua memory error : [%x to %s : %d]", source , self, session);
break;
case LUA_ERRERR:
skynet_error(context, "lua error in error : [%x to %s : %d]", source , self, session);
break;
case LUA_ERRGCMM:
skynet_error(context, "lua gc error : [%x to %s : %d]", source , self, session);
break;
};
lua_pop(L,1);
return 0;
}
根據(jù)上面的代碼分析推溃,當(dāng)服務(wù)是lua實(shí)現(xiàn)的時(shí)候,skynet底層核心框架在處理完消息以后届腐,回調(diào)lua層服務(wù)的回調(diào)函數(shù)時(shí)铁坎,要先經(jīng)過一次lua api協(xié)議的處理,將參數(shù)準(zhǔn)備好以后犁苏,然后調(diào)用lua服務(wù)中的回調(diào)函數(shù)硬萍。