??? 摘 要: CAR構(gòu)件進(jìn)行遠(yuǎn)程調(diào)用" title="遠(yuǎn)程調(diào)用">遠(yuǎn)程調(diào)用的原理和過程,針對(duì)該過程中服務(wù)器端的效率問題提出了進(jìn)程級(jí)線程池解決辦法,構(gòu)建了進(jìn)程級(jí)線程池模型,實(shí)現(xiàn)了關(guān)鍵部分的代碼。最后結(jié)合一個(gè)簡(jiǎn)單的構(gòu)件遠(yuǎn)程服務(wù)示例說明了其工作過程。
??? 關(guān)鍵詞: CAR構(gòu)件? 進(jìn)程級(jí)線程池? 遠(yuǎn)程調(diào)用
?
??? 20世紀(jì)80年代以來,目標(biāo)指向型軟件編程技術(shù)有了很大的發(fā)展,為大規(guī)模的軟件協(xié)同開發(fā)以及軟件標(biāo)準(zhǔn)化、軟件共享、軟件運(yùn)行安全機(jī)制等提供了理論基礎(chǔ)。其發(fā)展可以大致分為面向?qū)ο?/a>" title="面向?qū)ο?>面向?qū)ο?/a>編程、面向構(gòu)件編程、面向中間件編程三個(gè)階段。
??? CAR技術(shù)就是總結(jié)面向?qū)ο缶幊?、面向?gòu)件編程技術(shù)的發(fā)展歷史和經(jīng)驗(yàn),為更好地支持面向以Web服務(wù)為代表的下一代網(wǎng)絡(luò)應(yīng)用軟件開發(fā)而發(fā)明的。CAR為構(gòu)件軟件和應(yīng)用程序之間進(jìn)行通信提供了統(tǒng)一的標(biāo)準(zhǔn),它為構(gòu)件程序提供了一個(gè)面向?qū)ο蟮幕顒?dòng)環(huán)境。
1 CAR構(gòu)件技術(shù)及CAR遠(yuǎn)程構(gòu)件調(diào)用的基本原理
??? CAR(Component Application Run-Time)是一個(gè)具有國(guó)內(nèi)自主知識(shí)產(chǎn)權(quán)的構(gòu)件系統(tǒng),由上海科泰世紀(jì)科技有限公司開發(fā)。CAR構(gòu)件技術(shù)定義了一套網(wǎng)絡(luò)編程時(shí)代的構(gòu)件編程模型和編程規(guī)范,它規(guī)定了一組構(gòu)件間相互調(diào)用的標(biāo)準(zhǔn),使得二進(jìn)制構(gòu)件能夠自描述及在運(yùn)行時(shí)動(dòng)態(tài)鏈接。
1.1 CAR遠(yuǎn)程接口自動(dòng)列集/散集技術(shù)
??? CAR是面向?qū)ο蟮能浖P停瑢?duì)象是它的基本要素之一。使用對(duì)象的應(yīng)用(或另一個(gè)對(duì)象)稱為客戶,有時(shí)也稱為對(duì)象的用戶。對(duì)象和客戶之間的相互作用建立在客戶/服務(wù)器模型的基礎(chǔ)上。
??? 當(dāng)客戶端" title="客戶端">客戶端和服務(wù)器端所在地址空間不同時(shí),客戶端的進(jìn)程要調(diào)用服務(wù)器端的構(gòu)件服務(wù),屬于遠(yuǎn)程構(gòu)件調(diào)用。CAR構(gòu)件技術(shù)支持遠(yuǎn)程接口調(diào)用,通過調(diào)用數(shù)據(jù)的列集/散集技術(shù)進(jìn)行不同地址空間的數(shù)據(jù)交互。構(gòu)件服務(wù)和構(gòu)件服務(wù)調(diào)用者可以處于操作系統(tǒng)的不同空間,而調(diào)用者可以如同在同一地址空間中使用構(gòu)件一樣透明地進(jìn)行遠(yuǎn)程接口調(diào)用,也就是說完全向用戶屏蔽了底層使用標(biāo)準(zhǔn)的列集/散集過程。
??? CAR的自動(dòng)列集/散集主要用于和欣操作系統(tǒng)(Elastos),它在構(gòu)件的調(diào)用過程中的地位類似于COM的自動(dòng)列集/散集。用戶如果采用默認(rèn)的列集/散集過程,則使用一個(gè)遠(yuǎn)程接口如同使用一個(gè)本地接口一樣,完全屏蔽了數(shù)據(jù)的交換、傳遞過程。
??? Elastos2.0以存根/代理機(jī)制來實(shí)現(xiàn)遠(yuǎn)程接口自動(dòng)列集/散集,主要涉及到三個(gè)對(duì)象,即處于客戶端的代理(Proxy)對(duì)象,處于服務(wù)端的存根(stub)對(duì)象,以及處于內(nèi)核的(Object)對(duì)象。
??? 一個(gè)客戶端進(jìn)程不一定只調(diào)用一個(gè)遠(yuǎn)程構(gòu)件服務(wù)。為了更方便有效地與各個(gè)遠(yuǎn)程構(gòu)件交互數(shù)據(jù),Elastos在客戶端為每一個(gè)對(duì)應(yīng)的遠(yuǎn)程服務(wù)建立一個(gè)代理對(duì)象,記錄客戶進(jìn)程的信息、遠(yuǎn)程服務(wù)構(gòu)件對(duì)象的信息以及調(diào)用狀態(tài)等,負(fù)責(zé)客戶進(jìn)程與對(duì)應(yīng)的遠(yuǎn)程服務(wù)聯(lián)系。
??? Elastos會(huì)為每個(gè)提供遠(yuǎn)程服務(wù)的構(gòu)件對(duì)象建立一個(gè)存根對(duì)象,客戶端代理不是直接與遠(yuǎn)程提供服務(wù)的構(gòu)件對(duì)象聯(lián)系,而是與存根對(duì)象聯(lián)系,通過存根對(duì)象來調(diào)用構(gòu)件對(duì)象。
??? 內(nèi)核Object對(duì)象是聯(lián)系客戶端和服務(wù)器端的樞紐,保持了相關(guān)服務(wù)的信息以及創(chuàng)建對(duì)象代理所需要的一些信息。它的建立標(biāo)志著用戶可以通過某種方式遠(yuǎn)程獲得相關(guān)服務(wù)(服務(wù)的發(fā)布)。
??? CAR的自動(dòng)列集/散集通過在程序運(yùn)行過程中動(dòng)態(tài)生成存根/代理實(shí)現(xiàn)。一個(gè)用戶的遠(yuǎn)程構(gòu)件調(diào)用首先通過代理對(duì)象轉(zhuǎn)發(fā)到內(nèi)核相應(yīng)的Object對(duì)象,Object對(duì)象尋找到相應(yīng)的服務(wù)進(jìn)程以及存根對(duì)象,啟動(dòng)某一個(gè)服務(wù)線程并將調(diào)用轉(zhuǎn)發(fā)給存根對(duì)象,然后再由存根對(duì)象去完成調(diào)用構(gòu)件方法的過程。而調(diào)用返回的過程正好與這個(gè)流程相反。圖1是對(duì)象流程調(diào)用的一個(gè)簡(jiǎn)單示意圖。

