LINUX系统上使用多线程化的RPC

转自:http://blog.csdn.net/yin138/article/details/5692398
使用RPC时,在LINUX系统上使用rpcgen -M并不能生成具有多线程的RPC代理存根,只是生成的代码具有线程安全特性。在网络有一些网友讨论了一种实现方式,就是在接收到客户端的请求时,建立一个线程来处理客户端代码,这方 式一个外国的朋友给出了实现代码( http://www.redhat.com/archives/open-source-now-list/2004-June/msg00000.html  ),但在复制到我的LINUX(LINUX RHL5)时,并不能成功运行。在国内有一作者也写一个示例,是针对 UNIX网络编程第一卷求平方根的示例写的网址如下: http://blog.chinaunix.net/u1/37472/showart_726114.html

我使用该示例仍然没能得到运行(RPC: Procedure unavailable),通过增加主线程的延时,得到了正确运行。
但是通过增加延时只会加慢RPC服务器处理数据的时间,如果时间过长,会引起RCP调用超时,如果过短,而且RCP调用(新开的线程)还没有处理完成,也是会存在着问题的。我在本地通过延时一秒,并间隔一定时隙调用100次,发

现服务器出现段错误。
我做过将使用指针修改为变量,出现错误:RPC: Server can't decode arguments,后来我发现RPC调用参数的结构体并不是一级指针,而是多级指针。也就是说该结构中还是指针变量成员。

经过试验我总结出RPC调用的时间顺序时:
svc_run()
|->   获得客户请求
|       为客户数据建立参数
|       调用服务器存根(非多线程)
|       创建一个新线程代替服务器存根执行体  |    新线程开始
|       完成线程创建                                   |    访问参数
|<----清除参数                                         |    访问错误 (数据已经清除)
 
在插入时间等待之后的时间顺序为:
svc_run()
|->   获得客户请求
|       为客户数据建立参数
|       调用服务器存根(非多线程)
|       创建一个新线程代替服务器存根执行体   |    新线程开始
|       完成线程创建                                    |    访问参数
|       等待...                                             |    访问用户服务端执行体
|<-----清除参数                                        |    执行体处理
                                                              |     .........
                                                              |    返回客户端
上图展示了取得等待时间之后的调用顺序,这样就可以成功。如果客户连续请求,而客户端访问参数还没有完成时,将会导致问题,而且这个时间的设定与服务器的处理能力相关,运行负载相关。

为些我想到了使用一个线程锁来解决等待的问题,并且在产生100次连续调用之后,也没有发现问题,但并不代表没有问题,因为对于RPC的运行流程我只是在猜测,而且在返回给客户时,没有一些原始状态信息是否可以正常返回呢

?如果返回数据不是一个简单的数值,而是一个结构体数据,那会怎么样呢?所以希望能看到这时原朋友,如果使用时出现了问题,请告知我。我也会在有时间的时候来测试一下。

如下是服务端代码,是通过rpcgen生成之后再修改的.

[cpp]  view plain copy
  1. /* 
  2.  * Please do not edit this file. 
  3.  * It was generated using rpcgen. 
  4.  */  
  5. #include <unistd.h>  
  6. #include "square.h"  
  7. #include <stdio.h>  
  8. #include <stdlib.h>  
  9. #include <rpc/pmap_clnt.h>  
  10. #include <string.h>  
  11. #include <memory.h>  
  12. #include <sys/socket.h>  
  13. #include <netinet/in.h>  
  14.   
  15. #ifndef SIG_PF  
  16. #define SIG_PF void(*)(int)  
  17. #endif  
  18.   
  19. pthread_t p_thread;  
  20. pthread_attr_t attr;  
  21.   
  22. #ifdef USING_MT_RPC_WITH_LOCK  
  23. pthread_mutex_t rpcmutex;  
  24. #endif  
  25.   
  26. void*  
  27. square_prog_thread_func(void* param);  
  28.   
  29. #ifdef USING_MT_RPC_WITH_LOCK  
  30. void  
  31. square_prog_2_thread_func(  
  32.     struct svc_req *rqstp,   
  33.     register SVCXPRT *transp,   
  34.     pthread_mutex_t* pmutex);  
  35. #else  
  36. void  
  37. square_prog_2_thread_func(  
  38.     struct svc_req *rqstp,   
  39.     register SVCXPRT *transp);  
  40. #endif  
  41.   
  42. static void  
  43. square_prog_2(struct svc_req *rqstp, register SVCXPRT *transp)  
  44. {  
  45. /*        square_prog_2_thread_func(rqstp, transp); 
  46. return;*/  
  47.     struct data_str  
  48.         {  
  49.                 struct svc_req *rqstp;  
  50.                 SVCXPRT *transp;  
  51. #ifdef USING_MT_RPC_WITH_LOCK  
  52.         pthread_mutex_t *pmutex;  
  53. #endif  
  54.         } *data_ptr=(struct data_str*)malloc(sizeof(struct data_str));  
  55.         if (data_ptr == NULL){  
  56. #ifdef USING_MT_RPC_WITH_LOCK  
  57.         square_prog_2_thread_func(rqstp, transp, NULL);  
  58. #else  
  59.         square_prog_2_thread_func(rqstp, transp);  
  60. #endif  
  61.         return;  
  62.     }  
  63.         data_ptr-> rqstp = rqstp;  
  64.         data_ptr-> transp = transp;  
  65. #ifdef USING_MT_RPC_WITH_LOCK  
  66.     data_ptr-> pmutex = &rpcmutex;  
  67.     pthread_mutex_lock(&rpcmutex);  
  68. #endif  
  69.         /* Create a new thread for client. */  
  70.         pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_DETACHED);  
  71.         pthread_create(&p_thread,&attr,square_prog_thread_func ,(void *)data_ptr);  
  72. #ifdef USING_MT_RPC_WITH_LOCK  
  73.     pthread_mutex_lock(&rpcmutex);  
  74.     pthread_mutex_unlock(&rpcmutex);  
  75. #else  
  76.     sleep(1);  
  77. #endif  
  78. }  
  79.   
  80.   
  81. void*  
  82. square_prog_thread_func(void* param)  
  83. {  
  84.     struct thr_data  
  85.     {  
  86.         struct svc_req *rqstp;  
  87.         SVCXPRT *transp;  
  88. #ifdef USING_MT_RPC_WITH_LOCK  
  89.         pthread_mutex_t *pmutex;  
  90. #endif  
  91.     } *ptr_data;  
  92.   
  93.     /* recover param from thread param */  
  94.     ptr_data = (struct thr_data *)param;  
  95.     struct svc_req *rqstp = (ptr_data-> rqstp);  
  96.     register SVCXPRT *transp = (ptr_data-> transp);  
  97.     /* continue server processing ... */  
  98. #ifdef USING_MT_RPC_WITH_LOCK  
  99.     square_prog_2_thread_func(rqstp, transp, ptr_data->pmutex);  
  100. #else  
  101.     square_prog_2_thread_func(rqstp, transp);  
  102. #endif  
  103.     free(param);  
  104.     return NULL;  
  105. }  
  106.   
  107. #ifdef USING_MT_RPC_WITH_LOCK  
  108. void  
  109. square_prog_2_thread_func(  
  110.     struct svc_req *rqstp,   
  111.     register SVCXPRT *transp,  
  112.     pthread_mutex_t* pmutex)  
  113. #else  
  114. void  
  115. square_prog_2_thread_func(  
  116.     struct svc_req *rqstp,   
  117.     register SVCXPRT *transp)  
  118. #endif  
  119. {  
  120.     union {  
  121.         square_in squareproc_2_arg;  
  122.     } argument;  
  123.     union {  
  124.         square_out squareproc_2_res;  
  125.     } result;  
  126.     bool_t retval;  
  127.     xdrproc_t _xdr_argument, _xdr_result;  
  128.     bool_t (*local)(char *, void *, struct svc_req *);  
  129.   
  130.     switch (rqstp->rq_proc) {  
  131.     case NULLPROC:  
  132.         (void) svc_sendreply (transp, (xdrproc_t) xdr_void, (char *)NULL);  
  133.         return;  
  134.   
  135.     case SQUAREPROC:  
  136.         _xdr_argument = (xdrproc_t) xdr_square_in;  
  137.         _xdr_result = (xdrproc_t) xdr_square_out;  
  138.         local = (bool_t (*) (char *, void *,  struct svc_req *))squareproc_2_svc;  
  139.         break;  
  140.   
  141.     default:  
  142.         svcerr_noproc (transp);  
  143.         return;  
  144.     }  
  145.     memset ((char *)&argument, 0, sizeof (argument));  
  146.     if (!svc_getargs (transp, (xdrproc_t) _xdr_argument, (caddr_t) &argument)) {  
  147.         svcerr_decode (transp);  
  148.         return;  
  149.     }  
  150.     /* unlock rpc call thread */  
  151. #ifdef USING_MT_RPC_WITH_LOCK  
  152.     if (pmutex) pthread_mutex_unlock(pmutex);  
  153. #endif  
  154.     /* call user define function */  
  155.     retval = (bool_t) (*local)((char *)&argument, (void *)&result, rqstp);  
  156.     if (retval > 0 && !svc_sendreply(transp, (xdrproc_t) _xdr_result, (char *)&result)) {  
  157.         svcerr_systemerr (transp);  
  158.     }  
  159.     if (!svc_freeargs (transp, (xdrproc_t) _xdr_argument, (caddr_t) &argument)) {  
  160.         fprintf (stderr, "%s""unable to free arguments");  
  161.         exit (1);  
  162.     }  
  163.     if (!square_prog_2_freeresult (transp, _xdr_result, (caddr_t) &result))  
  164.         fprintf (stderr, "%s""unable to free results");  
  165.   
  166.     return;  
  167. }  
  168.   
  169. int  
  170. main (int argc, char **argv)  
  171. {  
  172.     register SVCXPRT *transp;  
  173. #ifdef USING_MT_RPC_WITH_LOCK  
  174.     /* Create a mutex object for rpc calling */  
  175.     pthread_mutex_init(&rpcmutex, NULL);  
  176. #endif  
  177.     pmap_unset (SQUARE_PROG, SQUARE_VERS);  
  178.   
  179.     transp = svcudp_create(RPC_ANYSOCK);  
  180.     if (transp == NULL) {  
  181.         fprintf (stderr, "%s""cannot create udp service.");  
  182.         exit(1);  
  183.     }  
  184.     if (!svc_register(transp, SQUARE_PROG, SQUARE_VERS, square_prog_2, IPPROTO_UDP)) {  
  185.         fprintf (stderr, "%s""unable to register (SQUARE_PROG, SQUARE_VERS, udp).");  
  186.         exit(1);  
  187.     }  
  188.   
  189.     transp = svctcp_create(RPC_ANYSOCK, 0, 0);  
  190.     if (transp == NULL) {  
  191.         fprintf (stderr, "%s""cannot create tcp service.");  
  192.         exit(1);  
  193.     }  
  194.     if (!svc_register(transp, SQUARE_PROG, SQUARE_VERS, square_prog_2, IPPROTO_TCP)) {  
  195.         fprintf (stderr, "%s""unable to register (SQUARE_PROG, SQUARE_VERS, tcp).");  
  196.         exit(1);  
  197.     }  
  198.   
  199.     svc_run ();  
  200.     fprintf (stderr, "%s""svc_run returned");  
  201.     exit (1);  
  202.     /* NOTREACHED */  
  203. }  

