
1. 개요
이 문서는 루아 인터프린터에서 사용할 수 있는 모듈을
리눅스 C 공유 라이브러로 제작하여 사용하는 방법을 기록한 문서이다.
2. 시리얼 모듈을 만들기 위한 기술 검토
이전 설계 방침에 따르면 다음과 같은 형식으로 시리얼 모듈의 함수들이 실행될 수 있어야 한다.
m = require "serial";
m.port_list();
m.create();
m = nil;
이 루아 스크립트는 serial.so 공유 라이브러리를 읽어 들이고
m 을 모듈변수로 할당한다.
이 m 모듈 변수를 이용하여 port_list() 함수와 create() 함수가 포함되어 있다.
이 모듈에 포함된 함수를 사용하려면
m.port_list();
와 같은 형태로 호출 하면 된다.
모듈 사용이 모두 끝나 더 이상의 사용을 하지 않는다면
m 모듈 변수에 nil 을 대입하면 된다.
3. 루아 스크립트를 자동으로 수행하기 위한 방법
위 스크립트 이름이 run.lua 라고 한다면
명령 라인에 다음과 같이 입력해야 한다.
./lua run.lua
매번 이렇게 입력하는 것은 매우 불편하다.
그래서 루아 스크립트의 첫줄을 다음과 같이 고치면
편하다.
#!/mnt/nfs/lua/lua
여기서 /mnt/nfs/lua/lua 은 실제로 lua 인터프린터가
있는 위치를 나타낸다.
자신의 환겨에 맞게 적절히 수정하면 된다.
또 한가지 주의 할점은 윈도우에서 루아 스크립트를 만들면
마지막에 ^M 문자가 추가되어 리눅스에서는 실행되지 않는다.
리눅스에서 초기 파일을 만들던가
또는 윈도우에서 만든후 ^M 문자를 제거해 주어야 한다.
3.1 테스트용 루아 스크립트
이 강좌를 진행하기 위해서 시험해야 하는 루아 스크립트는 다음과 같다.
#!/mnt/nfs/lua/lua
print "START TEST LUA"
m = require "serial";
m.port_list();
m.create();
m = nil;
4. C 모듈을 만들기 위한 Makefile
C 루아 모듈은 실제로 공유 라이브러리를 만드는 과정이다.
serial.so 라는 공유 라이브러리는 루아에서 C 모듈이 된다.
원 소스가 serial.c 라면 다음과 같은 컴파일 과정을 거치게 된다.
arm-linux-gcc -c \
-I. \
-I/usr/local/include \
-I/usr/arm-linux/include \
-I../include \
-I../../lua_include \
-Wall -O2 -g \
-DLUA_USE_LINUX \
-o serial.o serial.c
이 과정은 serial.c 를 컴파일 하여 오브젝트를 생성하는 과정인데
리눅스에서 수행되기 위해서
-DLUA_USE_LINUX
를 포함해야 하는 점과 컴파일 하기 위해서 필요한 헤더파일 디렉토리를
지정하는 부분만 주의 한다면 특별한 것이 없다.
arm-linux-gcc \
-L./ \
-L../../lua_lib/ \
-L/usr/arm-linux/lib \
-shared -Wl,-soname,serial.so \
serial.o -o serial.so \
-lm -ldl -llua
실제로 공유 라이브러리인 srial.o 를 만드는 과정인데
이 과정에서 다른 라이브러리들이 이미 컴파일 된
디렉토리를 지정하는 것과
공유 라이브러리 생성을 위한 옵션
-shared -Wl,-soname,serial.so
을 지정해야 한다.
또한 생성될 공유 라이브러리가 참조할 다른 공유 라이브러리를
지정하는 옵션인
-lm -ldl -llua
를 포함하여야 한다.
5. C 모듈의 구성
C 모듈이 루아 스크립트에서 사용되려면 몇 가지 규칙을
가지고 작성되어야 한다.
우선 "serial" 이라는 시리얼을 다루는 모듈이 있다고
가정하자.
이 루틴은 루아 스크립트에서 다음과 같은 형식으로
모듈로써 등록된다.
m = require "serial";
이 require() 함수는 결론적으로 serial.so 를 찾게 된다.
즉 C 모듈 함수이름은 serial.so 라는 이름을 가져야 한다는
것이다.
이 공유 라이브러리는 LUA 인터프린터에 의해서 읽혀진다.
LUA 인터프린터는 serial 라이브러리에서 초기 호출 함수를
찾는다. 이때 호출되는 함수 이름은 다음과 같은 형식이어야 한다.
LUALIB_API int luaopen_xxx( lua_State *L );
여기서 xxx 는 모듈 이름인 "serial " 대치되어
luaopen_serial()
라는 함수를 호출한다.
즉 serial.c 에는 다음과 같은 형태의
함수가 반드시 존재 해야 한다.
LUALIB_API int luaopen_serial( lua_State *L )
{
return 1;
}
이 함수에서 해야 하는 일은
모듈에 포함되는 함수를 모듈 구조체를 통하여 등록해야 한다.
모듈에 포함되는 함수는
struct luaL_Reg
구조체 변수를 통하여 정의하고
luaL_register()
함수를 통하여 등록된다.
5.1 테스트용 C 모듈
이 강좌를 진행하기 위해서 작성되는 C 모듈 소스는 다음과 같다.
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define LUA_LIB
#include <lua.h>
#include <lauxlib.h>
static int port_list( lua_State *L )
{
printf( "OK CALL MODULE port_list()\n" );
return 0;
}
static int create( lua_State *L )
{
printf( "OK CALL MODULE create()\n" );
return 0;
}
static const struct luaL_Reg sm[] =
{
{ "port_list" , port_list },
{ "create" , create },
{ NULL, NULL }
};
LUALIB_API int luaopen_serial( lua_State *L )
{
luaL_register(L, "serial", sm );
return 1;
}
5.2 Makefile
위 소스를 모듈로 만들기 위한 Makefile 은 다음과 같다.
각 모듈에 맞게 수정하는 부분은
LUA_MODULE : 루아 모듈명
C_SRCS : 모듈을 구성하는 소스 목록
-----------------------------------------------------------------------
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
6. 수행
수행은 nfs 를 마운트 해서 보드에서 하는데 다음과 같은 파일이 준비도 있어야 한다.
[root@falinux lua]$ ls
lua run.lua serial.so
[root@falinux lua]$
수행은 다음과 같이 하면 된다.
[root@falinux lua]$ ./run.lua
START TEST LUA
OK CALL MODULE port_list()
OK CALL MODULE create()
[root@falinux lua]$
모듈이 호출되는 것을 출력문으로 확인 할 수 있다.