2015-10-18 42 views
3

我根据从该页面的例子工作一个多线程的RPC服务器上的解码参数一段时间的错误,我发现服务器无法解码参数(基于squareproc_2的返回码)。在函数serv_request中调用squareproc_2_svc后,服务器端的执行似乎停止。请参阅下面的代码case: SQUAREPROC从square_svc.cRPC不能用于TCP传输

void *serv_request(void *data) 
{ 
    struct thr_data *ptr_data = (struct thr_data *)data; 
    { 
     square_in argument; 
     square_out result; 
     bool_t retval; 
     xdrproc_t _xdr_argument, _xdr_result; 
     bool_t (*local)(char *, void *, struct svc_req *); 
     struct svc_req *rqstp = ptr_data->rqstp; 
     register SVCXPRT *transp = ptr_data->transp; 
     switch (rqstp->rq_proc) { 
      case NULLPROC: 
       printf("NULLPROC called\n"); 
       (void) svc_sendreply (transp, (xdrproc_t) xdr_void, (char *)NULL); 
       return; 
      case SQUAREPROC: 
       _xdr_argument = (xdrproc_t) xdr_square_in; 
       _xdr_result = (xdrproc_t) xdr_square_out; 
       printf("_xdr_result = %ld\n",_xdr_result); 
       local = (bool_t (*) (char *, void *, struct svc_req *))squareproc_2_svc; 
       break; 
      default: 
       printf("default case executed"); 
       svcerr_noproc (transp); 
       return; 
     } 
     memset ((void *)&argument, 0, sizeof (argument)); 
     if (!svc_getargs (transp, (xdrproc_t) _xdr_argument, (caddr_t) &argument)) { 
      printf("svc_getargs failed"); 
      svcerr_decode (transp); 
      return; 
     } 
     retval = (bool_t) (*local)((char *)&argument, (void *)&result, rqstp); 
     printf("serv_request result: %d\n",retval); 
     if (retval > 0 && !svc_sendreply(transp, (xdrproc_t) _xdr_result, (char *)&result)) 
     { 
      printf("something happened...\n"); 
      svcerr_systemerr (transp); 
     } 
     if (!svc_freeargs (transp, (xdrproc_t) _xdr_argument, (caddr_t) &argument)) { 
      fprintf (stderr, "%s", "unable to free arguments"); 
      exit (1); 
     } 
     if (!square_prog_2_freeresult (transp, _xdr_result, (caddr_t) &result)) 
      fprintf (stderr, "%s", "unable to free results"); 
     return; 
    } 
} 

这里是squareproc_2_svc从文件square_server.c实现:

bool_t squareproc_2_svc(square_in *inp,square_out *outp,struct svc_req *rqstp) 
{ 
    printf("Thread id = '%ld' started, arg = %ld\n",pthread_self(),inp->arg1); 
    sleep(5); 
    outp->res1=inp->arg1*inp->arg1; 
    printf("Thread id = '%ld' is done %ld \n",pthread_self(),outp->res1); 
    return(TRUE); 
} 

客户端输出:

[email protected]:~/RPC/multithread_example$ ./ClientSQUARE localhost 2 
squareproc_2 called 
xdr_square_in result: 1 
function call failed; code: 11 

服务器端输出:

[email protected]:~/RPC/multithread_example$ sudo ./ServerSQUARE 
creating threads 
SQUAREPROC called 
xdr_square_in result: 0 

如您所见,xdr_square_in在服务器端返回FALSE结果。 这里是square.x

struct square_in { 
    long arg1; 
}; 

struct square_out { 
    long res1; 
}; 

program SQUARE_PROG { 
    version SQUARE_VERS { 
     square_out SQUAREPROC(square_in) = 1; 
    } = 2 ; 
} = 0x31230000; 

和square_xdr.c

/* 
* Please do not edit this file. 
* It was generated using rpcgen. 
*/ 

#include "square.h" 

bool_t 
xdr_square_in (XDR *xdrs, square_in *objp) 
{ 
    register int32_t *buf; 
    int retval; 
    if (!xdr_long (xdrs, &objp->arg1)) retval = FALSE; 
    else retval = TRUE; 
    printf("xdr_square_in result: %d\n",retval); 
    return retval; 
} 

bool_t 
xdr_square_out (XDR *xdrs, square_out *objp) 
{ 
    register int32_t *buf; 
    int retval; 
    if (!xdr_long (xdrs, &objp->res1)) retval = FALSE; 
    else retval = TRUE; 
    printf("xdr_square_out result: %d\n",retval); 
    return retval; 
} 

我在Ubuntu 14.04 LTS工作,生成stub和XDR代码rpcgen -a -M,并与gcc编译。

该错误似乎只在使用TCP作为传输方法时发生。我可以使用UDP作为传输器获得结果,但是当多个客户端的请求同时到达时,某些调用会失败。我希望能够支持多达15个客户。当我尝试使用UDP和10个客户端时,10个调用中的2个调用失败,返回码为squareproc_2

+0

为什么当他们只包含一个实体时创建两个联合'result'和'argument' – user3629249

+0

我同意这看起来很奇怪,但我仍然在学习RPC,并且认为有一些原因必须这样。也就是说,我相信写这个例子的人知道他们在做什么,虽然让我觉得很奇怪,但我无法找到或反对的理由,除了看起来很乱。 –

+0

这是memset()的原型void * memset(void * s,int c,size_t n);'为什么代码将第一个参数强制转换为'(char *)' – user3629249

回答

3

你有几个问题。