1.2 客戶端調(diào)用遠(yuǎn)程服務(wù)步驟
??? 當(dāng)確定代理、存根都存在,并且從客戶端到服務(wù)器端建立一條可以相互通信的通路后,客戶端就可以開始遠(yuǎn)程調(diào)用了。其流程如下:
??? (1)客戶端用戶的一次CAR遠(yuǎn)程調(diào)用會(huì)轉(zhuǎn)發(fā)到代理對(duì)象上,代理對(duì)象將調(diào)用棧中的數(shù)據(jù)根據(jù)元數(shù)據(jù)" title="元數(shù)據(jù)">元數(shù)據(jù)信息打包,并傳給內(nèi)核;
??? (2)內(nèi)核根據(jù)注冊(cè)信息找到服務(wù)及存根對(duì)象,并將打包的信息傳給存根對(duì)象;
??? (3)存根對(duì)象根據(jù)元數(shù)據(jù)將數(shù)據(jù)解包,并構(gòu)建和客戶端調(diào)用棧相應(yīng)的棧內(nèi)數(shù)據(jù),并調(diào)用真正的構(gòu)件服務(wù)接口函數(shù)" title="接口函數(shù)">接口函數(shù);
??? (4)服務(wù)構(gòu)件接口函數(shù)完成調(diào)用,并返回;
??? (5)存根對(duì)象獲取接口函數(shù)調(diào)用的參數(shù)信息及返回信息, 并將其打包,通過系統(tǒng)調(diào)用返回到內(nèi)核;
??? (6)內(nèi)核將服務(wù)器端的返回信息傳遞給客戶端,客戶端從系統(tǒng)調(diào)用返回;
??? (7)代理對(duì)象獲得返回信息,根據(jù)元數(shù)據(jù)解包并回填到用戶調(diào)用棧中。整個(gè)遠(yuǎn)程構(gòu)件方法調(diào)用過程完成。
??? 圖2是按照數(shù)據(jù)流程的形式給出的一個(gè)進(jìn)程間遠(yuǎn)程調(diào)用的全部過程。