在代码的#70行处,可以看到在准备为客户求建立一个线程,在创建完线程之后,在#73行开始等待锁,也就是在这个时候等待参数被RPC解析并到调用服务端处理代码期间。在#152行,我释放了锁,这样在主线程就可以返回了,而辅助线程将进入自定义的服务器处理函数。这样就实现了一个比较合理的等待。

下面将程序代码以及MAKEFILE文件发到此处,希望需要这个方面信息的朋友可以测试一下,并提出更多的想法。

square.x

[cpp]  view plain copy
  1. struct square_in {  
  2.   long  arg1;  
  3. };  
  4.   
  5. struct square_out {  
  6.   long  res1;  
  7. };  
  8.   
  9. program SQUARE_PROG {  
  10.     version SQUARE_VERS {  
  11.     square_out  SQUAREPROC(square_in) = 1;  
  12.                         /* procedure number = 1 */  
  13.     } = 2;              /* version number = 2 */  
  14. } = 0x31230000;         /* program number = 0x31230000 */  

server.c 服务务用户处理函数所在地

[cpp]  view plain copy
  1. #include <unistd.h>  
  2. #include <pthread.h>  
  3.   
  4. #include    "square.h"  
  5.   
  6. bool_t  
  7. squareproc_2_svc(square_in *inp, square_out *outp, struct svc_req *rqstp)  
  8. {  
  9.     printf("thread %ld started, arg = %ld/n",  
  10.            pthread_self(), inp->arg1);  
  11.     sleep(5);  
  12.     outp->res1 = inp->arg1 * inp->arg1;  
  13.     printf("thread %ld done/n", pthread_self());  
  14.     return(TRUE);  
  15. }  
  16.   
  17. int  
  18. square_prog_2_freeresult(SVCXPRT *transp, xdrproc_t xdr_result,  
  19.                          caddr_t result)  
  20. {  
  21.     xdr_free(xdr_result, result);  
  22.     return(1);  
  23. }  

