摘要:本文介紹使用rpcgent實現64位程序調用32位庫函數的方法,並給出樣例代碼。服務器
個人程序運行在64位Linux系統上,須要使用一個從外部得到的共享庫中的函數,這個共享庫是32位的,沒法得到源代碼或64位共享庫。網絡
我對Linux系統和程序的瞭解是:tcp
解決這個問題有兩個方法:ide
我要實現兩個程序:函數
我使用的方法是RPC(Remote Procedure Call )。
在Linux中,rpcgen命令行工具使這個工做至關簡單,咱們只須要編寫少許調用庫函數所需的代碼,就能實現上述功能,rpcgent爲咱們自動生成程序間通訊所需的代碼。而且,服務端和客戶端能夠運行在一臺機器上,也能夠運行在網絡的不一樣機器上。工具
關於rpcgen的簡單例子和說明,下面兩篇資料是很好的參考:
http://blog.csdn.net/hj19870806/article/details/8185604
《rpcgen Programming Guide》ui
本文再也不重複參考中的簡單例子,而是給出一個接近實際的例子,其實也很簡單,供各位參考。.net
客戶端要調用下面兩個庫函數,這兩個函數有多個入參和返回值。命令行
// 頭文件 retrieve.h // 庫文件 libretrieve.so (32位) int RetrieveByContent( // 返回: 整數 char *model, // 輸入: 字符串 char *content, // 輸入: 字符串 int threshhold ); // 輸入: 整數 // 頭文件 classify.h // 庫文件 libclassify.so (32位) char* ClassifyByContent( // 返回: 字符串 char* model, // 輸入: 字符串 char* content ); // 輸入: 字符串
用RPC語言編寫test.x文件,內容以下:code
struct retrievePara { string model<>; // <>表示不限制長度的字符串 string content<>; int threshold; }; struct classifyPara { string model<>; string content<>; }; program TESTPROG { version TESTVERS { int RetrieveByContent( retrievePara ) = 1; string ClassifyByContent( classifyPara ) = 2; } = 1; } = 101;
執行下列命令:
| 命令 | 生成文件 | 功能 |
| ----- | ----- | ----- |
| rpcgen test.x | test.h test_xdr.c test_clnt.c test_svc.c | 生成RPC通訊所需源文件 |
| rpcgen -Sc -o test_clnt_func.c test.x | test_clnt_func.c | 生成客戶端樣例程序的源文件 |
| rpcgen -Ss -o test_svc_func.c test.x | test_svc_func.c | 生成服務端樣例程序的源文件 |
修改客戶端 樣例代碼:
#include "test.h" /************************************************************/ /* */ /************************************************************/ void testprog_1( char *host ) { CLIENT *clnt; int *result_1; retrievePara retrievebycontent_1_arg; char **result_2; classifyPara classifybycontent_1_arg; /************************************************************/ // /************************************************************/ #ifndef DEBUG // 若是參數數據不少(例如字符串很長),則使用 "tcp",由於 "udp" 可能產生錯誤。 clnt = clnt_create (host, TESTPROG, TESTVERS, "udp"); if (clnt == NULL) { clnt_pcreateerror (host); exit (1); } #endif /* DEBUG */ /************************************************************/ // call RetrieveByContent() /************************************************************/ retrievebycontent_1_arg.modelName = "RetrieveByContent Modele"; retrievebycontent_1_arg.content = "RetrieveByContent Content"; retrievebycontent_1_arg.threshhold = 55; result_1 = retrievebycontent_1( &retrievebycontent_1_arg, clnt ); if ( result_1 == (int *) NULL ) { clnt_perror( clnt, "call RetrieveByContent failed" ); } printf( "RetrieveByContent return : %d\n", *result_1 ); /************************************************************/ // call ClassifyByContent /************************************************************/ classifybycontent_1_arg.modelName = "classifybycontent Model"; classifybycontent_1_arg.content = "classifybycontent Content"; result_2 = classifybycontent_1( &classifybycontent_1_arg, clnt ); if ( result_2 == (char **) NULL ) { clnt_perror (clnt, "call ClassifyByContent failed"); } printf( "ClassifyByContent return : %s\n", *result_2 ); /************************************************************/ // /************************************************************/ #ifndef DEBUG clnt_destroy (clnt); #endif /* DEBUG */ } /************************************************************/ /* 主程序 */ /************************************************************/ int main( int argc, char *argv[] ) { char *host = "127.0.0.1"; // 服務端程序所在機器的IP地址 testprog_1( host ); exit (0); }
修改服務端樣例代碼:
#include "test.h" #include "retrieve.h" // 共享庫的頭文件 #include "classify.h" // 共享庫的頭文件 /*******************************************************************/ /* 調用: RetrieveByContent */ /*******************************************************************/ int * retrievebycontent_1_svc( retrievePara *argp, struct svc_req *rqstp ) { static int result; // insert server code here printf( "Call RetrieveByContent :\n" ); printf( "Model : %s\n", argp->modelName ); printf( "Content : %s\n", argp->content ); printf( "Threshhold : %d\n", argp->threshold ); int iRet = 0; iRet = RetrieveByContent( argp->modelName, argp->content, argp->threshold ); printf( "Return %d\n", iRet ); result = iRet; return &result; } /*******************************************************************/ /* 調用 ClassifyByContent */ /*******************************************************************/ char ** classifybycontent_1_svc(classifyPara *argp, struct svc_req *rqstp) { static char * result; // insert server code here printf( "Call ClassifyByContent :\n" ); printf( "Model : %s\n", argp->modelName ); printf( "content : %s\n", argp->content ); char *s = NULL; s = ClassifyByContent( argp->modelName, argp->content ); printf( "Return %s\n", s ); result = s; return &result; }
在64位系統上編譯客戶端和服務端程序:
gcc -Wall -o test_client test_clnt_func. test_clnt.c test.xdr.c
gcc -m32 -Wall -o test_server test_svc_func.c test_svc.c test_clnt.c test_xdr.c -L . -l retrieve -l classify
用file命令能夠看出:
test_client 是64位程序
test_server是32位程序
運行程序前,系統須要安裝portmap,不然程序不能執行成功。
Ubuntu上的安裝命令是:sudo apt-get install portmap
先運行服務端程序,再運行客戶端程序,根據輸出信息能夠判斷庫函數調用成功。
客戶端能夠調用自定義函數,服務器實現自定義函數,自定義函數實現中可能調用多個32位庫函數,從而爲客戶端提供更高級的功能。
對於上面生成的服務端程序,不能同時運行多個服務端程序,運行第二個會使第一個失效。若是但願同時運行多個服務端,那麼客戶端怎麼找到對應的服務端呢?
能夠利用TESTVERS,只要客戶端和服務端使用相同的值就能對應起來。
客戶端: clnt = clnt_create (host, TESTPROG, TESTVERS, "udp");
服務端:修改編譯生成的文件test_svc.c,使TESTVERS的值與客戶端的相同。
關於Linux上32/64位程序,參見個人另外一篇文章:
《Linux:32/64位程序(應用程序、共享庫、內核模塊)》