??? 從圖2可以看出,在服務(wù)進(jìn)程內(nèi),服務(wù)最終是通過服務(wù)線程來提供的。很明顯,上述過程可能存在一個(gè)問題:一個(gè)進(jìn)程可能提供不止一種服務(wù),所以可能在服務(wù)進(jìn)程內(nèi)需要多次重復(fù)地創(chuàng)建服務(wù)線程來為客戶端服務(wù)。線程的創(chuàng)建、銷毀和調(diào)度本身是有代價(jià)的,如果一個(gè)線程的任務(wù)相對(duì)簡(jiǎn)單,則時(shí)間和空間開銷便不容忽視;如果一個(gè)服務(wù)進(jìn)程同時(shí)對(duì)外提供多個(gè)服務(wù),這種開銷會(huì)顯著地降低服務(wù)效率,從而導(dǎo)致整個(gè)系統(tǒng)的效率降低。對(duì)于Elastos這樣的嵌入式實(shí)時(shí)操作系統(tǒng)來說,這是非常致命的。
2 進(jìn)程級(jí)線程池模型
??? 由上面的分析可以看出,問題的關(guān)鍵在于對(duì)線程資源的低效管理。對(duì)于共享資源,有一個(gè)很著名的設(shè)計(jì)模式:資源池(Resource Pool)。該模式正是為了解決資源的頻繁分配﹑釋放所造成的問題。為解決上述問題,可以采用線程池技術(shù),這里的線程池基于進(jìn)程級(jí)。
2.1 線程池技術(shù)的原理
??? 線程池采用預(yù)創(chuàng)建技術(shù),在應(yīng)用程序啟動(dòng)之后,將立即創(chuàng)建一定數(shù)量的線程(N),并放入空閑隊(duì)列中。這些線程都是處于阻塞(Suspended)狀態(tài),不消耗CPU,但占用較小的內(nèi)存空間。當(dāng)任務(wù)到來后,緩沖池選擇一個(gè)空閑線程,把任務(wù)傳入此線程中運(yùn)行。當(dāng)N個(gè)線程都在處理任務(wù)時(shí),緩沖池便自動(dòng)創(chuàng)建一定數(shù)量的新線程,用于處理更多的任務(wù)。在任務(wù)執(zhí)行完畢后,線程也不退出,而是繼續(xù)保持在池中等待下一次任務(wù)。當(dāng)系統(tǒng)比較空閑時(shí),大部分線程一直處于暫停狀態(tài),線程池自動(dòng)銷毀一部分線程,回收系統(tǒng)資源。
??? 基于這種預(yù)創(chuàng)建技術(shù),線程池將線程創(chuàng)建和銷毀本身所帶來的開銷分?jǐn)偟搅烁鱾€(gè)具體任務(wù)上,執(zhí)行次數(shù)越多,每個(gè)任務(wù)所分擔(dān)到的線程本身開銷則越小。
??? 面向?qū)ο缶幊讨?,?chuàng)建和銷毀對(duì)象很費(fèi)時(shí)間,因?yàn)閯?chuàng)建一個(gè)對(duì)象要獲取內(nèi)存資源或者其他更多資源。所以提高服務(wù)程序效率的一個(gè)手段就是盡可能減少創(chuàng)建和銷毀對(duì)象的次數(shù),特別是一些耗費(fèi)大量資源的對(duì)象創(chuàng)建和銷毀。如何利用已有對(duì)象來服務(wù)是一個(gè)需要解決的關(guān)鍵問題,也是為什么要設(shè)計(jì)線程池的原因。
??? 針對(duì)線程的特點(diǎn),可將一般的線程使用過程的時(shí)間分段為:
??? T1——?jiǎng)?chuàng)建線程的時(shí)間
??? T2——在線程中執(zhí)行任務(wù)的時(shí)間
??? T3——線程銷毀的時(shí)間
??? 則完成一個(gè)任務(wù)所需時(shí)間T=T1+T2+T3。
對(duì)于Elastos中完成ezAPI調(diào)用的線程而言,T2所占用的時(shí)間相對(duì)于T1、T3較短,T2中包含的時(shí)間僅為一個(gè)接口函數(shù)執(zhí)行的時(shí)間??梢钥闯?,線程本身的開銷所占的比例為(T1+T3)/(T1+T2+T3)。如果線程執(zhí)行的時(shí)間很短,則這筆開銷可能占到20%-50%左右;如果任務(wù)執(zhí)行時(shí)間很頻繁,則這筆開銷將不可忽略。所以線程池在Elastos操作系統(tǒng)中的使用,理論上可以顯著改善系統(tǒng)性能。圖3是其簡(jiǎn)單的原理圖。

