LINUX系統上使用多線程化的RPC

來源:互聯網
上載者:User

使用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產生之後再修改的.

/*<br /> * Please do not edit this file.<br /> * It was generated using rpcgen.<br /> */<br />#include <unistd.h><br />#include "square.h"<br />#include <stdio.h><br />#include <stdlib.h><br />#include <rpc/pmap_clnt.h><br />#include <string.h><br />#include <memory.h><br />#include <sys/socket.h><br />#include <netinet/in.h></p><p>#ifndef SIG_PF<br />#define SIG_PF void(*)(int)<br />#endif</p><p>pthread_t p_thread;<br />pthread_attr_t attr;</p><p>#ifdef USING_MT_RPC_WITH_LOCK<br />pthread_mutex_t rpcmutex;<br />#endif</p><p>void*<br />square_prog_thread_func(void* param);</p><p>#ifdef USING_MT_RPC_WITH_LOCK<br />void<br />square_prog_2_thread_func(<br />struct svc_req *rqstp,<br />register SVCXPRT *transp,<br />pthread_mutex_t* pmutex);<br />#else<br />void<br />square_prog_2_thread_func(<br />struct svc_req *rqstp,<br />register SVCXPRT *transp);<br />#endif</p><p>static void<br />square_prog_2(struct svc_req *rqstp, register SVCXPRT *transp)<br />{<br />/* square_prog_2_thread_func(rqstp, transp);<br />return;*/<br />struct data_str<br /> {<br /> struct svc_req *rqstp;<br /> SVCXPRT *transp;<br />#ifdef USING_MT_RPC_WITH_LOCK<br />pthread_mutex_t *pmutex;<br />#endif<br /> } *data_ptr=(struct data_str*)malloc(sizeof(struct data_str));<br /> if (data_ptr == NULL){<br />#ifdef USING_MT_RPC_WITH_LOCK<br />square_prog_2_thread_func(rqstp, transp, NULL);<br />#else<br />square_prog_2_thread_func(rqstp, transp);<br />#endif<br />return;<br />}<br /> data_ptr-> rqstp = rqstp;<br /> data_ptr-> transp = transp;<br />#ifdef USING_MT_RPC_WITH_LOCK<br />data_ptr-> pmutex = &rpcmutex;<br />pthread_mutex_lock(&rpcmutex);<br />#endif<br /> /* Create a new thread for client. */<br /> pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_DETACHED);<br /> pthread_create(&p_thread,&attr,square_prog_thread_func ,(void *)data_ptr);<br />#ifdef USING_MT_RPC_WITH_LOCK<br />pthread_mutex_lock(&rpcmutex);<br />pthread_mutex_unlock(&rpcmutex);<br />#else<br />sleep(1);<br />#endif<br />}</p><p>void*<br />square_prog_thread_func(void* param)<br />{<br />struct thr_data<br />{<br />struct svc_req *rqstp;<br />SVCXPRT *transp;<br />#ifdef USING_MT_RPC_WITH_LOCK<br />pthread_mutex_t *pmutex;<br />#endif<br />} *ptr_data;</p><p>/* recover param from thread param */<br />ptr_data = (struct thr_data *)param;<br />struct svc_req *rqstp = (ptr_data-> rqstp);<br />register SVCXPRT *transp = (ptr_data-> transp);<br />/* continue server processing ... */<br />#ifdef USING_MT_RPC_WITH_LOCK<br />square_prog_2_thread_func(rqstp, transp, ptr_data->pmutex);<br />#else<br />square_prog_2_thread_func(rqstp, transp);<br />#endif<br />free(param);<br />return NULL;<br />}</p><p>#ifdef USING_MT_RPC_WITH_LOCK<br />void<br />square_prog_2_thread_func(<br />struct svc_req *rqstp,<br />register SVCXPRT *transp,<br />pthread_mutex_t* pmutex)<br />#else<br />void<br />square_prog_2_thread_func(<br />struct svc_req *rqstp,<br />register SVCXPRT *transp)<br />#endif<br />{<br />union {<br />square_in squareproc_2_arg;<br />} argument;<br />union {<br />square_out squareproc_2_res;<br />} result;<br />bool_t retval;<br />xdrproc_t _xdr_argument, _xdr_result;<br />bool_t (*local)(char *, void *, struct svc_req *);</p><p>switch (rqstp->rq_proc) {<br />case NULLPROC:<br />(void) svc_sendreply (transp, (xdrproc_t) xdr_void, (char *)NULL);<br />return;</p><p>case SQUAREPROC:<br />_xdr_argument = (xdrproc_t) xdr_square_in;<br />_xdr_result = (xdrproc_t) xdr_square_out;<br />local = (bool_t (*) (char *, void *, struct svc_req *))squareproc_2_svc;<br />break;</p><p>default:<br />svcerr_noproc (transp);<br />return;<br />}<br />memset ((char *)&argument, 0, sizeof (argument));<br />if (!svc_getargs (transp, (xdrproc_t) _xdr_argument, (caddr_t) &argument)) {<br />svcerr_decode (transp);<br />return;<br />}<br />/* unlock rpc call thread */<br />#ifdef USING_MT_RPC_WITH_LOCK<br />if (pmutex) pthread_mutex_unlock(pmutex);<br />#endif<br />/* call user define function */<br />retval = (bool_t) (*local)((char *)&argument, (void *)&result, rqstp);<br />if (retval > 0 && !svc_sendreply(transp, (xdrproc_t) _xdr_result, (char *)&result)) {<br />svcerr_systemerr (transp);<br />}<br />if (!svc_freeargs (transp, (xdrproc_t) _xdr_argument, (caddr_t) &argument)) {<br />fprintf (stderr, "%s", "unable to free arguments");<br />exit (1);<br />}<br />if (!square_prog_2_freeresult (transp, _xdr_result, (caddr_t) &result))<br />fprintf (stderr, "%s", "unable to free results");</p><p>return;<br />}</p><p>int<br />main (int argc, char **argv)<br />{<br />register SVCXPRT *transp;<br />#ifdef USING_MT_RPC_WITH_LOCK<br />/* Create a mutex object for rpc calling */<br />pthread_mutex_init(&rpcmutex, NULL);<br />#endif<br />pmap_unset (SQUARE_PROG, SQUARE_VERS);</p><p>transp = svcudp_create(RPC_ANYSOCK);<br />if (transp == NULL) {<br />fprintf (stderr, "%s", "cannot create udp service.");<br />exit(1);<br />}<br />if (!svc_register(transp, SQUARE_PROG, SQUARE_VERS, square_prog_2, IPPROTO_UDP)) {<br />fprintf (stderr, "%s", "unable to register (SQUARE_PROG, SQUARE_VERS, udp).");<br />exit(1);<br />}</p><p>transp = svctcp_create(RPC_ANYSOCK, 0, 0);<br />if (transp == NULL) {<br />fprintf (stderr, "%s", "cannot create tcp service.");<br />exit(1);<br />}<br />if (!svc_register(transp, SQUARE_PROG, SQUARE_VERS, square_prog_2, IPPROTO_TCP)) {<br />fprintf (stderr, "%s", "unable to register (SQUARE_PROG, SQUARE_VERS, tcp).");<br />exit(1);<br />}</p><p>svc_run ();<br />fprintf (stderr, "%s", "svc_run returned");<br />exit (1);<br />/* NOTREACHED */<br />}<br />

