摘要:在研究遠(yuǎn)程過程調(diào)用的原理和嵌入式系統(tǒng)特點(diǎn)的基礎(chǔ)上,提出一種遠(yuǎn)程過程調(diào)用的設(shè)計(jì)以及在VxWorks操作系統(tǒng)上服務(wù)器端和在Win-dows探作系統(tǒng)上客戶端的實(shí)現(xiàn)。經(jīng)在項(xiàng)目中的應(yīng)用,本設(shè)計(jì)與實(shí)現(xiàn)體現(xiàn)了良好的實(shí)用性、移植性和擴(kuò)展性。
關(guān)鍵詞:遠(yuǎn)程過程調(diào)用;嵌入式系統(tǒng);網(wǎng)絡(luò);狀態(tài)機(jī)
遠(yuǎn)程過程調(diào)用(Renmte Procedure call,RPC)最早是在B.J.Nelson的博士論文中論述的。這里的過程等價(jià)于例程,函數(shù)的意思。RPC的思想源于大多數(shù)的程序都以過程作為最小設(shè)計(jì)單位。RPC擴(kuò)展了過程調(diào)用機(jī)制,允許客戶端的過程通過網(wǎng)絡(luò)調(diào)用服務(wù)器端的過程。
從RPC的思想出發(fā),不同的組織和公司開發(fā)了不同的RPC協(xié)議。有SUN公司的ONC RPC,開放軟件基金會(huì)的DCE RPC,微軟公司的MSRPC等。這些RPC都依賴與特定操作系統(tǒng),并且定義了自己的接口描述語言(IDL),對(duì)于嵌入式開發(fā)過于復(fù)雜。
1 RPC的機(jī)制
1.1 過程調(diào)用
典型的過程調(diào)用就是過程A將參數(shù)和控制權(quán)交給過程B,過程B經(jīng)過一系列運(yùn)算或者下一級(jí)過程,最后把結(jié)果和控制權(quán)返回給過程A。
1.2 RPC流程
RPC分為同步RPC和異步RPC。在同步RPC中客戶端發(fā)出RPC調(diào)用的線程將被阻塞,直到從服務(wù)器端完成。異步RPC中客戶端發(fā)出調(diào)用的線程不會(huì)被阻塞而是繼續(xù)執(zhí)行。本文以同步RPC為研究對(duì)象。
RPC的思想就是使遠(yuǎn)程過程調(diào)用看上去就像在本地的過程調(diào)用一樣。從程序運(yùn)行角度來看,其流程如圖1所示??蛻舳?MACHINE A)的進(jìn)程通過網(wǎng)絡(luò)發(fā)送遠(yuǎn)程過程調(diào)用請(qǐng)求給服務(wù)器(MACHINE B)。服務(wù)器收到請(qǐng)求后處理,調(diào)用相應(yīng)的過程執(zhí)行,執(zhí)行完畢后服務(wù)器返回結(jié)果給客戶進(jìn)程。客戶進(jìn)程在發(fā)出遠(yuǎn)程過程調(diào)用后被阻塞,直到服務(wù)器返回結(jié)果給客戶進(jìn)程。
1.3 RPC的結(jié)構(gòu)模型
從描述的角度出發(fā),產(chǎn)生不同的RPC模型如Andrew S.Tanenhum在其著作分布式操作系統(tǒng)中論述的模型以及B.J.Nelson論文中的RPC模型等。但這些模型的主要組件都是相同的。圖2是B.J.Nelson博士的RPC結(jié)構(gòu)模型。客戶進(jìn)程、客戶存根和RPC運(yùn)行庫(kù)實(shí)例在客戶端執(zhí)行。服務(wù)進(jìn)程、服務(wù)器存根和RPC運(yùn)行庫(kù)實(shí)例在服務(wù)器端執(zhí)行??蛻暨^程調(diào)用相應(yīng)的客戶存根。客戶存根打包參數(shù)??蛻舳说腞PC運(yùn)行庫(kù)將打包好的參數(shù)通過網(wǎng)絡(luò)發(fā)送給服務(wù)器RPC運(yùn)行庫(kù)。服務(wù)器存根拆包參數(shù),然后調(diào)用服務(wù)器過程。完成后返回結(jié)果給服務(wù)器存根。服務(wù)器存根打包結(jié)果給服務(wù)器RPC運(yùn)行庫(kù)。服務(wù)器RPC運(yùn)行庫(kù)發(fā)送打包好的參數(shù)給客戶RPC運(yùn)行庫(kù)??蛻舸娓鸢⒔Y(jié)果取出返回給客戶。
盡管RPC的思想比較簡(jiǎn)單,但有很多問題需要考慮。由于有很多不同的CPU,如X86、ARM、SPARC等以及各種DSP、單片機(jī),產(chǎn)生了參數(shù)傳遞問題。如X86采用最低有效字節(jié)優(yōu)先,而SPARC是最高字節(jié)優(yōu)先。有些大型機(jī)采用EBCDIC碼,而其他處理器采用ASCII碼。存根就是用來解決這些問題。還有指針問題,涉及物理地址、虛擬地址、地址空間等很多考慮。我們知道不同計(jì)算機(jī)之間無法直接訪問彼此的地址。還有過程的參數(shù)如果為數(shù)據(jù)結(jié)構(gòu),這就引出數(shù)據(jù)對(duì)齊的問題。由此可以推斷所有的RPC實(shí)現(xiàn)都在一定的范圍適用。本文的RPC設(shè)計(jì)假定客戶端和服務(wù)器端有相同的大小端和并且都是32位處理器。
1.4 SunRPC
Sun RPC有時(shí)也稱為ONC(Open Network Computing)RPC。Sun RPC提供了一個(gè)接口語言IDL和rpcgen用于C語言支持。這門語言可以定義constants,typedef,structure,union。rpcgen可以產(chǎn)生server code,client stub和頭文件。server code主要是建立socket,注冊(cè)端口和監(jiān)聽,接受連接,拆參數(shù),調(diào)用實(shí)際的過程,打包返回值。client stub則是打包參數(shù),發(fā)送給server,將返回值解包。Sun RPC缺點(diǎn)就是對(duì)Windows沒有很好的支持。
2 設(shè)計(jì)
本設(shè)計(jì)與傳統(tǒng)的模型不同,服務(wù)器端分為:網(wǎng)絡(luò)通訊,接收狀態(tài)機(jī)和過程處理。客戶端分為網(wǎng)絡(luò)通訊,發(fā)送狀態(tài)機(jī)和過程調(diào)用。
圖3是服務(wù)器端的狀態(tài)機(jī)。服務(wù)器進(jìn)程從初始狀態(tài)進(jìn)入GetHeader狀態(tài)。GetHeader是讀取遠(yuǎn)程過程調(diào)用的頭信息。如果一次得到了所有數(shù)據(jù),也就是nCurLen>=dwTotalSize,則進(jìn)入GetComplatePacket狀態(tài),反之進(jìn)入GetData狀態(tài)。GetData是讀取參數(shù)數(shù)據(jù),讀取數(shù)據(jù)直到得到所有的數(shù)據(jù)進(jìn)入GetComplatePacket狀態(tài)。期間如果超時(shí),則回到GetHeader狀態(tài)。超時(shí)的起始時(shí)間從GetHeader韻第一個(gè)字節(jié)算起,如果在定義的時(shí)間無法讀取dwTotalSize個(gè)字節(jié),則Timeout從而回到GetHeader狀態(tài)。在GetHeader和GetData時(shí),如果讀取數(shù)據(jù)有錯(cuò)誤(如客戶端斷開連接,recv函數(shù)返回錯(cuò)誤)則狀態(tài)機(jī)退出。GetComplatePacket是得到了完整的包。CheckCall判斷當(dāng)前的調(diào)用是否是有效的過程調(diào)用。如果無效則進(jìn)入狀態(tài),并回復(fù)無效命令給客戶端,最后進(jìn)入GetHeader狀態(tài)。如果有效,則處理此調(diào)用,最后發(fā)送結(jié)果給客戶端。
圖4為客戶端狀態(tài)機(jī)。首先是打包參數(shù),發(fā)送到服務(wù)器端,等待服務(wù)器端的回復(fù),進(jìn)入GetHeader狀態(tài)。GetHeader,GetData和Get Com-plate Packet與服務(wù)器相應(yīng)的狀態(tài)意義相同。如果timeout則返回timeout錯(cuò)誤。如果得到了整個(gè)packet則拆分最后返回。
DCE—RPC和ONC—RPC允許選擇UDP或TCP協(xié)議。TCP協(xié)議傳輸控制協(xié)議,提供的是面向連接、可靠的字節(jié)流服務(wù)。UDP協(xié)議不提供可靠性,它只是把應(yīng)用程序傳給IP層的數(shù)據(jù)報(bào)發(fā)送出去,但是并不能保證它們能到達(dá)目的地。基于TCP協(xié)議的可靠性,選擇TCP作為通訊協(xié)議。
3 實(shí)現(xiàn)
3.1 數(shù)據(jù)結(jié)構(gòu)
服務(wù)器和客戶用共用包頭信息和每個(gè)過程的參數(shù)結(jié)構(gòu)。頭信息定義如下。dwCallID是過程的標(biāo)識(shí)號(hào)。每個(gè)過程都有一個(gè)唯一的號(hào)碼。bCallType是調(diào)用的類型。dwTotalSize是整包的字節(jié)數(shù)。dwReturn是返回結(jié)果。
過程調(diào)用的類型定義如下。RPC_TYPE_SIMPLE_WRITEREAD是簡(jiǎn)單的讀寫,輸入?yún)?shù)和輸出參數(shù)都在頭信息和過程的參數(shù)數(shù)據(jù)結(jié)構(gòu)中。RPC_TYPE_READ指返回結(jié)果保存在單獨(dú)的內(nèi)存。RPC_TYPE_WRITE指寫信息保存在單獨(dú)的內(nèi)存。RPC_TYPE_WRITE_READ調(diào)用是需要內(nèi)存保存輸入數(shù)據(jù),返回時(shí)需要保存輸出的結(jié)果。
每個(gè)過程定義自己的輸入輸出參數(shù)結(jié)構(gòu)。例如對(duì)獲取串口狀態(tài)GetCommState過程,建立RPC_GETCOMMSTATE結(jié)構(gòu)。
由于各個(gè)編譯器對(duì)struct數(shù)據(jù)結(jié)構(gòu)的成員的對(duì)齊實(shí)現(xiàn)不同。這樣同樣的struct在客戶端和服務(wù)器端的大小可能不同,同樣的成員在結(jié)構(gòu)中的位置不同。為了確??蛻舳撕头?wù)器端有相同的對(duì)齊,我們采用字節(jié)對(duì)齊用#pragma pack(1)。
3.2 Packet內(nèi)存布局
開始依次是頭信息和參數(shù),其余部分根據(jù)特定的過程而不同。以RPC_TYPE_WRITE_READ類型的布局為例:頭信息,參數(shù),輸入內(nèi)存塊[1…N],輸出內(nèi)存塊[1…N]。其他的過程類型布局類似。
3.3 服務(wù)器端實(shí)現(xiàn)
3.3.1 網(wǎng)絡(luò)模塊實(shí)現(xiàn)
RPC在單獨(dú)的任務(wù)中執(zhí)行。圖5為RPC任務(wù)流程圖。調(diào)用VxWorks的系統(tǒng)函數(shù)taskSpawn建立RPC任務(wù)。調(diào)用socket( )建立面向連接的SOCK_ STREAM套接字,bind將套接字與本地網(wǎng)絡(luò)地址和端口號(hào)捆綁,listen申明要在該端口偵聽客戶連接請(qǐng)求,accept阻塞等待請(qǐng)求的到來。
3.3.2 狀態(tài)機(jī)實(shí)現(xiàn)
當(dāng)accept后,進(jìn)入服務(wù)器端狀態(tài)機(jī)。設(shè)置accept返回的socket為非阻塞狀態(tài)。在阻塞的socket上調(diào)用send時(shí),如果沒有足夠的輸出緩沖區(qū),該調(diào)用將被阻塞。Recv也是一樣,要讀的數(shù)據(jù)沒有就緒時(shí),調(diào)用者阻塞。服務(wù)器不知道每次要讀取的字節(jié)數(shù),所以阻塞的socket無法工作。
分配2塊內(nèi)存:A和B。內(nèi)存A用來保存recv的內(nèi)容,內(nèi)存B用來保存客戶端發(fā)送的Packet內(nèi)容。因?yàn)榉?wù)器不知道客戶會(huì)發(fā)送多大的內(nèi)容過來,每次從內(nèi)存A拷貝到內(nèi)存B之前檢查內(nèi)存B的大小,如果內(nèi)存B剩余大小不夠則重新分配。
在得到了整個(gè)Packet后,即GetComplatePacket后,根據(jù)dwCallID調(diào)用服務(wù)器的本地過程,待返回后將返回值和內(nèi)存打包發(fā)送給客戶端。
3.4 客戶端實(shí)現(xiàn)
客戶端的流程如圖6所示。在Windows下運(yùn)行,首先調(diào)用WSAStartup,Windows根據(jù)請(qǐng)求的Socket版本來搜索相應(yīng)的Socket庫(kù),然后綁定找到的Socket庫(kù)到該應(yīng)用程序中。然后初始化socket,連接到服務(wù)器,接著過程調(diào)用。比如過程調(diào)用1會(huì)進(jìn)入圖4狀態(tài)機(jī)。狀態(tài)機(jī)和服務(wù)器端類似,只是首先參數(shù)打包,發(fā)送給服務(wù)器,返回后拆包并拷貝返回信息到內(nèi)存中。
4 結(jié)束語
本文設(shè)計(jì)和實(shí)現(xiàn)的RPC可應(yīng)用于白盒測(cè)試、跨平臺(tái)開發(fā)環(huán)境和開發(fā)客戶端軟件等。商用的嵌入式IDE軟件都很昂貴,通過本RPC,測(cè)試人員就可用開源的環(huán)境如cygwin等開發(fā)白盒測(cè)試代碼。另外對(duì)于有大量操作界面的嵌入式開發(fā),需要頻繁下載到開發(fā)板上驗(yàn)證,本文RPC可應(yīng)用于構(gòu)建跨平臺(tái)的開發(fā)環(huán)境,直接在Windows上開發(fā)界面部分,最后下載到開發(fā)板上,從而大大提高開發(fā)效率。大多數(shù)的嵌入式軟件都有相應(yīng)的PC客戶端軟件,本文的實(shí)現(xiàn)也適用于開發(fā)PC客戶端軟件。