• Linux:使用rpcgen实现64位程序调用32位库函数


    摘要:本文介绍使用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》

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

    实现实例

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

    // 头文件 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
    ", *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
    ", *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 :
    " );
        printf( "Model      : %s
    ", argp->modelName );
        printf( "Content    : %s
    ", argp->content   );
        printf( "Threshhold : %d
    ", argp->threshold );
    
    	int iRet = 0;
    	iRet = RetrieveByContent( argp->modelName,  
    	                          argp->content, 
    	                          argp->threshold );
    	printf( "Return %d
    ", 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 :
    " );
        printf( "Model    : %s
    ", argp->modelName );
        printf( "content  : %s
    ", argp->content   );
    
    	char *s = NULL;
    	s = ClassifyByContent( argp->modelName, argp->content );
    	printf( "Return %s
    ", 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下文件搜索命令学习笔记
    【转】C++格式化输出
    UNIX中的文件类型
    Unix内核中打开文件的表示
    网络编程学习笔记:linux下的socket编程
    TCP协议学习笔记(一)首部以及TCP的三次握手连接四次挥手断开
    C/C++源代码从写完到运行发生了什么
    C++ 函数形参发生动态绑定时指针增长步长与静态类型一致
    C++中为什么要将析构函数定义成虚函数
    C++求一个十进制的二进制中1的个数
  • 原文地址:https://www.cnblogs.com/ddk3000/p/5051108.html
Copyright © 2020-2023  润新知