我的新浪微博: http://weibo.com/freshairbrucewoo 。
歡迎大家相互交流,共同提高技術。
?
六、 NFS 協議之 RPC 的實現
因為 nfs 服務器啟動時的端口是不確定的,所以 nfs 服務器將自己的端口注冊到 rpc 服務,客戶端通過 rpc 請求知道 nfs 服務器的監聽端口。下面就分析整個 rpc 的處理過程。現在假設客戶端有一個 rpc 請求達到服務器端了,通過上面 nfs 協議初始化的分析知道:所有的數據讀寫事件都是在函數 nfs_rpcsvc_conn_data_handler 中處理,因為是客戶端發送來的請求數據,所以執行的是 epoll_in 事件處理相關代碼,這些事件的處理都是在函數 nfs_rpcsvc_conn_data_poll_in 中,這個函數實現如下:
1 int nfs_rpcsvc_conn_data_poll_in (rpcsvc_conn_t * conn) 2 3 { 4 5 ssize_t dataread = - 1 ; 6 7 size_t readsize = 0 ; 8 9 char *readaddr = NULL; 10 11 int ret = - 1 ; 12 13 readaddr = nfs_rpcsvc_record_read_addr (&conn->rstate); // rpc服務記錄開始讀取數據的地址 14 15 readsize = nfs_rpcsvc_record_read_size (&conn->rstate); // rpc服務記錄數據需要讀取的長度 16 17 dataread = nfs_rpcsvc_socket_read (conn->sockfd, readaddr, readsize); // 從socket中讀出記錄數據 18 19 if (dataread > 0 ) 20 21 ret = nfs_rpcsvc_record_update_state (conn, dataread); // 根據讀取的數據處理 22 23 return ret; 24 25 }
?
上面代碼首先會根據 rpc 服務記錄中的接收數據類型來判斷接收什么數據,主要是分為頭部消息和正式的 rpc 消息,正式的 rpc 消息的長度是通過頭部消息中給出的,所以接收消息的步驟一般是先頭部消息,然后正式的 rpc 調用消息,否則就是視為錯誤的消息,然后根據消息的長度從 socket 中讀出消息到 rpc 服務記錄的結構體的成員變量中,最后交給函數 nfs_rpcsvc_record_update_state 處理,它根據讀取的數據來處理整個 rpc 的過程,包括 xdr (外部數據表示)和根據消息獲取調用的函數并且執行函數,具體實現如下:
1 int nfs_rpcsvc_record_update_state (rpcsvc_conn_t * conn, ssize_t dataread) 2 3 { 4 5 rpcsvc_record_state_t *rs = NULL; 6 7 rpcsvc_t *svc = NULL; 8 9 rs = &conn-> rstate; 10 11 if (nfs_rpcsvc_record_readfraghdr(rs)) // 根據rpc服務的記錄狀態是否讀取頭部消息 12 13 dataread = nfs_rpcsvc_record_update_fraghdr (rs, dataread); // 讀取消息頭部 14 15 if (nfs_rpcsvc_record_readfrag(rs)) { // 是否讀取后面的數據 16 17 if ((dataread > 0 ) && (nfs_rpcsvc_record_vectored (rs))) { // 是否讀取向量片段( 18 19 dataread = nfs_rpcsvc_handle_vectored_frag (conn, dataread); // 處理向量片段數據 20 21 } else if (dataread > 0 ) { 22 23 dataread = nfs_rpcsvc_record_update_frag (rs, dataread); // 更新rpc服務記錄的片段數據 24 25 } 26 27 } 28 29 if ((nfs_rpcsvc_record_readfraghdr(rs)) && (rs->islastfrag)) { // 如果下一條消息是頭部消息且是最后一幀 30 31 nfs_rpcsvc_handle_rpc_call (conn); // 處理rpc調用 32 33 svc = nfs_rpcsvc_conn_rpcsvc (conn); // 鏈接對象引用加1 34 35 nfs_rpcsvc_record_init (rs, svc->ctx->iobuf_pool); // 重新初始化rpc服務記錄的狀態信息 36 37 } 38 39 return 0 ; 40 41 }
?
整個函數首先讀取協議信息的頭部消息,讀取完頭部信息以后更新 rpc 服務記錄狀態,然后根據更新的狀態繼續讀取頭部信息后面的消息,后面的消息分為兩種情況來讀取,一般第一次來的是一個頭部消息,這個消息中記錄了下一次需要讀取的消息的長度,也就是正式的 rpc 調用信息的長度。所以當第二次消息響應來的時候就是正式消息,根據不同的消息有不同的處理方式。頭部消息處理方式主要是為接收正式的消息做一些初始化和準備工作(例如數據的長度和類型等)。如果頭部消息則不會執行處理 rpc 的調用函數,因為它必須要接收到 rpc 調用消息以后才能處理。下面繼續分析處理 rpc 調用的函數 nfs_rpcsvc_handle_rpc_call ,因為它是處理整個 rpc 調用的核心,它的實現如下:
1 int nfs_rpcsvc_handle_rpc_call (rpcsvc_conn_t * conn) 2 3 { 4 5 rpcsvc_actor_t *actor = NULL; 6 7 rpcsvc_request_t *req = NULL; 8 9 int ret = - 1 ; 10 11 req = nfs_rpcsvc_request_create (conn); // 動態創建一個rpc服務請求對象(結構體) 12 13 if (!nfs_rpcsvc_request_accepted (req)) // 是否接受rpc服務請求 14 15 ; 16 17 actor = nfs_rpcsvc_program_actor (req); // 得到rpc服務調用過程的描述對象 18 19 if ((actor) && (actor-> actor)) { 20 21 THIS = nfs_rpcsvc_request_actorxl (req); // 得到請求的xlator鏈表 22 23 nfs_rpcsvc_conn_ref (conn); // 鏈接狀態對象的引用加1 24 25 ret = actor->actor (req); // 執行函數調用 26 27 } 28 29 return ret; 30 31 }
?
這個函數首先根據鏈接狀態對象創建一個 rpc 服務請求的對象,然后根據 rpc 服務請求對象得到一個 rpc 服務調用過程的描述對象,最后就根據這個描述對象執行具體的某一個 rpc 遠程調用請求。下面在看看怎樣根據連接狀態對象創建 rpc 服務請求對象的, nfs_rpcsvc_request_create 函數實現如下:
1 rpcsvc_request_t * nfs_rpcsvc_request_create (rpcsvc_conn_t * conn) 2 3 { 4 5 char *msgbuf = NULL; 6 7 struct rpc_msg rpcmsg; 8 9 struct iovec progmsg; /* RPC Program payload */ 10 11 rpcsvc_request_t *req = NULL; 12 13 int ret = - 1 ; 14 15 rpcsvc_program_t *program = NULL; 16 17 nfs_rpcsvc_alloc_request (conn, req); // 從內存池中得到一個權限請求對象并且初始化為0 18 19 msgbuf = iobuf_ptr (conn->rstate.activeiob); // 從激活的IO緩存得到一個用于消息存放的緩存空間 20 21 // 從xdr數據格式轉換到rpc數據格式 22 23 ret = nfs_xdr_to_rpc_call (msgbuf, conn->rstate.recordsize, & rpcmsg, 24 25 &progmsg, req->cred.authdata, req-> verf.authdata); 26 27 nfs_rpcsvc_request_init (conn, &rpcmsg, progmsg, req); // 根據上面轉換的消息初始化rpc服務請求對象 28 29 if (nfs_rpc_call_rpcvers (&rpcmsg) != 2 ) { // rpc協議版本是否支持 30 31 ; 32 33 } 34 35 ret = __nfs_rpcsvc_program_actor (req, &program); // 根據程序版本號得到正確的rpc請求描述對象 36 37 req->program = program; 38 39 ret = nfs_rpcsvc_authenticate (req); // 執行權限驗證函數調用驗證權限 40 41 if (ret == RPCSVC_AUTH_REJECT) { // 是否被權限拒絕 42 43 ; 44 45 } 46 47 return req; 48 49 }
?
通過上面的函數調用就得到了一個正確版本的 rpc 服務遠程調用程序的描述對象,后面會根據這個對象得到對應的遠程調用函數的描述對象,這個是通過下面這個函數實現的:
1 rpcsvc_actor_t * nfs_rpcsvc_program_actor (rpcsvc_request_t * req) 2 3 { 4 5 int err = SYSTEM_ERR; 6 7 rpcsvc_actor_t *actor = NULL; 8 9 actor = &req->program->actors[req->procnum]; // 根據函數id得到正確的函數調用對象 10 11 return actor; 12 13 }
?
這里得到的函數調用對象就會返回給調用程序,調用程序就會具體執行遠程過程調用了。到此一個完整的 rpc 調用以及一個 nfs 服務就完成了, nfs 服務器就等待下一個請求,整個過程可謂一波三折,整個過程繞了很大一個圈。下面通過一個圖來完整描述整個過程:
附件 1?NFS?Protocol?Family
? |
?
NFS?Protocol?Family |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
MNTV1
:
ftp://ftp.rfc-editor.org/in-notes/rfc1094.txt
.
Directory?Path?Length
:
The?directory?path?length.
Mntv3
:
ftp://ftp.rfc-editor.org/in-notes/rfc1813.txt
.
Directory?path?length
:
The?directory?path?length.
NFS2
:
ftp://ftp.rfc-editor.org/in-notes/rfc1094.txt
.
File?info/Directory?info : The?File?info?or?directory?info.
NFS3
:
ftp://ftp.rfc-editor.org/in-notes/rfc1813.txt
.
1?The?number?of?over-the-wire?packets?for?a?given?set?of?file?operations?is?reduced?by?returning?file?attributes?on?every?operation,?thus?decreasing?the?number?of?calls?to?get?modified?attributes. 2?The?write?throughput?bottleneck?caused?by?the?synchronous?definition?of?write?in?the?NFS?version?2?protocol?has?been?addressed?by?adding?support?so?that?the?NFS?server?can?do?unsafe?writes.?Unsafe?writes?are?writes?which?have?not?been?committed?to?stable?storage?before?the?operation?returns. 3?Limitations?on?transfer?sizes?have?been?relaxed.
????The?ability?to?support?multiple?versions?of?a?protocol?in?RPC?will?allow?implementors?of?the?NFS?version?3?protocol?to?define?clients?and?servers?that?provide?backward?compatibility?with?the?existing?installed?base?of?NFS?version?2?protocol?implementations.
Object?info/?File?info/?Directory?info?Length
:
The?information?length?in?octets
NFSv4
:
ftp://ftp.rfc-editor.org/in-notes/rfc3010.txt
·?Improved?access?and?good?performance?on?the?Internet. ·?Strong?security?with?negotiation?built?into?the?protocol. ·?Good?cross-platform?interoperability. ·?Designed?for?protocol?extensions.
????The?general?file?system?model?used?for?the?NFS?version?4?protocol?is?the?same?as?previous?versions.?The?server?file?system?is?hierarchical?with?the?regular?files?contained?within?being?treated?as?opaque?byte?streams.?In?a?slight?departure,?file?and?directory?names?are?encoded?with?UTF-8?to?deal?with?the?basics?of?internationalization.
Tag?Length
:
The?length?in?bytes?of?the?tag
NLMv4
:
ftp://ftp.rfc-editor.org/in-notes/rfc1813.txt
.
Cookie?Length
:
The?cookie?length.
NSMv1
:
http://www.opengroup.org/onlinepubs/009629799/chap11.htm
.
Name?Length
:
?
The?mon?name?or?host?name?length.
|
更多文章、技術交流、商務合作、聯系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號聯系: 360901061
您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對您有幫助就好】元
