lua-epoll 模块简单分析

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

你可能感兴趣的:(lua-epoll 模块简单分析)