1. 개요


    이 문서는 시리얼 모듈을 루아에서 동작 시키기 위한 기능 중에서
    시리얼 통신의 이벤트 방식 프로그램 지원을 위한 poll 객체 구현 내용을 기록한 문서이다.
   
    이 문서에 대한 시험은 EZ-S3C2440 으로 하였다.
   
    이번에도 루아의 사용법 소개와 소스 내용까지만 다룬다.
    실제 C 에서 어떻게 구현했는가에 대한 소스 설명은 다음에
    다룬다.

2. poll 객체


    poll 객체는 다음과 같은 기능을 제공한다.
   
        - poll 이벤트 등록
        - poll 함수
        - poll 이벤트 체크

       
  2.1 poll 객체 생성


    이벤트 처리를 지원하기 위해서는 poll 객체를 생성해야 한다.
   
    생성은 다음과 같다.

p = require "poll"; -- 폴 모듈 생성 
poll = p.create(4); -- poll 객체 생성
    
            :
    
p = nil;            -- 실제 소멸
 

  2.2 poll 이벤트 초기화 및 등록


    원래 C 의 poll 함수의 이벤트 종류가 다양하지만 poll 객체에서는
    가장 많이 쓰인느 다음과 같은 이벤트만 지원한다.
   
        - 시간초과 이벤트
        - 에러이벤트
        - 읽기이벤트
        - 쓰기이벤트
   
    현재 설계된 이벤트는 수행 중간에 이벤트의 삭제나 추가는 되지 않는다.
    즉 TCP 포트 연결과 같은 이벤트 추가는 지원되지 않고 있다.
    이후에 추가될 예정이다.
   
    이벤트의 초기화 및 등록은 다음과 같은 형식으로 사용되어야 한다.