client.c 客户调用,在这里希望注意的就是clnt_destroy函数的调用,尤其是在错误的时候没有调用时,将导致客户与服务器之间保持了一个连接(在LINUX上表示找开了一个文件),如果出现多次,将在服务器上相当于打开了许多的TCP连接(文件),将导致服务器程序因为受操作系统的进程打开文件数限制而出错(打开文件时)。所以请一定要注意!

[cpp]  view plain copy
  1. #include <rpc/rpc.h>  
  2.   
  3. #include    "square.h"  
  4.   
  5.   
  6. int  
  7. main(int argc, char **argv)  
  8. {  
  9.     CLIENT      *cl;  
  10.     square_in   in;  
  11.     square_out  out;  
  12.   
  13.     if (argc != 3){  
  14.         printf("usage: client <hostname> <integer-value>");  
  15.         exit(0);  
  16.     }  
  17.   
  18.     cl = clnt_create(argv[1], SQUARE_PROG, SQUARE_VERS, "tcp");  
  19.   
  20.     in.arg1 = atol(argv[2]);  
  21.     if (squareproc_2(&in, &out, cl) != RPC_SUCCESS){  
  22.         printf("%s", clnt_sperror(cl, argv[1]));  
  23.         clnt_destroy(cl);  
  24.         exit(0);  
  25.     }  
  26.     clnt_destroy(cl);  
  27.     printf("result: %ld/n", out.res1);  
  28.     exit(0);  
  29. }  