从Xen的页面,当它在square_prog_2的在pthread_create,它首先调用pthread_attr_setdetachstate,但它需要之前做pthread_attr_init 。此外,attr似乎是静态的/全局的 - 将其放入函数的堆栈帧中。

square_prog_2获取两个参数:rqstp和transp。这些被保存到一个malloc'ed结构data_str [因此每个线程都有自己的副本]。但是,我想知道rqstp和transp值是什么(例如printf(“%p”))。它们需要不同或者每个线程在尝试使用它们时会相互碰撞[因此需要pthread_mutex_lock]。 malloc不会克隆rqstp/transp,因此如果它们是相同的,那就是问题所在,因为您可能有两个线程试图同时在相同的缓冲区中执行riff操作。

有一个返回码11.禁止一些特殊的代码,看起来像SIGSEGV在线程上的可疑。这将完全由rqstp/transp重叠计算。

您可能需要对此进行重新设计,因为我怀疑XDR是而不是线程安全 - 也不应该这样做。另外,我不认为svc_ *是线程安全的/意识到的。

开始单线程。作为一项测试,请直接调用serv__request_square_prog_2(例如,执行而不是做pthread_ *)。我敢打赌,所有模式都适用。

如果是这样,抓住你的帽子 - 使用线程的示例代码被打破 - 充满了竞争条件,并会segfault,等等。如果你没有挂上使用线程(不需要这样的轻量级任务为x * x),您可以按原样享用。

否则,解决方案有点复杂。主线程必须做全部对套接字的访问和所有的XDR解析/编码。它不能使用svc_run - 你必须推出自己的。孩子可以做实际工作(如X * X),并可能触摸插座/ REQ /运输等

主线程:

while (1) { 
    if (svc_getreq_poll()) { 
     // parse XDR 
     // create data/return struct for child thread 
     // create thread 
     // add struct to list of "in-flight" requests 
    } 

    forall struct in inflight { 
     if (reqdone) { 
      // take result from struct 
      // encode into XDR 
      // do send_reply 
      // remove struct from list 
     } 
    } 
} 

对于孩子是结构看起来像:

struct child_struct { 
    int num; 
    int num_squared; 
}; 

而且孩子的线程函数变成了一个班轮:ptr->num_squared = ptr->num * ptr->num

UPDATE:多线程RPC服务器出现 Linux或FreeBSD下支持

这里有一个文件:https://www.redhat.com/archives/redhat-list/2004-June/msg00439.html这有一个更清洁的例子,从启动。

从此:记住-linux不支持rpcgen的一个选项。库调用 通过在SunOS RPC提供构建多线程RPC服务器不可用Linux下以及

这里是在Linux的rpcgen手册页:http://linux.die.net/man/1/rpcgen -M的没有提及。国际海事组织,这意味着rpcgen计划有选项,并确实生成存根,但底层的支持不存在,所以他们离开了文档。

这里是FreeBSD的手册页[为什么没有支持的理由]:http://www.freebsd.org/cgi/man.cgi?query=rpcgen&sektion=1&manpath=FreeBSD+5.0-RELEASE见在此为-M商务部:

米 - 生成传递参数多线程安全存根和的rpcgen之间产生 生成的代码和用户编写的代码。此选项 对于想要在其代码中使用线程的用户非常有用。但是,rpc_svc_calls(3)函数还不是MT安全的,其中 意味着rpcgen生成的服务器端代码不会是MT安全的。

的另一种方法:

为什么与RPC/XDR麻烦呢?对于您打算使用的大型阵列而言,开销很大。大多数标准用途都适用于黄页等数据不多的情况。

现在大多数系统都是小尾数。只需将本机缓冲区爆炸到您直接打开的套接字即可。在服务器上,有一个守护进程做一个监听,然后分叉一个孩子,并让孩子做接受,读入数据,做计算,并发送回复。在最坏的情况下,孩子需要做endian swap,但这很容易在bswap_32的紧密循环中完成。

在任一方向前缀的数据有效载荷的每个消息的开始一个简单的小控制结构:

struct msgcontrol { 
    int what_i_am; 
    int operation_to_perform; 
    int payload_length; 
    int payload[0]; 
}; 

特别说明:我已经商业化做过(如MPI和推出自己的)你可能不得不发出setsockopt调用来增加内核的大小套接字缓冲区的大小足以支撑一大堆数据

实际上,现在我想起来了,如果你不想滚动你自己的,MPI可能会感兴趣。但是,使用它,我不是一个真正的粉丝。它有意想不到的问题,我们不得不删除它,直接控制我们的套接字。

+0

首先,谢谢你的回答和解释!你的解释与我在其他几次讨论中发现的一些线索是一致的。这台服务器实际上是要处理几个大型数组(〜15000个数字)的计算平均值,这就是为什么我想对它进行线程化的原因。平方运算只是开始使用RPC的一种简单方法,并试图使服务器成为多线程而不会遇到尝试传递数组的复杂问题。看来XDR方法是线程安全的:http://man7.org/linux/man-pages/man3/xdr.3.html 我想我会坚持单线程。 –

+0

此外,我觉得svc_ *方法在一定程度上也必须是线程安全的,因为它们在rpcgen -a -M生成的代码中(-M选项应该生成线程安全代码,除非我丢失了东西) –

+0

@YakAttack你错过了一些东西,但是你必须深深地挖掘_really_才能找到它,所以不要觉得不好:-)这不是rpcgen所做的[这是正确的]。这就是MT RPC服务器不受Linux底层库的支持。您使用的生成代码是正确的,但仍然无效,那还有什么其他解释?我已经为此更新了我的帖子。我为你提供了一个替代方案 - 我过去做过一个与你的用例非常相似的用例。 –