lua-epoll 模塊簡單分析

    這個模塊是把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進行參數傳遞與返回。

相關文章
相關標籤/搜索