这个模块是把Linux下的epoll操作按照Lua Cfunction 的格式封装出来,供lua使用。
Lua要求每一个扩展模块,必须提供luaopen_XXX(lua_State *L) 作为模块的入口函数,此函数会在require加载模块时被调用到。我们就从这个函数开始分析:
static const struct luaL_Reg epoll[]={
{"setnonblocking",setnonblocking},
{"create",ep_create},
{"register",ep_event_add},
{"modify",ep_event_mod},
{"unregister",ep_event_del},
{"wait",ep_wait},
{"close",ep_close},
{NULL,NULL},
};
int luaopen_epoll(lua_State *L){
// register epoll table
luaL_register(L,"epoll",epoll);
#define SETCONST(EVENT) \
lua_pushnumber(L,EVENT); \
lua_setfield(L,-2,#EVENT)
// push const values into epoll table.
SETCONST(EPOLLIN);
SETCONST(EPOLLPRI);
SETCONST(EPOLLOUT);
SETCONST(EPOLLRDNORM);
SETCONST(EPOLLRDBAND);
SETCONST(EPOLLWRNORM);
SETCONST(EPOLLWRBAND);
SETCONST(EPOLLMSG);
SETCONST(EPOLLERR);
SETCONST(EPOLLHUP);
SETCONST(EPOLLRDHUP);
SETCONST(EPOLLONESHOT);
SETCONST(EPOLLET);
return 1;
}
该函数首先是调用luaL_register向 lua _G全局table中注册了epoll table,并将epoll结构体中的成员注册到epoll table中。看一下luaL_register 官方文档说明:
void luaL_register (lua_State *L,
const char *libname,
const luaL_Reg *l);
Opens a library.
When called with libname equal to NULL, it simply registers all functions in the list l (see luaL_Reg) into the table on the top of the stack.
When called with a non-null libname, luaL_register creates a new table t, sets it as the value of the global variable libname, sets it as the value of package.loaded[libname], and registers on it all functions in the list l. If there is a table in package.loaded[libname] or in variable libname, reuses this table instead of creating a new one.
In any case the function leaves the table on the top of the stack.
然后调用SETCONST宏来向epoll table中插入EVENTS数值。
luaopen_epoll函数返回后,epoll table的成员如下:
/*
epoll = {
setnonblocking = setnonblocking,
create = ep_create,
register = ep_event_add,
modify = ep_event_mod,
unregister = ep_event_del,
wait = ep_wait,
close = ep_close,
EPOLLIN = EPOLLIN,
EPOLLPRI = EPOLLPRI,
EPOLLOUT = EPOLLOUT,
EPOLLRDNORM = EPOLLRDNORM,
EPOLLRDBAND = EPOLLRDBAND,
EPOLLWRNORM = EPOLLWRNORM,
EPOLLWRBAND = EPOLLWRBAND,
EPOLLMSG = EPOLLMSG,
EPOLLERR = EPOLLERR,
EPOLLHUP = EPOLLHUP,
EPOLLRDHUP = EPOLLRDHUP,
EPOLLONESHOT = EPOLLONESHOT,
EPOLLET = EPOLLET,
};
*/
此时,在lua中执行:
local epoll = require("epoll")
local epfd = epoll.create()
就会调用到epoll table中名为create的函数,即ep_create,下面看一下其实现:
static int ep_create(lua_State *L){
int epfd;
if((epfd=epoll_create(1))==-1){
DSERR();
}
lua_pushinteger(L,epfd);
return 1;
}
跳过错误处理,可以发现非常的简单,就是调用epoll_create方法,然后将其返回值压入栈顶。如果epoll_create调用出错,则执行RSTERR宏:
#define DSERR() \
lua_pushnil(L); \
lua_pushstring(L,strerror(errno)); \
return 2
这个宏就是压入nil与error信息到栈中。
再看看如何注册一个事件:向epoll中注册一个可读事件
epoll.register(epfd, sfd, poll.EPOLLIN)
上述lua代码会调用到epoll table中名为register函数,即ep_event_add函数:
static int ep_event_add(lua_State *L){
int epfd,fd;
EVENTMASK eventmask;
epfd=luaL_checkint(L,1);
fd=luaL_checkint(L,2);
eventmask=luaL_checknumber(L,3);
struct epoll_event ev;
ev.data.fd=fd;
ev.events=eventmask;
if(epoll_ctl(epfd,EPOLL_CTL_ADD,fd,&ev)==-1){
DSERR();
}
lua_pushboolean(L,1);
return 1;
}
在调用上述函数时,栈的情况为:/*L stack: epfd, fd, eventmask*/。ep_event_add首先是把传入的参数从栈中取出,然后执行epoll_ctl(),最后把执行结果压入栈顶进行返回。
最后,看一下epoll.wait的使用:
events,err=epoll.wait(epfd,timeout,max_events)
lua执行epoll.wait函数,即执行ep_wait()函数:
static int ep_wait(lua_State *L){
int i,n,epfd,timeout,max_events;
epfd=luaL_checkint(L,1);
timeout=luaL_checkint(L,2);
max_events=luaL_checkint(L,3);
struct epoll_event events[max_events];
if((n=epoll_wait(epfd,events,max_events,timeout))==-1){
DSERR();
}
lua_newtable(L);
for(i=0;i<n;++i){
lua_pushinteger(L,events[i].data.fd);
lua_pushnumber(L,events[i].events);
lua_settable(L,-3);
}
return 1;
}
执行ep_wait()函数时,此时栈的情况为:/*L stack: epfd, timeout, max_events */。首先也是把传入的参数从栈中取出,然后执行epoll_wait(),然后把结果压入栈中进行返回。不过这里返回是一个table,以就绪的fd作为table的索引,值为对应的就绪事件,即t[events[i].data.fd] = events[i].events
其实只要自己动手写上一两个模块,就清楚lua C API的使用了,也就明白了Lua是如何通过栈与C进行参数传递与返回。