在伺服器上配置Lua時,需要考慮到該機子是32位的還是64位機子,而且還要根據選擇不同的安裝Lua方法採取不同的處理方式,以解決有關-fPIC問題(32bitOS與64bitOS上都存在),和使用源碼安裝中make出現的warning:mplicit declaration of function ?readline?錯誤(在64bitOS上安裝時會出現此錯誤)首先問題一:因為這裡的配置是專門針對Erlang+C+lua的,在編譯.so檔案時,需要使用到-fPIC選項,會出現relocation R_X86_64_32 against `luaO_nilobject_' can not be used when making a shared object; recompile with -fPIC /usr/local/lib/liblua.a: could not read symbols: Bad value collect2: ld returned 1 exit status 錯誤,有關-fPIC問題在其他情況下調用是否出問題,與所調用的連結庫或程式安裝設定有關;而這裡的錯誤,是因為預設情況下,Lua的MakeFile檔案中CFLAGS選項沒有啟動-fPIC設定。解決方案:進入Lua的源碼目錄(也就是解壓Lua之後的目標目錄),使用編輯器開啟Makefile(命令:Vim src/Makefile),注意其第11行,把CFLAGS= -O2 -Wall $(MYCFLAGS) 修改為 CFLAGS= -O2 -Wall -fPIC $(MYCFLAGS),儲存..問題二:通常我們會首先下載Lua的源碼安裝包,然後解壓,進入目標目錄,使用 #make linux 命令編譯,但在這裡就會出現錯誤,系統提示Error;安裝步驟中斷....解決方案:yum install libtermcap-devel [1/4]yum install ncurses-devel [2/4]yum install libevent-devel [3/4]yum install readline-devel [4/4]安裝完以上庫,再重新執行make linux,系統不再提示問題,然後執行#make install,安裝。安裝以上方法設定好,重新編譯安裝lua,能夠解決將在下一步編譯產生.so動態連結程式庫是產生的system warning和error;接下來是編譯、運行代碼由於這裡解決方案是Erlang 使用 driver方法 與C互動,而C直接通過其已有的API與Lua互動,這裡會涉及到三種程式碼的編寫,因此需要在C代碼中,根據與C互動的目標代碼的功能而分開獨立的代碼模組,如port_dirver.c是專門寫與Erlang互動的C代碼,c_lua_XX.c是專門儲存port_dirver_XX.c通過C調用Lua的函數(即Erlang調用lua,同理,C調用lua),lua_c_XX.c則是由C程式處理的專門給Lua調用的方法。Erlang + C:Erlang driver方法中,C代碼中通常需要至少有3個方法,其中為:ErlDrvData (*start)(ErlDrvPort port, char *command, [SysDriverOpts* opts]);void (*stop)(ErlDrvData drv_data); void (*output)(ErlDrvData drv_data, char *buf, int len);而最多與Erlang直接相關的方法也只能是10個函數簽名定義通常如下:ErlDrvEntry example_driver_entry = { NULL, /* F_PTR init, N/A */ example_drv_start, /* L_PTR start, called when port is opened */ example_drv_stop, /* F_PTR stop, called when port is closed */ example_drv_output, /* F_PTR output, called when erlang has sent */ NULL, /* F_PTR ready_input, called when input descriptor ready */ NULL, /* F_PTR ready_output, called when output descriptor ready */ "example_drv", /* char *driver_name, the argument to open_port */→→→→→→↓ NULL, /* F_PTR finish, called when unloaded */ ↓ NULL, /* F_PTR control, port_command callback */ ↓ NULL, /* F_PTR timeout, reserved */ ↓ NULL /* F_PTR outputv, reserved */ ↓ }; ↓←←←←←←←←←←←←←←←←←←←←←兩個參數值必須一樣←←←←←←←←←←←←←←←←←←←←←↓ DRIVER_INIT(example_drv) /* must match name in driver_entry */{ return &example_driver_entry;}對於以上函數中的方法簽名,詳細可見Erlang document :http://www.erlang.org/doc/man/driver_entry.htmlC + Lualua調用C函數時,首先是需要註冊函數的,也就是說我們必須把C函數的地址以一個適當的方式傳遞給lua解析器。Lua調用C函數,與C調用Lua一樣,都是需要使用相同類型的棧來互動,而且這互動的棧並不是全域變數,每個函數都有自己的私人棧。當Lua調用C函數的時候,第一個參數總是在這個私人棧的index=1的位置。甚至當一個C函數調用Lua代碼(Lua代碼調用同一個C函數或者其他的C函數),每一個C函數都有自己的獨立的私人棧,並且第一個參數在index=1的位置。C與lua的互動:首先需要在C中引入Lua的庫,一般程式碼如下:static void upload_luatest(){ int error; L = lua_open(); /*建立一個指向Lua解譯器的指標。*/ luaL_openlibs(L); /*函數載入所有Lua庫*/ lua_pushcfunction(L, average); /*此處註冊將來給Lua調*/ lua_setglobal(L, "avge"); /*用的函數名avge,綁定到C函數average;*/ error = luaL_dofile(L,"divlua.lua"); /*載入.lua指令檔*/}C調用lua時,首先要擷取全域的變數(函數簽名,如lua_getglobal( L, "add")),然後使用lua_pushnumber()的方法連續往棧中壓進參數數,再使用lua_call(lua_State *L, int nargs, int nresults)調用該Lua函數。lua調用C時,首先需要先在.c的檔案中註冊(在調用lua_open()後面適當的加上以下函數:lua_pushcfunction(lua_State* L, FunName);lua_setglobal(lua_State* L,YouFunName)第一個函數將類型為function的值入棧,第二個函數將function賦值給全域變數YouFunName),這樣修改之後,重新編譯Lua,你就可以在你的Lua程式中使用新的YouFunName函數了。也就是說,一旦一個C函數被註冊之後並儲存到Lua中,在Lua程式中就可以直接引用他的地址(當我們註冊這個函數的時候傳遞給Lua的地址)來訪問這個函數了。換句話說,一旦C函數被註冊之後,Lua調用這個函數並不依賴於函數名,包的位置,或者調用函數的可見的規則。編譯:進入代碼目錄,使用gcc編譯器編譯,命令如下:gcc -o example_drv.so -I /usr/local/lib/erlang/lib/erl_interface-3.6.2/include/ -I /usr/local/lib/erlang/erts-5.7.2/include/ -I /usr/local/include/ -L /usr/local/lib -fpic -shared -L /usr/local/lib/erlang/lib/erl_interface-3.6.2/lib/ -L ./lua_attr.h complex.c luafun.c port_driver.c -lei -lerl_interface -llua -ldl -lm -Wall解析: -I 指示在此編譯中需要連結的庫類檔案目錄, -L 指明需要連結的庫檔案或標頭檔 -fPIC -shared-lei -lerl_interface 需要使用到Erlang中ei module 和erl_interface module 的BIF,在這裡指示標明-llua -ldl 指示在編譯中使用Lua的庫,和Lua編譯命令-lm 這命令指明需要連結數學庫,因為Lua中需要調用C方法,而這裡的C是標準C, 這就相當於Lua直接調用了標準庫的C API,因此需要使用-lm連結其數學庫。以上編譯成功之後,產生一個.so 的檔案把該檔案copy到需要載入這.so的erl源碼目錄(假設為目錄A)中,同時需要在相同目錄下添加相關的類庫,這裡可以通過 ldd 命令來獲得運行時載入的動態庫,如:#ldd example_drv.so 顯示結果如下: libdl.so.2 => /lib64/libdl.so.2 (0x00002aafb4b94000) libm.so.6 => /lib64/libm.so.6 (0x00002aafb4d98000) libc.so.6 => /lib64/libc.so.6 (0x00002aafb501b000) /lib64/ld-linux-x86-64.so.2 (0x0000003ca6a00000)由以上可以,需要把libdl.so.2,libm.so.6,libc.so.6,d-linux-x86-64.so.2copy到源碼目錄(目錄A)中。到此,Erlang可以載入.so,執行 Erlang+C+lua 的互動。 另:(1)what is PICPIC stands for Position-independent code and is machine instruction code that executes properly regardless of where in memory it resides. PIC is commonly used for shared libraries, so that the same library code can be loaded in a location in each program address space whereit won't overlap any other uses of memory (for example, other shared libraries).(2)有關在編譯.c代碼檔案時出現的型如:Warning: no newline at end of file的錯誤解決方案:英文的意思就是說文末沒有分行符號。Unix文檔的斷行符號分行符號是一個字元\n,Windows的是分別的兩個\n\r,所以在Windows下編輯的最後一個字元是\r不是\n,而我們很多時候都是在windows環境下編寫代碼,然後在上傳到linux系統上,因此編譯器以為有錯誤。只要在檔案最後補一個新行即可。
在伺服器上配置Lua時,需要考慮到該機子是32位的還是64位機子,而且還要根據選擇不同的安裝Lua方法採取不同的處理方式,
以解決有關-fPIC問題(32bitOS與64bitOS上都存在),和使用源碼安裝中make出現的warning:mplicit declaration of function ?readline?錯
誤(在64bitOS上安裝時會出現此錯誤)
首先問題一:
因為這裡的配置是專門針對Erlang+C+lua的,在編譯.so檔案時,需要使用到-fPIC選項,會出現relocation R_X86_64_32 against `luaO_nilobject_'
can not be used when making a shared object;
recompile with -fPIC /usr/local/lib/liblua.a: could not read symbols: Bad value
collect2: ld returned 1 exit status 錯誤,有關-fPIC問題在其他情況下調用是否出問題,與所調用的連結庫或程式安裝設定有關;
而這裡的錯誤,是因為預設情況下,Lua的MakeFile檔案中CFLAGS選項沒有啟動-fPIC設定。
解決方案:
進入Lua的源碼目錄(也就是解壓Lua之後的目標目錄),使用編輯器開啟Makefile(命令:Vim src/Makefile),
注意其第11行,把CFLAGS= -O2 -Wall $(MYCFLAGS) 修改為 CFLAGS= -O2 -Wall -fPIC $(MYCFLAGS),儲存..
問題二:
通常我們會首先下載Lua的源碼安裝包,然後解壓,進入目標目錄,使用 #make linux 命令編譯,但在這裡就會出現錯誤,
系統提示Error;安裝步驟中斷....
解決方案:
yum install libtermcap-devel [1/4]
yum install ncurses-devel [2/4]
yum install libevent-devel [3/4]
yum install readline-devel [4/4]
安裝完以上庫,再重新執行make linux,系統不再提示問題,然後執行#make install,安裝。
安裝以上方法設定好,重新編譯安裝lua,
能夠解決將在下一步編譯產生.so動態連結程式庫是產生的system warning和error;
接下來是編譯、運行代碼
由於這裡解決方案是Erlang 使用 driver方法 與C互動,
而C直接通過其已有的API與Lua互動,這裡會涉及到三種程式碼的編寫,因此需要在C代碼中,
根據與C互動的目標代碼的功能而分開獨立的代碼模組,如port_dirver.c是專門寫與Erlang互動的C代碼,
c_lua_XX.c是專門儲存port_dirver_XX.c通過C調用Lua的函數(即Erlang調用lua,同理,C調用lua),
lua_c_XX.c則是由C程式處理的專門給Lua調用的方法。
Erlang + C:
Erlang driver方法中,C代碼中通常需要至少有3個方法,
其中為:ErlDrvData (*start)(ErlDrvPort port, char *command, [SysDriverOpts* opts]);void (*stop)(ErlDrvData drv_data);
void (*output)(ErlDrvData drv_data, char *buf, int len);
而最多與Erlang直接相關的方法也只能是10個函數簽名定義通常如下:
ErlDrvEntry example_driver_entry = {
NULL, /* F_PTR init, N/A */
example_drv_start, /* L_PTR start, called when port is opened */
example_drv_stop, /* F_PTR stop, called when port is closed */
example_drv_output, /* F_PTR output, called when erlang has sent */
NULL, /* F_PTR ready_input, called when input descriptor ready */
NULL, /* F_PTR ready_output, called when output descriptor ready */
"example_drv", /* char *driver_name, the argument to open_port */
NULL, /* F_PTR finish, called when unloaded */
NULL, /* F_PTR control, port_command callback */
NULL, /* F_PTR timeout, reserved */
NULL /* F_PTR outputv, reserved */
};
DRIVER_INIT(example_drv) /* must match name in driver_entry */
{
return &example_driver_entry;
}
對於以上函數中的方法簽名,詳細可見Erlang document :http://www.erlang.org/doc/man/driver_entry.html
C + Lua
lua調用C函數時,
首先是需要註冊函數的,也就是說我們必須把C函數的地址以一個適當的方式傳遞給lua解析器。
Lua調用C函數,與C調用Lua一樣,都是需要使用相同類型的棧來互動,而且這互動的棧並不是全域變數,每個函數都有自己的私人棧。
當Lua調用C函數的時候,第一個參數總是在這個私人棧的index=1的位置。
甚至當一個C函數調用Lua代碼(Lua代碼調用同一個C函數或者其他的C函數),
每一個C函數都有自己的獨立的私人棧,並且第一個參數在index=1的位置。
C與lua的互動:
首先需要在C中引入Lua的庫,一般程式碼如下:
static void upload_luatest(){
int error; L = lua_open(); /*建立一個指向Lua解譯器的指標。*/
luaL_openlibs(L); /*函數載入所有Lua庫*/
lua_pushcfunction(L, average); /*此處註冊將來給Lua調*/
lua_setglobal(L, "avge"); /*用的函數名avge,綁定到C函數average;*/
error = luaL_dofile(L,"divlua.lua"); /*載入.lua指令檔*/
}
C調用lua時,
首先要擷取全域的變數(函數簽名,如lua_getglobal( L, "add")),然後使用lua_pushnumber()的方法連續往棧中壓進參數數,
再使用lua_call(lua_State *L, int nargs, int nresults)調用該Lua函數。lua調用C時,首先需要先在.c的檔案中註冊(在調用
lua_open()後面適當的加上以下函數:lua_pushcfunction(lua_State* L, FunName);lua_setglobal(lua_State* L,YouFunName)
第一個函數將類型為function的值入棧,第二個函數將function賦值給全域變數YouFunName),這樣修改之後,重新編譯Lua,
你就可以在你的Lua程式中使用新的YouFunName函數了。也就是說,一旦一個C函數被註冊之後並儲存到Lua中,
在Lua程式中就可以直接引用他的地址(當我們註冊這個函數的時候傳遞給Lua的地址)來訪問這個函數了。
換句話說,一旦C函數被註冊之後,Lua調用這個函數並不依賴於函數名,包的位置,或者調用函數的可見的規則。
編譯:
進入代碼目錄,使用gcc編譯器編譯,命令如下:
gcc -o example_drv.so -I /usr/local/lib/erlang/lib/erl_interface-3.6.2/include/ -I /usr/local/lib/erlang/erts-5.7.2/include/
-I /usr/local/include/ -L /usr/local/lib -fpic -shared -L /usr/local/lib/erlang/lib/erl_interface-3.6.2/lib/
-L ./lua_attr.h complex.c luafun.c port_driver.c -lei -lerl_interface -llua -ldl -lm -Wall
解析:
-I 指示在此編譯中需要連結的庫類檔案目錄,
-L 指明需要連結的庫檔案或標頭檔
-fPIC -shared
-lei -lerl_interface 需要使用到Erlang中ei module 和erl_interface module 的BIF,在這裡指示標明
-llua -ldl 指示在編譯中使用Lua的庫,和Lua編譯命令
-lm 這命令指明需要連結數學庫,因為Lua中需要調用C方法,而這裡的C是標準C, 這就相當於Lua直接調用了標準庫的C API,
因此需要使用-lm連結其數學庫。以上編譯成功之後,產生一個.so 的檔案;
把該檔案copy到需要載入這.so的erl源碼目錄(假設為目錄A)中,同時需要在相同目錄下添加相關的類庫,
這裡可以通過 ldd 命令來獲得運行時載入的動態庫,
如:#ldd example_drv.so
顯示結果如下:
libdl.so.2 => /lib64/libdl.so.2 (0x00002aafb4b94000)
libm.so.6 => /lib64/libm.so.6 (0x00002aafb4d98000)
libc.so.6 => /lib64/libc.so.6 (0x00002aafb501b000)
/lib64/ld-linux-x86-64.so.2 (0x0000003ca6a00000)
由以上可以,需要把libdl.so.2,libm.so.6,libc.so.6,d-linux-x86-64.so.2copy到源碼目錄(目錄A)中。
到此,Erlang可以載入.so,執行 Erlang+C+lua 的互動。
另:
(1)
what is PIC?
PIC stands for Position-independent code and is machine instruction code that executes properly regardless of where in memory it resides. PIC is commonly used for shared libraries, so that the same library code can be loaded in a location in each program address space whereit won't overlap any other uses of memory (for example, other shared libraries).
(2)
有關在編譯.c代碼檔案時出現的型如:Warning: no newline at end of file的錯誤解決方案:英文的意思就是說文末沒有分行符號。
Unix文檔的斷行符號分行符號是一個字元\n,Windows的是分別的兩個\n\r,所以在Windows下編輯的最後一個字元是\r不是\n,
而我們很多時候都是在windows環境下編寫代碼,然後在上傳到linux系統上,因此編譯器以為有錯誤。
只要在檔案最後補一個新行即可。