1. 개요

 

    이 문서는 시리얼 모듈을 리눅스 C 공유 라이브러리로 제작하기 위해서
    필요한 사용자 데이터와 메타 테이블을 구현하는 방법을
    기록한 문서이다.
   

2. 사용자 데이터

 

    C 에서 모듈을 구현할 때 내부적인 관리를 위해서 구조체를 사용하는 것이 편리하다.
    C 구조체는 루아의 기본적인 데이터 형은 아니기 때문에 이를 사용자 데이터라고 한다.
   
    사용자 데이터라고 해서 특별한 형태를 갇는 것은 아니다.
    단지 사용자 데이터를 할당할 때 루아 함수를 이용하는 것 뿐이다.
   
    우리가 앞으로 구현할 시리얼 모듈에서는 디바이스 파일 이름이 필요하다.
    이런 데이터를 관리할 구조체를 serial_t 라고 하고
    우선은  디바이스 파일 이름만 포함하는것을 다음과 같이 간단하게 만들어 보자
   
        typedef struct {
          char device_name[128];
        } serial_t ;

 

    보는 것과 같이 C 에서 다루는 구조체와 하나도 다를 것이 없다.
   
    하지만 이 구조체를 생성할 경우에는 lua_newuserdata() 함수를 반드시 사용해야 한다.
    사용예는 다음과 같다.
   
        serial_t *com = (serial_t *)lua_newuserdata(L, sizeof(serial_t));
   
    인자로는 루아 스테이터스 구조체와 생성할 사용자 데이터 구조체의 크기이다.
   
    사용자 데이터 역시 루아에서 관리하는 메모리 이기 때문에
    생성 후 해제는 신경 쓸 필요가 없다.
   
3. 사용자 데이터와 메타 테이블

 

    메타 테이블이라는 것은 루아 연산자를 재정의 하기 위해서 사용되는 것이다.
    자세한 것은 루아 관련 서적을 살펴 보기를 바란다.
   
    여기서는 사용자 데이터를 사용하고 앞으로 시리얼 모듈을 관리 할 때 필요한
    메타 테이블 함수들에 대한 것을 중심으로 살표 보겠다.
   
    우선 메타 테이블과 루아 스크립트와의 관계를 살펴 보자
   
        m = require "serial";
        com =  m.create();
        print( com );
        m = nil;
       
    이 루아 스크립트는
   
    1) 시리얼 모듈을 m 변수에 할당 한다.
    2) 그 다음에 모듈 함수인 m.create() 함수를 이용하여 사용자 데이터를 생성하고
       이 사용자 데이터를 com 변수에 할당한다.
    3) print 에 의해서 com 사용자 데이터를 출력한다.
    4) m 모듈 변수에 nil 을 대입함으로써 모듈을 해제 한다.
   
    이중 메타 스크립트가 관여하는 부분은 총 세부분이다.
   
    2), 3), 4) 번이 각각 메타 테이블이 적용된다.
   
    2) 는 m.create 의  '.' 이 메타 테이블에
       __index 키를 갖는 함수를 호출하게 된다.\
      
    3) 은 문자열 변환 처리가 필요하므로  메타 테이블에
        __tostring 키를 갖는 함수를 호출하게 된다.
       
    4) 는 모듈을 해제 함으로써 메모리에 대한 가비지 콜렉션을
       발생 시키는데 메타 테이블에
       __gc  키를 갖는 함수를 호출하게 된다.
      
    이런 메타 테이블은 luaL_reg 구조체를 사용해서 정의하는데
   
    다음과 같은 형태로 사용한다.
   
        static const luaL_reg serial_meta[] = {
            
          {"__tostring" , serial_tostring   },
          {"__gc"       , serial_gc         },
          {0, 0}
        };

    이것을 다음과 같은 절차를 통해서 등록한다.
   
        luaL_newmetatable(L, "serial" );           
        lua_pushvalue(L, -1);              
        lua_setfield(L, -2, "__index");
        luaL_register(L, NULL, serial_meta );
   
    luaL_newmetatable 의 두번째 인자는 메타 테이블 이름이 된다.

       
   
4. 사용자 데이터 생성

 

    위 사용자 데이터를 관리하기 위해서는 다음과 같이 사용자 데이터는 생성하는
    루틴을 별도로 만들어 관리하는 것이 편한다.
   
        static serial_t *serial_new_userdata(lua_State *L)
        {
          serial_t *com = (serial_t *)lua_newuserdata(L, sizeof(serial_t));
          luaL_getmetatable(L, SERIAL);
          lua_setmetatable(L, -2);
          return com;
        }
   
    사용자 데이터를 만들고 메타 테이블을 연결하는 과정이 하나의 함수로 만들어 진다.
   
