Chinaunix首页 | 论坛 | 博客
  • 博客访问: 550594
  • 博文数量: 83
  • 博客积分: 6010
  • 博客等级: 准将
  • 技术积分: 1169
  • 用 户 组: 普通用户
  • 注册时间: 2007-04-29 22:34
文章分类

全部博文(83)

文章存档

2011年(3)

2010年(29)

2009年(30)

2008年(21)

我的朋友

分类: LINUX

2008-06-21 17:00:06

rpc函数参数及返回值的传递跟普通本地环境下的函数调用还是有很大区别的。本文着重讨论多线程(rpcgen -M)环境下缓冲区(字符串)是怎么传递的。

下面的例子test中client传“hello“给server,server统计接受的client请求数,并返回“hello“+请求数给client。显然,client与server之间传递的消息都是缓冲区(字符串),而不是基本的定长的数据类型。


这是test.x

/* rpcgen -M test.x  */

const MAXLEN = 255;
typedef string filename<MAXLEN>;
program test{
    version test_ver{
        filename func(filename) = 1;
    } = 1;
} = 177;


这是client.c

#include "test.h"
#define host "localhost"
/* gcc -o client test_clnt.c client.c test_xdr.c * /

int
main()
{
    CLIENT *cl;
    enum clnt_stat res;
    filename in = "hello";
    filename out;


    out = (filename) malloc(sizeof(MAXLEN));
    printf("out = [%p]\n", out);
    cl = clnt_create (host, test, test_ver, "tcp");
    if (cl == NULL) {
        clnt_pcreateerror (host);
        return 1;
    }
    res = func_1(&in, &out, cl);
    if (res != RPC_SUCCESS){
        clnt_perror(cl, host);
        return 1;
    }
    printf("receive : [%p]%s\n", out, out);
    clnt_destroy(cl);
    return 0;
}


这是server.c

#include "test.h"
/* gcc -o server test_svc.c server.c test_xdr.c */
bool_t func_1_svc(filename *in, filename *out, struct svc_req *s)
{
    printf("receive : %s\n", *in);
    filename out1;
    static int i;
 
    out1 = (filename) malloc (sizeof(MAXLEN));
    printf("out1 = %p\n", out1);
    sprintf(out1, "%s%d\n", *in, i++);
    *out = out1;
    printf("return out : %s\n", *out);
    return TRUE;
}


int test_1_freeresult (SVCXPRT *transp, xdrproc_t xdr_result, caddr_t result)
{
    xdr_free(xdr_result, result);
}


client端运行结果:

$ ./client
out = [0x602010]
receive : [0x602010]hello0

$ ./client
out = [0x602010]
receive : [0x602010]hello1

$ ./client
out = [0x602010]
receive : [0x602010]hello2


server端运行结果:

$ ./server
receive : hello
out1 = 0x609b00
return out : hello0

receive : hello
out1 = 0x609b00
return out : hello1

receive : hello
out1 = 0x609b00
return out : hello2



从以上结果看出,client端和server端都接收到正确的数据了。

1, client端:从out输出的地址可以看出,out(其实是char*类型)指向的地址并没有被改变,也就是说调用func_1并没有为out重新分配空间。因此可以推断,当“ func_1(&in, &out, cl);”返回时,rpc已经把返回的字符串拷贝到out指向的地址空间了,而不是重新分配空间,再把out指向该空间。因此这里就有一个问题要注意:

