摘要:本文介绍使用rpcgent实现64位程序调用32位库函数的方法,并给出样例代码。

我的问题

我的程序运行在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》

本文不再重复参考中的简单例子,而是给出一个接近实际的例子,其实也很简单,供各位参考。

实现实例

客户端要调用下面两个库函数,这两个函数有多个入参和返回值。

  1. // 头文件 retrieve.h
  2. // 库文件 libretrieve.so (32位)
  3. int RetrieveByContent( // 返回: 整数
  4. char *model, // 输入: 字符串
  5. char *content, // 输入: 字符串
  6. int threshhold ); // 输入: 整数
  7. // 头文件 classify.h
  8. // 库文件 libclassify.so (32位)
  9. char* ClassifyByContent( // 返回: 字符串
  10. char* model, // 输入: 字符串
  11. char* content ); // 输入: 字符串

用RPC语言编写test.x文件,内容如下:

  1. struct retrievePara {
  2. string model<>; // <>表示不限制长度的字符串
  3. string content<>;
  4. int threshold;
  5. };
  6. struct classifyPara {
  7. string model<>;
  8. string content<>;
  9. };
  10. program TESTPROG {
  11. version TESTVERS {
  12. int RetrieveByContent( retrievePara ) = 1;
  13. string ClassifyByContent( classifyPara ) = 2;
  14. } = 1;
  15. } = 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 生成服务端样例程序的源文件

修改客户端 样例代码:

  1. #include "test.h"
  2. /************************************************************/
  3. /* */
  4. /************************************************************/
  5. void testprog_1( char *host )
  6. {
  7. CLIENT *clnt;
  8. int *result_1;
  9. retrievePara retrievebycontent_1_arg;
  10. char **result_2;
  11. classifyPara classifybycontent_1_arg;
  12. /************************************************************/
  13. //
  14. /************************************************************/
  15. #ifndef DEBUG
  16. // 如果参数数据很多(例如字符串很长),则使用 "tcp",因为 "udp" 可能产生错误。
  17. clnt = clnt_create (host, TESTPROG, TESTVERS, "udp");
  18. if (clnt == NULL) {
  19. clnt_pcreateerror (host);
  20. exit (1);
  21. }
  22. #endif /* DEBUG */
  23. /************************************************************/
  24. // call RetrieveByContent()
  25. /************************************************************/
  26. retrievebycontent_1_arg.modelName = "RetrieveByContent Modele";
  27. retrievebycontent_1_arg.content = "RetrieveByContent Content";
  28. retrievebycontent_1_arg.threshhold = 55;
  29. result_1 = retrievebycontent_1( &retrievebycontent_1_arg, clnt );
  30. if ( result_1 == (int *) NULL )
  31. {
  32. clnt_perror( clnt, "call RetrieveByContent failed" );
  33. }
  34. printf( "RetrieveByContent return : %d\n", *result_1 );
  35. /************************************************************/
  36. // call ClassifyByContent
  37. /************************************************************/
  38. classifybycontent_1_arg.modelName = "classifybycontent Model";
  39. classifybycontent_1_arg.content = "classifybycontent Content";
  40. result_2 = classifybycontent_1( &classifybycontent_1_arg, clnt );
  41. if ( result_2 == (char **) NULL )
  42. {
  43. clnt_perror (clnt, "call ClassifyByContent failed");
  44. }
  45. printf( "ClassifyByContent return : %s\n", *result_2 );
  46. /************************************************************/
  47. //
  48. /************************************************************/
  49. #ifndef DEBUG
  50. clnt_destroy (clnt);
  51. #endif /* DEBUG */
  52. }
  53. /************************************************************/
  54. /* 主程序 */
  55. /************************************************************/
  56. int main( int argc, char *argv[] )
  57. {
  58. char *host = "127.0.0.1"; // 服务端程序所在机器的IP地址
  59. testprog_1( host );
  60. exit (0);
  61. }

修改服务端样例代码:

  1. #include "test.h"
  2. #include "retrieve.h" // 共享库的头文件
  3. #include "classify.h" // 共享库的头文件
  4. /*******************************************************************/
  5. /* 调用: RetrieveByContent */
  6. /*******************************************************************/
  7. int * retrievebycontent_1_svc( retrievePara *argp, struct svc_req *rqstp )
  8. {
  9. static int result;
  10. // insert server code here
  11. printf( "Call RetrieveByContent :\n" );
  12. printf( "Model : %s\n", argp->modelName );
  13. printf( "Content : %s\n", argp->content );
  14. printf( "Threshhold : %d\n", argp->threshold );
  15. int iRet = 0;
  16. iRet = RetrieveByContent( argp->modelName,
  17. argp->content,
  18. argp->threshold );
  19. printf( "Return %d\n", iRet );
  20. result = iRet;
  21. return &result;
  22. }
  23. /*******************************************************************/
  24. /* 调用 ClassifyByContent */
  25. /*******************************************************************/
  26. char ** classifybycontent_1_svc(classifyPara *argp, struct svc_req *rqstp)
  27. {
  28. static char * result;
  29. // insert server code here
  30. printf( "Call ClassifyByContent :\n" );
  31. printf( "Model : %s\n", argp->modelName );
  32. printf( "content : %s\n", argp->content );
  33. char *s = NULL;
  34. s = ClassifyByContent( argp->modelName, argp->content );
  35. printf( "Return %s\n", s );
  36. result = s;
  37. return &result;
  38. }