在代碼的#70行處,可以看到在準備為客戶求建立一個線程,在建立完線程之後,在#73行開始等待鎖,也就是在這個時候等待參數被RPC解析併到調用服務端處理代碼期間。在#152行,我釋放了鎖,這樣在主線程就可以返回了,而輔助線程將進入自訂的伺服器處理函數。這樣就實現了一個比較合理的等待。

下面將程式碼以及MAKEFILE檔案發到此處,希望需要這個方面資訊的朋友可以測試一下,並提出更多的想法。

square.x

struct square_in {<br /> longarg1;<br />};</p><p>struct square_out {<br /> longres1;<br />};</p><p>program SQUARE_PROG {<br /> version SQUARE_VERS {<br />square_outSQUAREPROC(square_in) = 1;<br />/* procedure number = 1 */<br /> } = 2;/* version number = 2 */<br />} = 0x31230000;/* program number = 0x31230000 */<br />

server.c 服務務使用者處理函數所在地

#include <unistd.h><br />#include <pthread.h></p><p>#include"square.h"</p><p>bool_t<br />squareproc_2_svc(square_in *inp, square_out *outp, struct svc_req *rqstp)<br />{<br />printf("thread %ld started, arg = %ld/n",<br /> pthread_self(), inp->arg1);<br />sleep(5);<br />outp->res1 = inp->arg1 * inp->arg1;<br />printf("thread %ld done/n", pthread_self());<br />return(TRUE);<br />}</p><p>int<br />square_prog_2_freeresult(SVCXPRT *transp, xdrproc_t xdr_result,<br /> caddr_t result)<br />{<br />xdr_free(xdr_result, result);<br />return(1);<br />}<br />