client端指向期待server返回的地址空间要在调用前分配好(out指向的空间要事先被分配好),不然就会出现“Segmentation fault”错误(读者不妨自己验证,只需把out = (filename) malloc(sizeof(MAXLEN));这句去掉就行了。

2,   server端:out的空间是不是也要事先分配好。也就是说,是不是一定要像上面server.c中先通过out1分配空间,再把out指向out1呢?试试再说。把server.c改成如下,client.c不变:

bool_t func_1_svc(filename *in, filename *out, struct svc_req *s)
{
     printf("receive : %s\n", *in);
    static int i;
    sprintf(*out, "%s%d\n", *in, i++);
    printf("return out : [%p]%s\n", *out, *out);
    return TRUE;
}


int test_1_freeresult (SVCXPRT *transp, xdrproc_t xdr_result, caddr_t result)
{
    xdr_free(xdr_result, result);
}


结果却在server端显示:

*receive : hello
return out : [0x607930]hello0

*** glibc detected *** ./server: double free or corruption (out): 0x0000000000607930 ***
======= Backtrace: =========
/lib/libc.so.6[0x2ad05656faad]
/lib/libc.so.6(cfree+0x76)[0x2ad056571796]
/lib/libc.so.6(xdr_string+0xce)[0x2ad0565e864e]
./server[0x400e22]
/lib/libc.so.6(xdr_free+0x15)[0x2ad0565e7e45]
./server[0x400dfb]
./server[0x400bd1]
/lib/libc.so.6(svc_getreq_common+0x1de)[0x2ad0565e5e5e]
/lib/libc.so.6(svc_getreq_poll+0xaa)[0x2ad0565e620a]
/lib/libc.so.6(svc_run+0xa9)[0x2ad0565e68c9]
./server[0x400d21]
/lib/libc.so.6(__libc_start_main+0xf4)[0x2ad056520b74]
./server[0x400a09]
======= Memory map: ========

[...]

上面输出显示,在func_1_svc返回之后出现了“double free or corruption” out的错误,也就是说out指向的空间被double free了,在Backtrace里面还看到了熟悉的xdr_free(这是需要自己写的,在test_1_freeresult中,负责释放server端程序内存的函数),于是不难想到这可能是xdr_free试图free掉out的空间,但是out的空间根本就没有被分配过(或者说已经被释放过了),所以出现了double free的错误。于是便不难想到,把xdr_free去掉是否就可以了?再试试,把server.c中的xdr_free(xdr_result, result);这句去掉,结果:

client端输出:

$ ./client
out = [0x602010]
receive : [0x602010]
$ ./client
out = [0x602010]
receive : [0x602010]
$ ./client
out = [0x602010]
receive : [0x602010]

可以看到client根本没有收到server端传来的数据

server端的输出:

$ ./server
receive : hello
return out : [0x607930]hello0

receive : hello
return out : [0x607930]hello1

receive : hello
return out : [0x607930]hello2

server端是正确的。

这说明,server端在执行func_1_svc之后并没有把out指向的内存传输给client端——这应该是svc_sendreply函数做的事情,在rpcgen生成的test_svc.c中可以找到。

所以结论是:server端对要返回给client的内存空间也要自己事先分配好(rpcgen没有帮你做),之后该区间要通过
test_1_freeresult来释放。

下面的图反映了rpc数据的传输流程:


另外,上图还反映了2点:
1, in 在server是“只读”的,因为最后server并不把in返回给client,所以写也是白写
2, out在server中是“只写”的,因为client并没有把out的内容传给server。

上面是自己摸索的,如果有错还请不吝赐教
阅读(8654) | 评论(2) | 转发(0) |
给主人留下些什么吧!~~

chinaunix网友2011-06-18 07:07:55

发表于: 2008-06-21,修改于: 2008-11-11 15:57,已浏览1149次 有点惊讶,难道这么长时间,没有人对你的观点表示疑问? client不需要申请out空间,out空间已经在stack上有了。 server当然要申请空间放filename,这是常识问题。 可是你申请的也不对啊malloc(sizeof(MAXLEN)), 这会申请4byte的空间。 当然malloc为了效率期间,会开一块最小单元给你,大概是16字节。 你刚好copy了6个字节进去,所以没有问题,你放个大点123456789|123456789 看看,free的时候就出错了。

chinaunix网友2011-06-18 07:07:12

发表于: 2008-06-21,修改于: 2008-11-11 15:57,已浏览1149次 有点惊讶,难道这么长时间,没有人对你的观点表示疑问? client不需要申请out空间,out空间已经在stack上有了。 server当然要申请空间放filename,这是常识问题。 可是你申请的也不对啊malloc(sizeof(MAXLEN)), 这会申请4byte的空间。 当然malloc为了效率期间,会开一块最小单元给你,大概是16字节。 你刚好copy了6个字节进去,所以没有问题,你放个大点123456789|123456789 看看,free的时候就出错了。