??? 從圖中可以看出,線程池中的線程(池線程)同普通的線程無異,實(shí)現(xiàn)上屬于同類數(shù)據(jù)結(jié)構(gòu)。但是對(duì)于線程池中的線程,其所需資源已經(jīng)分配并初始化,池線程只用在棧中構(gòu)建好ezAPI調(diào)用的相關(guān)棧布局,就可以直接調(diào)用了。
2.2 進(jìn)程級(jí)線程池模型的實(shí)現(xiàn)
??? 在模型實(shí)現(xiàn)過程中,主要涉及三個(gè)類:進(jìn)程類、線程類、線程池類。線程池對(duì)象作為進(jìn)程數(shù)據(jù)結(jié)構(gòu)的一部分而存在,一個(gè)進(jìn)程對(duì)應(yīng)一個(gè)線程池,每個(gè)線程池中存在0~MaxThreadNum個(gè)線程。當(dāng)進(jìn)程初創(chuàng)建時(shí),線程池中的線程數(shù)為0。
2.2.1 線程池類定義
class ThreadPool {
public:
void Initialize(CProcess*pOwner);
ECODE GetThread(Thread**ppThread,ScheduleClass*pScheduleClass,uint_t uSchedulePolicy);//獲取線程池中線程
void PutBackThread(Thread*pThread);//將執(zhí)行完任務(wù)的
//線程回收
inline Thread*FindThread(ScheduleClass*pScheduleClass,
uint_t uSchedulePolicy);//尋找適合線程
inline void SetCapacity(UINT uCapacity);//設(shè)置線程池容量
~ThreadPool( );
private:
ECODE CreateThread(Thread**ppThread,ScheduleClass?鄢pScheduleClass,uint_t uSchedulePolicy);//在線程池中創(chuàng)建
//新線程
void DestroyThread(Thread*pThread);
public:
CProcess* m_pOwner;//線程池所在的進(jìn)程對(duì)象
UINT??m_uCapacity;//線程池容量
DLinkNode?m_threadList;//線程池中的空閑線程隊(duì)列
UINT??m_cThreads;//線程池中的空閑線程數(shù)量
KMutex??m_threadLock;//線程池互斥變量,用于同步
};
2.2.2 進(jìn)程類中有關(guān)線程池的代碼
class CProcess:
{
public:
CARAPI_(ULONG) AddRef(void);
CARAPI_(ULONG) Release(void);
CARAPI Start(/*[in]*/ WString wsName, /*[in]*/ WString wsArgs);
CARAPI Kill( );
public:
ThreadPool?m_threadPool;//每個(gè)進(jìn)程均有一個(gè)線程池
ProcessHContext m_hcontext;
};
2.2.3 進(jìn)程類與線程類中關(guān)于線程池操作部分的代碼
INLINE ECODE CProcess∷GetThread(Thread**ppThread)
//獲得線程
{
if(ProcessState_Finished==m_processState && this !=∷GetCurrentProcess()) {
??return E_PROCESS_ALREADY_EXITED;
}
return m_threadPool.GetThread(ppThread,pScheduleClass,uSchedulePolicy);
}
ULONG Thread∷Release(void)?//銷毀線程
{
if (ThreadState_Running!=m_uPoolThreadState) {
??if (1==lRef && !this->IsFinished() && m_pOwner)
???m_pOwner->m_threadPool.PutBackThread(this);
??else if (0==lRef) {
???m_pOwner->m_threadPool.m_threadLock.Lock();
???m_inProcLink.Detach();
???m_pOwner->m_threadPool.m_threadLock.Unlock();
??}
??delete this;
}
else assert(0 !=lRef);
return (ULONG)lRef;
}
??? 使用上面描述的線程池模型后,進(jìn)程間遠(yuǎn)程調(diào)用的過程就發(fā)生了變化,內(nèi)核空間部分的步驟7由原來的在服務(wù)進(jìn)程中直接創(chuàng)建新的線程變成了從服務(wù)進(jìn)程的線程池中直接獲取一個(gè)空閑線程來提供服務(wù),如圖4所示。