client.c 客戶調用,在這裡希望注意的就是clnt_destroy函數的調用,尤其是在錯誤的時候沒有調用時,將導致客戶與伺服器之間保持了一個串連(在LINUX上表示找開了一個檔案),如果出現多次,將在伺服器上相當於開啟了許多的TCP串連(檔案),將導致伺服器程式因為受作業系統的進程開啟檔案數限制而出錯(開啟檔案時)。所以請一定要注意!

#include <rpc/rpc.h></p><p>#include"square.h"</p><p>int<br />main(int argc, char **argv)<br />{<br />CLIENT*cl;<br />square_inin;<br />square_outout;</p><p>if (argc != 3){<br />printf("usage: client <hostname> <integer-value>");<br />exit(0);<br />}</p><p>cl = clnt_create(argv[1], SQUARE_PROG, SQUARE_VERS, "tcp");</p><p>in.arg1 = atol(argv[2]);<br />if (squareproc_2(&in, &out, cl) != RPC_SUCCESS){<br />printf("%s", clnt_sperror(cl, argv[1]));<br />clnt_destroy(cl);<br />exit(0);<br />}<br />clnt_destroy(cl);<br />printf("result: %ld/n", out.res1);<br />exit(0);<br />}<br />

Makefile 編譯檔案

PROGS =client server<br />#cancele when you don't need thread lock to use mulitthread<br />MACRO = -DUSING_MT_RPC_WITH_LOCK<br />CFLAGS = -g -dH -Wall -DDEBUG $(MACRO)<br />#CFLAGS = -Wall<br />LINKFLAGS = -lpthread<br />CC = gcc</p><p>all:${PROGS}</p><p>square.h square_clnt.c square_svc.c square_xdr.c:square.x<br />rpcgen -C -M square.x</p><p>square_clnt.o: square_clnt.c square.h</p><p>square_svc.o: square_svc.c square.h</p><p>client:square.h client.o square_clnt.o square_xdr.o<br />${CC} ${CFLAGS} -o $@ client.o square_clnt.o square_xdr.o</p><p>server:square.h server.o square_svc.o square_xdr.o<br />${CC} ${CFLAGS} -o $@ server.o square_svc.o square_xdr.o $(LINKFLAGS)</p><p>clean:<br />rm -f *o<br />

 

編譯方法,先使用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)
是線程在完成服務端使用者函數調用準備給啟用返回結果時出現的。按理應該不是出現段錯誤的。

如果需要伺服器工作穩定的話,需要增加去這個錯誤的處理。希望能解決的朋友幫忙解決一下。

 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.