在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:使用rpcgen实现64位程序调用32位库函数的更多相关文章

  1. 64位进程调用32位dll的解决方法 / 程序64位化带来的问题和思考

    最近做在Windows XP X64,VS2005环境下做32位程序编译为64位程序的工作,遇到了一些64位编程中可能遇到的问题:如内联汇编(解决方法改为C/C++代码),long类型的变化,最关键的 ...

  2. 64位进程调用32位dll的解决方法

    64位进程调用32位dll的解决方法   最近做在Windows XP X64,VS2005环境下做32位程序编译为64位程序的工作,遇到了一些64位编程中可能遇到的问题:如内联汇编(解决方法改为C/ ...

  3. 64位.net调用32位com服务(c++)

    说明: 因64位.net无法调用32位dll,才采用调用进程外com形式. 该项目必须为release时编译才不会报错. 项目调用时,添加引用->com中找到该com服务,添加即可. 部署: 启 ...

  4. ubuntu16 64位 编译64位程序和32位程序

    安装了ubuntu16 64位的系统,想在该环境下用gcc编译64位和32位的程序 默认已经安装了64位环境的gcc 1. 首先确认安装的环境是不是64位的 cocoa@cocoaUKlyn:~/De ...

  5. 64位程序调用32DLL解决方案

    最近做一个.NETCore项目,需要调用以前用VB6写的老程序,原本想重写,但由于其调用了大量32DLL,重写后还需要编译为32位才能运行,于是干脆把老代码整个封装为32DLL,然后准备在64位程序中 ...

  6. Linux 64位编译\链接32位程序

    测试机器:Ubuntu14.04 64位 gcc编译32位程序,添加参数-m32: $ gcc -c -fno-builtin -m32 TinyHelloWorld.c ld链接32位代码,添加参数 ...

  7. 32位程序调用Oracle11gR2数据库libclntsh.so失败

    [问题描述]32位程序调用Oracle11gR2数据库的libclntsh.so库时会返回失败. [问题原因]32位程序只能调用32位的Oracle客户端实例包,而R2数据库默认安装完毕后是没有lib ...

  8. 64位系统VBS调用32位COM组件

    64位系统VBS调用32位COM组件 标签: 32位, 64位, COM, COM组件, VB, VBS, VBScript 标题: 64位系统VBS调用32位COM组件作者: Demon链接: ht ...

  9. 64位主机64位oracle下装32位客户端ODAC(NFPACS版)

    64位主机64位oracle下装32位客户端ODAC(NFPACS版) by dd 1.下载Oracle Data Access Components(ODAC) Xcopy的两个版本: x86:(我 ...

随机推荐

  1. 同步IO、异步IO、阻塞IO、非阻塞IO之间的联系与区别

    POSIX 同步IO.异步IO.阻塞IO.非阻塞IO,这几个词常见于各种各样的与网络相关的文章之中,往往不同上下文中它们的意思是不一样的,以致于我在很长一段时间对此感到困惑,所以想写一篇文章整理一下. ...

  2. 自建k8s集群日志采集到阿里云日志服务

    自建k8s集群 的master 节点安装 logtail 采集工具 wget http://logtail-release-cn-hangzhou.oss-cn-hangzhou.aliyuncs.c ...

  3. Android开发导出apk报错:Unable to build: the file dx.jar was not loaded from the SDK folder

    问题背景 此问题一般出现在,同时使用了Eclipse和Android Studio,eclipse是不会去下载最新的Android的相关tools,但是studio有时候会自动更新最新的build-t ...

  4. UE4 Notes

    Unreal Engine 4 减少编辑器的帧率C:\Program Files\Epic Games\UE_4.19\Engine\Config\BaseEngine.ini[/Script/Unr ...

  5. RDLC 根据条件改变背景颜色-多个IIF

    =IIf(Fields!DATE_DIFF.Value < 5 ,"White",IIf(Fields!DATE_DIFF.Value >=5 AND Fields!D ...

  6. Unity输出PC端(Windows) 拖拽文件到app中

    需求:给策划们写一个PC端(Window)的Excel导表工具.本来用OpenFile打开FileExplorerDialog后让他们自己选择想要添加的Excel文件就行了,结果有个需求是希望能拖拽E ...

  7. GRE tunnel 2

    1.GRE简介 通用路由封装协议GRE(Generic Routing Encapsulation)可以对某些网络层协议(如IPX.ATM.IPv6.AppleTalk等)的数据报文进行封装,使这些被 ...

  8. SQL Server 中,如何獲得上個月的第一天和最後一天( 帶時間戳)

    select DATEADD(MONTH, DATEDIFF(MONTH, 0, GETDATE())-1, 0) --First day of previous month select DATEA ...

  9. Centos VMware 克隆后 网络配置

    第一步:生产新的网卡地址,启动系统. 第二步:修改主机名(注:此处根据个人需要,不修改也行,此处我是用于搭建集群,修改主机名做区分) 执行命令:vi /etc/sysconfig/network 修改 ...

  10. python列表的切片操作允许索引超出范围

    其余的不说,列表切片操作允许索引超出范围: