這個模塊是把Linux下的epoll操做按照Lua Cfunction 的格式封裝出來,供lua使用。
函數
Lua要求每個擴展模塊,必須提供luaopen_XXX(lua_State *L) 做爲模塊的入口函數,此函數會在require加載模塊時被調用到。咱們就從這個函數開始分析:ui
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 官方文檔說明:this
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數值。
lua
luaopen_epoll函數返回後,epoll table的成員以下:
spa
/* 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中執行:code
local epoll = require("epoll") local epfd = epoll.create()
就會調用到epoll table中名爲create的函數,即ep_create,下面看一下其實現:
orm
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進行參數傳遞與返回。