5. 사용자 데이터 획득

 

    이렇게 만들어진 사용자 데이터는 필요한 경우 루아 스택에서 얻어 와야 한다.
   
    이 역시 별도의 함수를 만들어서 사용하는 것이 편리하다.
   
        static serial_t * serial_get_data(lua_State *L, int index)
        {
            serial_t *com = (serial_t *)lua_touserdata(L, index);
            if (com == NULL) luaL_typerror(L, index, SERIAL);
            return com;
        }
   
    스택에서 사용자 데이터를 얻어오고 얻기에 실패하면 타입에러로 처리하도록 하고 있다.
   
6. 시험용 루아 소스

 

    #!/mnt/nfs/lua/lua
    print "START TEST LUA"
   
    m = require "serial";
    print( m );
    m.port_list();
    com =  m.create();
    print( com );
    m = nil;

 

7. 사용자 데이터 시험용 C 모듈 소스

 

    #include <stdarg.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
   
    #define LUA_LIB
   
    #include <lua.h>
    #include <lauxlib.h>
   
    #define SERIAL      "SERIAL"
   
    typedef struct {
      char device_name[128];
    } serial_t ;
   
    static int port_list( lua_State *L )
    {
        return 0;
    }
   
    static serial_t *serial_new_userdata(lua_State *L)
    {
      serial_t *com = (serial_t *)lua_newuserdata(L, sizeof(serial_t));
      luaL_getmetatable(L, SERIAL);
      lua_setmetatable(L, -2);
      return com;
    }
   
    static serial_t * serial_get_data(lua_State *L, int index)
    {
        serial_t *com = (serial_t *)lua_touserdata(L, index);
        if (com == NULL) luaL_typerror(L, index, SERIAL);
        return com;
    }
   
    static int serial_create( lua_State *L )
    {
        serial_t *com;
   
        printf( "OK CALL MODULE create()\n" );
       
        com = serial_new_userdata(L);
        sprintf( com->device_name , "DEVICE NAME" );
       
        return 1;
    }
   
    static int serial_tostring (lua_State *L)
    {
        serial_t *data;
       
        printf( "CALL TO STRING " );
     
        data = serial_get_data( L, 1 );
        lua_pushfstring(L, "[%s]", data->device_name);
        return 1;
    }
   
    static int serial_gc (lua_State *L)
    {
        serial_t *data;
        data = serial_get_data( L, 1 );
   
        printf( "CALL GC [%s]\n", data->device_name );
        return 0;
    }
    
  
    static const struct luaL_Reg sm[] =
    {
        { "port_list"   , port_list     },
        { "create"      , serial_create },
       
        { NULL, NULL }
    };
   
    static const luaL_reg serial_meta[] = {
        
        {"__tostring"   , serial_tostring   },
        {"__gc"     , serial_gc         },
        {0, 0}
    };
   
    LUALIB_API int luaopen_serial( lua_State *L )
    {
        int top;
       
        luaL_register(L, "serial", sm );
       
        top = lua_gettop(L);   
   
        luaL_newmetatable(L, SERIAL);          
   
        lua_pushvalue(L, -1);              
        lua_setfield(L, -2, "__index");
   
        luaL_register(L, NULL, serial_meta );
   
        lua_settop(L, top);
       
        return 1;
    }

 

8. Makefile

 

    CROSS_PREFIX = arm-linux
   
    LUA_MODULE = serial.so
   
    TARGET     = $(LUA_MODULE)
    TARGET_NFS = /nfs/lua
   
    LIB_LUA_A  = liblua.a
   
    C_SRCS =
    LUA_SRCS =
    LUA_LIBS_SRCS =
   
    C_SRCS      += serial.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)-gcc
    CXX          = $(CROSS_PREFIX)-g++
    AR           = $(CROSS_PREFIX)-ar rcu
    AR2          = $(CROSS_PREFIX)-ranlib
    RANLIB       = $(CROSS_PREFIX)-ranlib
    LD           = $(CROSS_PREFIX)-ld
    NM           = $(CROSS_PREFIX)-nm
    STRIP        = $(CROSS_PREFIX)-strip
    OBJCOPY      = $(CROSS_PREFIX)-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

 

9. 수행

 

    수행은 nfs 를 마운트 해서 보드에서 하는데 다음과 같은 파일이 준비도 있어야 한다.
   
    [root@falinux lua]$ ls        
    lua        run.lua    serial.so
    [root@falinux lua]$           
   
    수행은 다음과 같이 하면 된다.

    [root@falinux lua]$ ./run.lua
    START TEST LUA
    table: 0x338e8
    OK CALL MODULE create()
    CALL TO STRING [DEVICE NAME]
    CALL GC [DEVICE NAME]
   
    [root@falinux lua]$ ./run.lua
    START TEST LUA
    OK CALL MODULE port_list()
    OK CALL MODULE create()
    [root@falinux lua]$

    사용자 테이블에 메타 테이블 함수들이 호출되는 것을 확인 할 수 있다.
    C000_lua.gif