这个模块是把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进行参数传递与返回。