Linux:使用rpcgen实现64位程序调用32位库函数
摘要:本文介绍使用rpcgent实现64位程序调用32位库函数的方法,并给出样例代码。
我的问题
我的程序运行在64位Linux系统上,需要使用一个从外部获得的共享库中的函数,这个共享库是32位的,无法获得源代码或64位共享库。
我对Linux系统和程序的了解是:
- 64位程序只能调用64位共享库,32位程序只能调用32位共享库。
- 64位程序不能运行在32位系统上,32位程序可以运行在64位系统上。
解决这个问题有两个方法:
- 把程序编译为32位,这样就可以使用32位共享库。但我的程序也使用了其它64位共享库,把它们都换成32位共享库不合适。
- 实现一个32位的中间程序(它调用32位共享库),我的64位程序与32位中间程序进行通信,从而实现调用32位共享库。本文介绍这种方法的实现。
实现方法
我要实现两个程序:
- 32位的中间程序:它是服务端,接受客户端的请求,然后调用32位共享库,并将执行结果返回给客户端。
- 64位的主程序:它是客户端,通过向服务端发起请求并获得回应,从而实现调用32位共享库功能的目的。
我使用的方法是RPC(Remote Procedure Call )。
在Linux中,rpcgen命令行工具使这个工作相当简单,我们只需要编写少量调用库函数所需的代码,就能实现上述功能,rpcgent为我们自动生成程序间通信所需的代码。并且,服务端和客户端可以运行在一台机器上,也可以运行在网络的不同机器上。
关于rpcgen的简单例子和说明,下面两篇资料是很好的参考:
http://blog.csdn.net/hj19870806/article/details/8185604
《rpcgen Programming Guide》
本文不再重复参考中的简单例子,而是给出一个接近实际的例子,其实也很简单,供各位参考。
实现实例
客户端要调用下面两个库函数,这两个函数有多个入参和返回值。
// 头文件 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文件,内容如下:
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位程序(应用程序、共享库、内核模块)》
Linux:使用rpcgen实现64位程序调用32位库函数的更多相关文章
- 64位进程调用32位dll的解决方法 / 程序64位化带来的问题和思考
最近做在Windows XP X64,VS2005环境下做32位程序编译为64位程序的工作,遇到了一些64位编程中可能遇到的问题:如内联汇编(解决方法改为C/C++代码),long类型的变化,最关键的 ...
- 64位进程调用32位dll的解决方法
64位进程调用32位dll的解决方法 最近做在Windows XP X64,VS2005环境下做32位程序编译为64位程序的工作,遇到了一些64位编程中可能遇到的问题:如内联汇编(解决方法改为C/ ...
- 64位.net调用32位com服务(c++)
说明: 因64位.net无法调用32位dll,才采用调用进程外com形式. 该项目必须为release时编译才不会报错. 项目调用时,添加引用->com中找到该com服务,添加即可. 部署: 启 ...
- ubuntu16 64位 编译64位程序和32位程序
安装了ubuntu16 64位的系统,想在该环境下用gcc编译64位和32位的程序 默认已经安装了64位环境的gcc 1. 首先确认安装的环境是不是64位的 cocoa@cocoaUKlyn:~/De ...
- 64位程序调用32DLL解决方案
最近做一个.NETCore项目,需要调用以前用VB6写的老程序,原本想重写,但由于其调用了大量32DLL,重写后还需要编译为32位才能运行,于是干脆把老代码整个封装为32DLL,然后准备在64位程序中 ...
- Linux 64位编译\链接32位程序
测试机器:Ubuntu14.04 64位 gcc编译32位程序,添加参数-m32: $ gcc -c -fno-builtin -m32 TinyHelloWorld.c ld链接32位代码,添加参数 ...
- 32位程序调用Oracle11gR2数据库libclntsh.so失败
[问题描述]32位程序调用Oracle11gR2数据库的libclntsh.so库时会返回失败. [问题原因]32位程序只能调用32位的Oracle客户端实例包,而R2数据库默认安装完毕后是没有lib ...
- 64位系统VBS调用32位COM组件
64位系统VBS调用32位COM组件 标签: 32位, 64位, COM, COM组件, VB, VBS, VBScript 标题: 64位系统VBS调用32位COM组件作者: Demon链接: ht ...
- 64位主机64位oracle下装32位客户端ODAC(NFPACS版)
64位主机64位oracle下装32位客户端ODAC(NFPACS版) by dd 1.下载Oracle Data Access Components(ODAC) Xcopy的两个版本: x86:(我 ...
随机推荐
- centos7.0安装cuda驱动
00.CUDA简介 CUDA和GPU的并行处理能力来加速深度学习和其他计算密集型应用程序 01.CPU+GPU协同架构 02.部署环境 [docker@lab-250 ~]$ cat /etc/*re ...
- Java基础(四)线程快速了解
开始整理线程之前,之前有个命令忘记整理了,先整理一下jar命令的使用 Jar包 其实可以理解是java的压缩包方便使用,只要在classpath设置jar路径即可数据库驱动,ssh框架等都是以jar包 ...
- 查看哪个用户登录过服务器 记录 时间 和 ip
who /var/log/wtmp 1>wtmp 一个用户每次登录进入和退出时间的永久纪录
- vue的js文件中获取vue实例
1.main.js导出vue实例: var vue = new Vue({ el: '#app', router, components: { App }, template: '<App/&g ...
- RSA 分段加解密【解决“不正确的长度”的异常】
RSA 是常用的非对称加密算法.最近使用时却出现了“不正确的长度”的异常,研究发现是由于待加密的数据超长所致. .NET Framework 中提供的 RSA 算法规定: 待加密的字节数不能超过密钥的 ...
- input 呼起数字键盘
1. Android,定义 type="number" 2. iOS,定义 style="ime-mode: disabled;" 注,呼起数字键盘后,用户输入 ...
- [微信小程序] 通过快速启动demo分析小程序入门关键点
(1)小程序基础结构 下图是在开发者工具通过快速启动模式创建的小程序的目录结构 可以看到,小程序中主要包含有4中类型不同的文件 .json 后缀的 JSON 配置文件 .wxml 后缀的 WXML 模 ...
- ROS常用命令汇总
ROS系统也是通过命令行操作,总结了下平时工作中使用的命令工具,以后使用多会陆续添加. 查看ROS_PACKAGE_PATH环境变量 $ echo $ROS_PACKAGE_PATH /home/sa ...
- R包的小技巧
通常我们都是直接使用library(pkg_name) 的形式加载R包,在同一台机器上面,对于我们而言,这个包所在的路径一定是在.libPaths() 路面的,但是对于其他用户而言,这个路径可能不存 ...
- c# httpclient
using Newtonsoft.Json; using System; using System.Collections.Generic; using System.IO; using System ...