zoukankan      html  css  js  c++  java
  • 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位程序(应用程序、共享库、内核模块)》

  • 相关阅读:
    hdu acm 2844 Coins 解题报告
    hdu 1963 Investment 解题报告
    codeforces 454B. Little Pony and Sort by Shift 解题报告
    广大暑假训练1 E题 Paid Roads(poj 3411) 解题报告
    hdu acm 2191 悼念512汶川大地震遇难同胞——珍惜现在,感恩生活
    hdu acm 1114 Piggy-Bank 解题报告
    poj 2531 Network Saboteur 解题报告
    数据库范式
    ngnix 配置CI框架 与 CI的简单使用
    Vundle的安装
  • 原文地址:https://www.cnblogs.com/ddk3000/p/5051108.html
Copyright © 2011-2022 走看看