Linux:使用rpcgen实现64位程序调用32位库函数
2015-10-22 19:18
453 查看
摘要:本文介绍使用rpcgent实现64位程序调用32位库函数的方法,并给出样例代码。
我对Linux系统和程序的了解是:
1. 64位程序只能调用64位共享库,32位程序只能调用32位共享库。
2. 64位程序不能运行在32位系统上,32位程序可以运行在64位系统上。
解决这个问题有两个方法:
1. 把程序编译为32位,这样就可以使用32位共享库。但我的程序也使用了其它64位共享库,把它们都换成32位共享库不合适。
2. 实现一个32位的中间程序(它调用32位共享库),我的64位程序与32位中间程序进行通信,从而实现调用32位共享库。本文介绍这种方法的实现。
1. 32位的中间程序:它是服务端,接受客户端的请求,然后调用32位共享库,并将执行结果返回给客户端。
2. 64位的主程序:它是客户端,通过向服务端发起请求并获得回应,从而实现调用32位共享库功能的目的。
我使用的方法是RPC(Remote Procedure Call )。
在Linux中,rpcgen命令行工具使这个工作相当简单,我们只需要编写少量调用库函数所需的代码,就能实现上述功能,rpcgent为我们自动生成程序间通信所需的代码。并且,服务端和客户端可以运行在一台机器上,也可以运行在网络的不同机器上。
关于rpcgen的简单例子和说明,下面两篇资料是很好的参考:
http://blog.csdn.net/hj19870806/article/details/8185604
《rpcgen Programming Guide》
本文不再重复参考中的简单例子,而是给出一个接近实际的例子,其实也很简单,供各位参考。
用RPC语言编写test.x文件,内容如下:
执行下列命令:
修改客户端 样例代码:
修改服务端样例代码:
在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
先运行服务端程序,再运行客户端程序,根据输出信息可以判断库函数调用成功。
对于上面生成的服务端程序,不能同时运行多个服务端程序,运行第二个会使第一个失效。如果希望同时运行多个服务端,那么客户端怎么找到对应的服务端呢?
可以利用TESTVERS,只要客户端和服务端使用相同的值就能对应起来。
客户端: clnt = clnt_create (host, TESTPROG, TESTVERS, “udp”);
服务端:修改编译生成的文件test_svc.c,使TESTVERS的值与客户端的相同。
关于Linux上32/64位程序,参见我的另一篇文章:
《Linux:32/64位程序(应用程序、共享库、内核模块)》
我的问题
我的程序运行在64位Linux系统上,需要使用一个从外部获得的共享库中的函数,这个共享库是32位的,无法获得源代码或64位共享库。我对Linux系统和程序的了解是:
1. 64位程序只能调用64位共享库,32位程序只能调用32位共享库。
2. 64位程序不能运行在32位系统上,32位程序可以运行在64位系统上。
解决这个问题有两个方法:
1. 把程序编译为32位,这样就可以使用32位共享库。但我的程序也使用了其它64位共享库,把它们都换成32位共享库不合适。
2. 实现一个32位的中间程序(它调用32位共享库),我的64位程序与32位中间程序进行通信,从而实现调用32位共享库。本文介绍这种方法的实现。
实现方法
我要实现两个程序:1. 32位的中间程序:它是服务端,接受客户端的请求,然后调用32位共享库,并将执行结果返回给客户端。
2. 64位的主程序:它是客户端,通过向服务端发起请求并获得回应,从而实现调用32位共享库功能的目的。
我使用的方法是RPC(Remote Procedure Call )。
在Linux中,rpcgen命令行工具使这个工作相当简单,我们只需要编写少量调用库函数所需的代码,就能实现上述功能,rpcgent为我们自动生成程序间通信所需的代码。并且,服务端和客户端可以运行在一台机器上,也可以运行在网络的不同机器上。
关于rpcgen的简单例子和说明,下面两篇资料是很好的参考:
http://blog.csdn.net/hj19870806/article/details/8185604
《rpcgen Programming Guide》
本文不再重复参考中的简单例子,而是给出一个接近实际的例子,其实也很简单,供各位参考。
实现实例
客户端要调用下面两个库函数,这两个函数有多个入参和返回值。[code]// 头文件 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 | 生成服务端样例程序的源文件 |
[code]#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); }
修改服务端样例代码:
[code]#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位程序(应用程序、共享库、内核模块)》
相关文章推荐
- linux 以及android 系统下常用命令备忘
- linux-0.11完全剖析第二天: bochs+ubuntu 14.04 运行简单的多任务内核
- Linux file 命令 【转载】
- 最有用的Linux命令行使用技巧集锦
- 如何更新centos的系统时间
- Vim命令学习笔记
- 别出心裁的Linux命令学习法
- DTB Device Tree 视频教程 - 以瑞萨Cortex-A15为例
- ssh远程链接的原理及方法
- linux mount nobody nogroup
- linux下踢出已登录用户
- CentOS6.7 常用操作命令
- linux python升级过程
- 常用Linux操作命令总结
- Linux修改SSH连接数 重启SSH服务
- 十大好用的Linux实用工具推荐
- CentOS7下LVS+Keepalived实现高性能高可用负载均衡
- linux中sd卡驱动(2)
- linux中sd卡驱动(1)
- Linux进程间通信(五) - 信号灯(史上最全)及其经典应用案例