Makefile 编译文件

[cpp]  view plain copy
  1. PROGS = client server  
  2. #cancele when you don't need thread lock to use mulitthread  
  3. MACRO = -DUSING_MT_RPC_WITH_LOCK  
  4. CFLAGS = -g -dH -Wall -DDEBUG $(MACRO)  
  5. #CFLAGS = -Wall  
  6. LINKFLAGS = -lpthread  
  7. CC = gcc  
  8.   
  9. all:    ${PROGS}  
  10.   
  11. square.h square_clnt.c square_svc.c square_xdr.c:   square.x  
  12.             rpcgen -C -M square.x  
  13.   
  14. square_clnt.o: square_clnt.c square.h  
  15.   
  16. square_svc.o: square_svc.c square.h  
  17.   
  18. client: square.h client.o square_clnt.o square_xdr.o  
  19.             ${CC} ${CFLAGS} -o $@ client.o square_clnt.o square_xdr.o  
  20.   
  21. server: square.h server.o square_svc.o square_xdr.o  
  22.             ${CC} ${CFLAGS} -o $@ server.o square_svc.o square_xdr.o $(LINKFLAGS)  
  23.   
  24. clean:  
  25.         rm -f  *o  

 

编译方法,先使用make命令编译程序,然后再将square_svc.c文件覆盖,再编译,方能实现多线程。

最后,我发现,如果客户端过早终止,服务器将出错。问题出在square_svc.c文件#156行,在给TCP回复的时候出的问题:

Program received signal SIGSEGV, Segmentation fault.
[Switching to Thread -1208173680 (LWP 28672)]
0x00ac7ef5 in writetcp () from /lib/libc.so.6
(gdb) where
#0  0x00ac7ef5 in writetcp () from /lib/libc.so.6
#1  0x00aca570 in xdrrec_endofrecord_internal () from /lib/libc.so.6
#2  0x00ac7e00 in svctcp_reply () from /lib/libc.so.6
#3  0x00ac682c in svc_sendreply_internal () from /lib/libc.so.6
#4  0x08048b39 in square_prog_2_thread_func (rqstp=0xbfe9af18, transp=0x8f36708, pmutex=0x80491a4)
    at square_svc.c:156
#5  0x08048a3a in square_prog_thread_func (param=0x8f33008) at square_svc.c:99
#6  0x00b462db in start_thread () from /lib/libpthread.so.0
#7  0x00aa014e in clone () from /lib/libc.so.6
(gdb) 
是线程在完成服务端用户函数调用准备给启用返回结果时出现的。按理应该不是出现段错误的。

如果需要服务器工作稳定的话,需要增加去这个错误的处理。希望能解决的朋友帮忙解决一下。

refs: http://blog.csdn.net/pbymw8iwm/article/details/8623106

http://blog.csdn.net/chongyangqu/article/details/4367781

http://blog.csdn.net/hiplayer/article/details/7834517

http://blog.csdn.net/pbymw8iwm/article/details/7999104

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值