poll.clear();
poll.add_event( com.fd,true);

    clear() 는 이벤트의 목록을 초기화 하는 과정이다.
    즉 이 함수가 호출되면 이전에 등록된 모든 이벤트가 제거된다.
    add_event() 함수는 새로운 이벤트를 등록하게 된다. 이 함수의 파라메터는 다음과 같다.
   
    add_event( 파일디스크립터 , 읽기 이벤트 조건, 에러 이벤트 조건, 쓰기 이벤트 조건  );
   
    만약에 파라메터를 생략하면 false 즉 해당 이벤트는 체크 하지 않게 된다.
   
  2.3 poll 동작

   

    C 라이브러리의 poll() 함수를 호출하는 것은 poll 객체의 wait() 함수 이다.
    이 함수는 파라메터 인자로 시간 초과 값(밀리초) 을 지정해야 한다.

 poll.wait( 1000 ) 

    이 함수의 반환은  내부적으로 poll() 함수에 실패한 것인데 이 때는
    대부분 종료 처리 하는 것이 많다. (파일 디스크립터를 잘못 지정하는 것과 같은 프로그램 버그이거나
    시스템에 치명적 문제로 인한 종료 이기 때문이다.
   
  2.4 시간 초과 이벤트 체크


    wait() 함수를 빠져 나오는 경우는 이벤트가 발생하거나 아무 이벤트가 발생하지 않아서 시간초과가 발생한 경우이다.
   
    이때 시간 초과 검사를 가장 먼저 해야 하는데 이때 사용하는 함수가 check_event_read() 이다.

 if poll.check_event_timeout() then 

    // 아무런 이벤트 없이 시간 초과 발생
   
else

    :
end;

  2.5 이벤트 체크 및 처리 예


    시간 초과가 아니라면 이벤트가 발생한 경우인데 이때는 각 이벤트별로 다음 함수들을 사용하여 체크해야 한다.

poll.check_event_read(fd);  // fd 에 읽기 이벤트가 발생했는가?
poll.check_event_error(fd); // fd 에 에러 이벤트가 발생했는가?
poll.check_event_write(fd); // fd 에 쓰기 이벤트가 발생했는가?

    만약 읽기 이벤트가 발생했다면 다음과 같이 해당 이벤트에 따라서 읽기 처리를 해 주어야 한다.

 if poll.check_event_read( com.fd ) then

    print( "serial read event" );
    rxsize, rxdata = com.read();
    if rxsize > 0 then
        printf( "    read size = %d", rxsize, rxdata );
        printf( ">> [%s]",rxdata );
        printf( "\n" );
    end;    
end;

  2.6 시험용 루아 데모 소스

 

#!/bin/lua
-- #!/mnt/nfs/lua/lua
printf = function(s,...) return io.write(s:format(...)) end;
printf( ">> poll test\n" );
m = require "serial";
p = require "poll";
    printf( ">> serial port list\n" );
    spl = m.port_list();
    printf( "   total = %d\n", spl.count );
    printf( "   list  = " ); print( spl );
    spl.make_device_file();
    poll = p.create(4);
    printf( ">> com create\n" );
    com  = m.create();
    com.port    = "/dev/ttyS1";
    com.baud    = 115200;
    com.data    = 8;
    com.parity = "none";
    printf( "    port   = %s\n", com.port );
    printf( "    baud   = %d\n", com.baud );
    printf( "    data   = %d\n", com.data );
    printf( "    parity = %s\n", com.parity );
    com.open();
    while true do
        poll.clear();
        poll.add_event( com.fd,true);
        if poll.wait( 1000 ) == false then break; end;
        if poll.check_event_timeout() then 
            print( "poll time out" );
            com.write("serial write\r\n" );
        else
            print( "poll event" );
            if poll.check_event_read( com.fd ) then
                print( "serial read event" );
                rxsize, rxdata = com.read();
                if rxsize > 0 then
                    printf( "    read size = %d", rxsize, rxdata );
                    printf( ">> [%s]",rxdata );
                    printf( "\n" );
                end;    
            end;
        end;
    end;
    com.close();
p = nil;
m = nil;

3. 구현 소스 목록


    2 항에서 설명한 내용을 구현하기 위한 소스 구성은 다음과 같다.

    Makefile            : 컴파일을 위한 메이크 파일
    run.lua             : 시험을 위한 루아 스크립트
    poll.c              : poll 모듈를 위한 기본 구성 소스

4. poll.c 소스


#include <stdarg.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/poll.h>
#define LUA_LIB
#include <lua.h>
#include <lauxlib.h>
#define POLL_MODULE    "POLL_MODULE"
#define POLL            "POLL"
typedef struct 
{
    int             max_count;
    struct pollfd   *ufds;
    
    int             count;
    int             wait_time;
    int             poll_result;
    
} poll_t;
static poll_t *call_func_pol = NULL;
static poll_t *poll_new_userdata(lua_State *L)
{
  poll_t *pol = (poll_t *) lua_newuserdata(L, sizeof(poll_t));
  luaL_getmetatable(L, POLL);
  lua_setmetatable(L, -2);
  return pol;
}
static poll_t * poll_get_data(lua_State *L, int index)
{
    poll_t *pol = (poll_t *) lua_touserdata(L, index);
    if (pol == NULL) luaL_typerror(L, index, POLL);
    return pol;
}
int poll_create( lua_State *L )
{
    poll_t *pol;
    pol = poll_new_userdata(L);
    pol->max_count = lua_tointeger( L, 1 );
    if( pol->max_count < 0 ) pol->max_count = 0;
    if( pol->max_count == 0 )   pol->ufds = NULL;
    else                        pol->ufds = malloc( pol->max_count * sizeof( struct pollfd ) ); 
    pol->count      = 0;
    pol->wait_time  = 0;
    return 1;
}
static int poll_index( lua_State *L )
{
    poll_t  *pol;
    char    *var_str; 
    if( lua_type(L, 2) == LUA_TSTRING ) 
    {
        pol         = poll_get_data( L, 1 );
        var_str = (char *) lua_tostring( L, 2 );          
        if     ( !strcmp( var_str, "wait"                   ) ) { call_func_pol = pol; }
        else if( !strcmp( var_str, "clear"                  ) ) { call_func_pol = pol; }
        else if( !strcmp( var_str, "check_event_timeout"    ) ) { call_func_pol = pol; }
        else if( !strcmp( var_str, "add_event"              ) ) { call_func_pol = pol; }
        else if( !strcmp( var_str, "check_event_read"       ) ) { call_func_pol = pol; }
        else if( !strcmp( var_str, "check_event_error"      ) ) { call_func_pol = pol; }
        else if( !strcmp( var_str, "check_event_write"      ) ) { call_func_pol = pol; }
        lua_getmetatable(L, 1);
        lua_replace(L, 1);
        lua_rawget(L, 1);
        return 1;
    }
    return 0;
}
static int poll_tostring (lua_State *L)
{
    poll_t *pol;
    pol = poll_get_data( L, 1 );
    lua_pushstring ( L, "poll_object" );
    return 1;
}
static int poll_gc(lua_State *L)
{
    poll_t *pol;
    pol = poll_get_data( L, 1 );
    return 0;
}
static int poll_wait(lua_State *L)
{
    poll_t  *pol;
    int     ret;
    pol = call_func_pol;
    pol->poll_result = -1;
    ret = -1;
    pol->wait_time = lua_tointeger( L, 1 );
    pol->poll_result = poll( pol->ufds, pol->count, pol->wait_time );
    if( pol->poll_result >= 0 )     lua_pushboolean(L, 1);
    else                            lua_pushboolean(L, 0);
    call_func_pol = NULL;
    return 1;   
}
static int poll_clear(lua_State *L)
{
    poll_t          *pol;
    struct  pollfd  *poll_item;
    int             lp;
    pol = call_func_pol;
    pol->count = 0;
    call_func_pol = NULL;
    if( pol->max_count  == 0    ) return 0; 
    if( pol->ufds       == NULL ) return 0; 
    memset( pol->ufds, 0, sizeof( struct pollfd ) * pol->max_count );
    poll_item = pol->ufds;
    for( lp = 0; lp < pol->max_count; lp++ ) 
    {
        poll_item->fd = -1;
        poll_item++;
    }
    return 0;
}
static int poll_add_event(lua_State *L)
{
    poll_t          *pol;
    struct  pollfd  *poll_item;
    int             lp;
    int             append_fd;
    int             append_read;
    int             append_error;
    int             append_write;
    pol = call_func_pol;
    append_fd       = lua_tointeger( L, 1 );
    append_read     = lua_toboolean( L, 2 );
    append_error    = lua_toboolean( L, 3 );
    append_write    = lua_toboolean( L, 4 );
    if( pol->max_count  == 0            ) return 0; 
    if( pol->ufds       == NULL         ) return 0; 
    if( pol->max_count  <= pol->count   ) return 0;
    poll_item = pol->ufds;  
    for( lp = 0; lp < pol->max_count; lp++ ) 
    {
        if( poll_item->fd == append_fd )
        {
            printf( "error - same fd %d\n", append_fd );
            break;
        }   
        if( poll_item->fd == -1 )
        {
            poll_item->fd = append_fd;
            poll_item->events   = 0;
            poll_item->revents  = 0;
            if( append_read     ) poll_item->events  |= POLLIN  ;
            if( append_error    ) poll_item->events  |= POLLERR  ;
            if( append_write    ) poll_item->events  |= POLLOUT     ;
            if( poll_item->events == 0 )
            {
                poll_item->fd = -1;
                printf( "error - append without event\n" );
            }
            else
            {
                pol->count = pol->count + 1;    
            }       
            break;
        }
        poll_item++;
    }
    call_func_pol = NULL;
    return 0;
}
static int poll_check_event_timeout(lua_State *L)
{
    poll_t  *pol;
    pol = call_func_pol;
    if( pol->poll_result == 0 )     lua_pushboolean(L, 1);
    else                            lua_pushboolean(L, 0);
    call_func_pol = NULL;
    return 1;   
}
static int poll_check_event_read(lua_State *L)
{
    poll_t          *pol;
    int             check_fd;
    struct  pollfd  *poll_item;
    int             lp;
    pol = call_func_pol;
    check_fd        = lua_tointeger( L, 1 );
    poll_item = pol->ufds;  
    for( lp = 0; lp < pol->count; lp++ ) 
    {
        if( poll_item->fd == check_fd )
        {
            if( poll_item->revents & POLLIN )
            {   
                lua_pushboolean(L, 1);
                return 1;   
            }   
        }   
        poll_item++;    
    }
    lua_pushboolean(L, 0);
    return 1;   
}
static int poll_check_event_error(lua_State *L)
{
    poll_t          *pol;
    int             check_fd;
    struct  pollfd  *poll_item;
    int             lp;
    pol = call_func_pol;
    check_fd        = lua_tointeger( L, 1 );
    poll_item = pol->ufds;  
    for( lp = 0; lp < pol->count; lp++ ) 
    {
        if( poll_item->fd == check_fd )
        {
            if( poll_item->revents & POLLERR )
            {   
                lua_pushboolean(L, 1);
                return 1;   
            }   
        }   
        poll_item++;    
    }
    lua_pushboolean(L, 0);
    return 1;   
}
static int poll_check_event_write(lua_State *L)
{
    poll_t          *pol;
    int             check_fd;
    struct  pollfd  *poll_item;
    int             lp;
    pol = call_func_pol;
    check_fd        = lua_tointeger( L, 1 );
    poll_item = pol->ufds;  
    for( lp = 0; lp < pol->count; lp++ ) 
    {
        if( poll_item->fd == check_fd )
        {
            if( poll_item->revents & POLLOUT )
            {   
                lua_pushboolean(L, 1);
                return 1;   
            }   
        }   
        poll_item++;    
    }
    lua_pushboolean(L, 0);
    return 1;   
}
static const luaL_reg poll_meta[] = {
    {"__index"              , poll_index                },
    {"__tostring"           , poll_tostring             },
    {"__gc"                 , poll_gc                   },
    {"clear"                , poll_clear                },
    {"wait"                 , poll_wait                 },
    {"check_event_timeout"  , poll_check_event_timeout  },
    {"add_event"            , poll_add_event            },
    {"check_event_read"     , poll_check_event_read     },
    {"check_event_error"    , poll_check_event_error    },
    {"check_event_write"    , poll_check_event_write    },
    {0, 0}
};
static const struct luaL_Reg pm[] =
{
    { "create"      , poll_create   },
    { NULL, NULL }
};
int poll_new_metatable(lua_State *L )
{
    int top;
    top = lua_gettop(L);   
    luaL_newmetatable(L, POLL );          
    lua_pushvalue(L, -1);              
    lua_setfield(L, -2, "__index");
    luaL_register(L, NULL, poll_meta );
    lua_settop(L, top);
    return 1;
}
LUALIB_API int luaopen_poll( lua_State *L )
{
    luaL_register(L, POLL_MODULE, pm );
    poll_new_metatable(L);
    return 1;
}

5. Makefile

 

    모듈을 컴파일을 위한 메이크 파일이다.

CROSS_PREFIX = arm-linux
CROSS_PREFIX_CC = $(CROSS_PREFIX)-
LUA_MODULE = poll.so
TARGET     = $(LUA_MODULE)
TARGET_NFS = /nfs/lua
LIB_LUA_A  = liblua.a
C_SRCS = 
LUA_SRCS = 
LUA_LIBS_SRCS = 
C_SRCS      += poll.c
LUA_SRCS    += run.lua
# LUA Support  -- CORE 
LUA_LIBS_SRCS = 
LUA_LIBS_SRCS += ../../lua_lib/lapi.c
LUA_LIBS_SRCS += ../../lua_lib/lcode.c
LUA_LIBS_SRCS += ../../lua_lib/ldebug.c
LUA_LIBS_SRCS += ../../lua_lib/ldo.c
LUA_LIBS_SRCS += ../../lua_lib/ldump.c
LUA_LIBS_SRCS += ../../lua_lib/lfunc.c
LUA_LIBS_SRCS += ../../lua_lib/lgc.c
LUA_LIBS_SRCS += ../../lua_lib/llex.c
LUA_LIBS_SRCS += ../../lua_lib/lmem.c
LUA_LIBS_SRCS += ../../lua_lib/lobject.c
LUA_LIBS_SRCS += ../../lua_lib/lopcodes.c
LUA_LIBS_SRCS += ../../lua_lib/lparser.c
LUA_LIBS_SRCS += ../../lua_lib/lstate.c
LUA_LIBS_SRCS += ../../lua_lib/lstring.c
LUA_LIBS_SRCS += ../../lua_lib/ltable.c
LUA_LIBS_SRCS += ../../lua_lib/ltm.c
LUA_LIBS_SRCS += ../../lua_lib/lundump.c
LUA_LIBS_SRCS += ../../lua_lib/lvm.c
LUA_LIBS_SRCS += ../../lua_lib/lzio.c
# LUA Support  -- LIB
LUA_LIBS_SRCS += ../../lua_lib/lauxlib.c
LUA_LIBS_SRCS += ../../lua_lib/lbaselib.c
LUA_LIBS_SRCS += ../../lua_lib/ldblib.c
LUA_LIBS_SRCS += ../../lua_lib/liolib.c
LUA_LIBS_SRCS += ../../lua_lib/lmathlib.c
LUA_LIBS_SRCS += ../../lua_lib/loslib.c
LUA_LIBS_SRCS += ../../lua_lib/ltablib.c
LUA_LIBS_SRCS += ../../lua_lib/lstrlib.c
LUA_LIBS_SRCS += ../../lua_lib/loadlib.c
LUA_LIBS_SRCS += ../../lua_lib/linit.c
INCLUDES += -I. 
INCLUDES += -I/usr/local/include
INCLUDES += -I/usr/$(CROSS_PREFIX)/include
INCLUDES += -I../include
INCLUDES += -I../../lua_include
LDFLAGS += -L./
LDFLAGS += -L../../lua_lib/
LDFLAGS += -L/usr/$(CROSS_PREFIX)/lib
LIBS  = -lm -ldl -llua
CFLAGS   += $(INCLUDES)
CFLAGS   += -Wall -O2 -g -DLUA_USE_LINUX
CPPFLAGS += $(DEFINES)
ARFLAGS = rs
#---------------------------------------------------------------------
CC           = $(CROSS_PREFIX_CC)gcc
CXX          = $(CROSS_PREFIX_CC)g++
AR           = $(CROSS_PREFIX_CC)ar rcu
AR2          = $(CROSS_PREFIX_CC)ranlib
RANLIB       = $(CROSS_PREFIX_CC)ranlib
LD           = $(CROSS_PREFIX_CC)ld
NM           = $(CROSS_PREFIX_CC)nm
STRIP        = $(CROSS_PREFIX_CC)strip
OBJCOPY      = $(CROSS_PREFIX_CC)objcopy
CP = cp
MV = mv
#--------------------------------------------------------------------
C_OBJS          = $(C_SRCS:%.c=%.o)
LUA_LIBS_OBJS   = $(LUA_LIBS_SRCS:%.c=%.o)
#
# Compilation target for C files
#
%.o:%.c
    @echo "Compiling $< ..."
    $(CC) -c $(CFLAGS) -o $@ $<

%.o:%.cc
    @echo "Compiling $< ..."
    $(CXX) -c $(CPPFLAGS) $(CFLAGS) -o $@ $<
#
# Compilation target for C++ files
#
%.o:%.cc
    @echo "C++ compiling $< ..."
    $(CXX) -c $(CFLAGS) $(CXXFLAGS) -o $@ $<
all : $(LIB_LUA_A) $(LIB_GXLIB_A) $(TARGET)
    cp -a $(LUA_MODULE) $(TARGET_NFS)
    cp $(LUA_SRCS) $(TARGET_NFS)
$(LIB_LUA_A) : $(LUA_LIBS_OBJS)
    $(AR) $@ $?
    $(RANLIB) $@
$(TARGET) : $(C_OBJS)
    $(CC) $(LDFLAGS)  -shared -Wl,-soname,$(TARGET) $(C_OBJS) -o $@ $(LIBS)
dep :
    $(CC) -M $(INCLUDES) $(C_SRCS) > .depend
clean:
    rm -f *.bak
    rm -f *.map
    rm -f *.o
    rm -f *.so
    rm -f $(C_OBJS)
    rm -f $(LUA_LIBS_OBJS)
    rm -f $(LIB_LUA_A)
    rm -f $(TARGET) core
distclean: clean
    rm -rf .depend
ifeq (.depend,$(wildcard .depend))
include .depend
endif

6. 실행예


    다음은 EZ-S3C2440 에서 실행한 예 이다.

 
[root@falinux lua]$ ./run.lua
    >> poll test
    >> serial port list
       total = 3
       list  = /dev/ttyS0 /dev/ttyS1 /dev/ttyS2
    >> com create
        port   = /dev/ttyS1
        baud   = 115200
        data   = 8
        parity = none
    poll time out
    poll time out
    poll event
    serial read event
        read size = 1>> [d]
    poll event
    serial read event
        read size = 1>> [f]
    poll event
    serial read event
        read size = 1>> [s]
    poll time out
    poll event
    serial read event
        read size = 1>> [f]
    poll event
    serial read event
        read size = 1>> [d]
    poll time out

C000_lua.gif