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

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

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

??? 從圖中可以看出,線程池中的線程(池線程)同普通的線程無異,實現上屬于同類數據結構。但是對于線程池中的線程,其所需資源已經分配并初始化,池線程只用在棧中構建好ezAPI調用的相關棧布局,就可以直接調用了。
2.2 進程級線程池模型的實現
??? 在模型實現過程中,主要涉及三個類:進程類、線程類、線程池類。線程池對象作為進程數據結構的一部分而存在,一個進程對應一個線程池,每個線程池中存在0~MaxThreadNum個線程。當進程初創(chuàng)建時,線程池中的線程數為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í)行完任務的
//線程回收
inline Thread*FindThread(ScheduleClass*pScheduleClass,
uint_t uSchedulePolicy);//尋找適合線程
inline void SetCapacity(UINT uCapacity);//設置線程池容量
~ThreadPool( );
private:
ECODE CreateThread(Thread**ppThread,ScheduleClass?鄢pScheduleClass,uint_t uSchedulePolicy);//在線程池中創(chuàng)建
//新線程
void DestroyThread(Thread*pThread);
public:
CProcess* m_pOwner;//線程池所在的進程對象
UINT??m_uCapacity;//線程池容量
DLinkNode?m_threadList;//線程池中的空閑線程隊列
UINT??m_cThreads;//線程池中的空閑線程數量
KMutex??m_threadLock;//線程池互斥變量,用于同步
};
2.2.2 進程類中有關線程池的代碼
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;//每個進程均有一個線程池
ProcessHContext m_hcontext;
};
2.2.3 進程類與線程類中關于線程池操作部分的代碼
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;
}
??? 使用上面描述的線程池模型后,進程間遠程調用的過程就發(fā)生了變化,內核空間部分的步驟7由原來的在服務進程中直接創(chuàng)建新的線程變成了從服務進程的線程池中直接獲取一個空閑線程來提供服務,如圖4所示。

3 線程池應用舉例
??? 下面以一個簡單的遠程調用實例說明上面所描述的線程池模型的應用。
??? 假設現在有三個CAR構件:car1.dll、car2.dll、car3.dll,分別提供構件對象obj1、obj2、obj3,每個對象中實現接口函數如下:
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″);
}
現有一個服務進程如下:
void main( )
{
??IObject*svr1 = new obj1( );
??IObject*svr2 = new obj2( );
??IObject*svr3 = new obj3( );
??EzRegisterService(server1,svr1);//注冊服務1
??EzRegisterService(server2,svr2);//注冊服務2
??EzRegisterService(server3,svr3);//注冊服務3
??wait( );
??EzUnregisterService(server1);//注銷服務1
??EzUnregisterService(server2);//注銷服務2
??EzUnregisterService(server3);//注銷服務3
??svr1.delete( );
??svr2.delete( );
??svr3.delete( );
??……
??return;
}
??? 該進程通過三個構件提供三種服務,每種服務提供一個簡單的打印功能。
??? 這時有三個客戶進程分別如下:
void main( )
{
??IObject*host1;
??EzFindService(L″server1″,&host1);
//查詢服務1
??host1->server1();//提供服務1
??……
??return;
}
void main()
{
??IObject*host2;
??EzFindService(L″server2″,&host2);//查詢服務2
??host2->server2();//提供服務2
??……
??return;
}
void main()
{
??IObject*host3;
??EzFindService(L″server3″,&host3);//查詢服務3
??host3->server3();//提供服務3
??……
??return;
}
??? 上述過程中,當三個客戶進程并發(fā)進行時,服務進程就需要為每一種服務創(chuàng)建一個服務線程以提供服務,這時就會利用線程池來創(chuàng)建所用的線程。這就大大提高了服務效率,從而提高了整個系統(tǒng)的性能。
??? 線程池致力于減少線程本身的開銷對應用所產生的影響,但前提是線程本身開銷與線程執(zhí)行任務相比不可忽略。如果線程本身的開銷相對于線程任務執(zhí)行開銷而言可以忽略不計,則此時線程池所帶來的好處不明顯。例如對于FTP服務器以及Telnet服務器,通常傳送文件的時間較長,開銷較大,則此時采用線程池未必是理想的方法,而可以選擇“即時創(chuàng)建,即時銷毀”的策略。
??? 在構件遠程調用過程中,服務進程中線程池的設計與實現可以大大提高服務效率。本文通過簡要介紹CAR構件遠程調用的原理,引出了該過程中存在的效率問題,然后針對問題提出了進程級線程池的模型,并給出了一個簡單的實現,通過該線程池能夠極大地提高構件遠程調用中服務器端的服務效率。
??? 該線程池是在進程級別實現的,并沒有實現系統(tǒng)級線程池。為了在兩種方案及典型的應用情況下取得最好的服務性能,還應該進一步實現系統(tǒng)級線程池,并根據典型應用制定測試用例,對兩種方案進行效率上的比較,從而從中選取一種更優(yōu)的解決方案。
參考文獻
[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,FARRELL J P.Pthreads Programming.O′Reilly & Associates,Sebastopol,CA,1996.