3 線程池應(yīng)用舉例
??? 下面以一個(gè)簡(jiǎn)單的遠(yuǎn)程調(diào)用實(shí)例說明上面所描述的線程池模型的應(yīng)用。
??? 假設(shè)現(xiàn)在有三個(gè)CAR構(gòu)件:car1.dll、car2.dll、car3.dll,分別提供構(gòu)件對(duì)象obj1、obj2、obj3,每個(gè)對(duì)象中實(shí)現(xiàn)接口函數(shù)如下:
obj1∷server1( )
{
?printf(″this is the first service!\n″);
}
obj2∷server2( )
{
??printf(″this is the second service!\n″);
}
obj3∷server3( )
{
??printf(″this is the third service!\n″);
}
現(xiàn)有一個(gè)服務(wù)進(jìn)程如下:
void main( )
{
??IObject*svr1 = new obj1( );
??IObject*svr2 = new obj2( );
??IObject*svr3 = new obj3( );
??EzRegisterService(server1,svr1);//注冊(cè)服務(wù)1
??EzRegisterService(server2,svr2);//注冊(cè)服務(wù)2
??EzRegisterService(server3,svr3);//注冊(cè)服務(wù)3
??wait( );
??EzUnregisterService(server1);//注銷服務(wù)1
??EzUnregisterService(server2);//注銷服務(wù)2
??EzUnregisterService(server3);//注銷服務(wù)3
??svr1.delete( );
??svr2.delete( );
??svr3.delete( );
??……
??return;
}
??? 該進(jìn)程通過三個(gè)構(gòu)件提供三種服務(wù),每種服務(wù)提供一個(gè)簡(jiǎn)單的打印功能。
??? 這時(shí)有三個(gè)客戶進(jìn)程分別如下:
void main( )
{
??IObject*host1;
??EzFindService(L″server1″,&host1);
//查詢服務(wù)1
??host1->server1();//提供服務(wù)1
??……
??return;
}
void main()
{
??IObject*host2;
??EzFindService(L″server2″,&host2);//查詢服務(wù)2
??host2->server2();//提供服務(wù)2
??……
??return;
}
void main()
{
??IObject*host3;
??EzFindService(L″server3″,&host3);//查詢服務(wù)3
??host3->server3();//提供服務(wù)3
??……
??return;
}
??? 上述過程中,當(dāng)三個(gè)客戶進(jìn)程并發(fā)進(jìn)行時(shí),服務(wù)進(jìn)程就需要為每一種服務(wù)創(chuàng)建一個(gè)服務(wù)線程以提供服務(wù),這時(shí)就會(huì)利用線程池來創(chuàng)建所用的線程。這就大大提高了服務(wù)效率,從而提高了整個(gè)系統(tǒng)的性能。
??? 線程池致力于減少線程本身的開銷對(duì)應(yīng)用所產(chǎn)生的影響,但前提是線程本身開銷與線程執(zhí)行任務(wù)相比不可忽略。如果線程本身的開銷相對(duì)于線程任務(wù)執(zhí)行開銷而言可以忽略不計(jì),則此時(shí)線程池所帶來的好處不明顯。例如對(duì)于FTP服務(wù)器以及Telnet服務(wù)器,通常傳送文件的時(shí)間較長(zhǎng),開銷較大,則此時(shí)采用線程池未必是理想的方法,而可以選擇“即時(shí)創(chuàng)建,即時(shí)銷毀”的策略。
??? 在構(gòu)件遠(yuǎn)程調(diào)用過程中,服務(wù)進(jìn)程中線程池的設(shè)計(jì)與實(shí)現(xiàn)可以大大提高服務(wù)效率。本文通過簡(jiǎn)要介紹CAR構(gòu)件遠(yuǎn)程調(diào)用的原理,引出了該過程中存在的效率問題,然后針對(duì)問題提出了進(jìn)程級(jí)線程池的模型,并給出了一個(gè)簡(jiǎn)單的實(shí)現(xiàn),通過該線程池能夠極大地提高構(gòu)件遠(yuǎn)程調(diào)用中服務(wù)器端的服務(wù)效率。
??? 該線程池是在進(jìn)程級(jí)別實(shí)現(xiàn)的,并沒有實(shí)現(xiàn)系統(tǒng)級(jí)線程池。為了在兩種方案及典型的應(yīng)用情況下取得最好的服務(wù)性能,還應(yīng)該進(jìn)一步實(shí)現(xiàn)系統(tǒng)級(jí)線程池,并根據(jù)典型應(yīng)用制定測(cè)試用例,對(duì)兩種方案進(jìn)行效率上的比較,從而從中選取一種更優(yōu)的解決方案。
參考文獻(xiàn)
[1] KORETIDE.Elastos 2.0 Operating System Manual[M/CD].http://www.koretide.com.cn,2004/2005,6.
[2] KORETIDE.CAR′s Manual[M/CD].http://www.koretide.com.cn,2004/2005,6.
[3] BOVET D P,CESATI M.Understanding the Linux Kernel,2nd Edition[M].Sebastopol:O′Reilly,2002:22-35.
[4] BUTENHOF D R.Programming with POSIX Threads.AddisonWesley,1997.
[5] KRIEMANN R.Implementation and Usage of a Thread Pool based on POSIX Threads Max-Planck-Institute for Mathematics in the Sciences.2004-10-19
[6] CALCOTE J.Thread pools and server performance.Dr.Dobb′s Juurnal,1997,7:60-64.
[7] NICHOLS B,BUTTLAR D,F(xiàn)ARRELL J P.Pthreads Programming.O′Reilly & Associates,Sebastopol,CA,1996.
