2015-10-18 83 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底層庫的支持。您使用的生成代碼是正確的,但仍然無效,那還有什麼其他解釋?我已經爲此更新了我的帖子。我爲你提供了一個替代方案 - 我過去做過一個與你的用例非常相似的用例。 –