青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品

S.l.e!ep.¢%

像打了激速一樣,以四倍的速度運轉,開心的工作
簡單、開放、平等的公司文化;尊重個性、自由與個人價值;
posts - 1098, comments - 335, trackbacks - 0, articles - 1
  C++博客 :: 首頁 :: 新隨筆 :: 聯系 :: 聚合  :: 管理

探索一種C++二進制模塊的熱更新機制

Posted on 2010-05-26 08:24 S.l.e!ep.¢% 閱讀(1181) 評論(0)  編輯 收藏 引用 所屬分類: C++

現如今,提供穩定可靠且能滿足人民群眾日益增長的物質文化需要的服務是互聯網服務商的基本責任,所以服務端軟件一定要夠壯夠強夠靈活。服務程序一旦跑起來那就最好7X24小時地永遠別掛,而且多變的、不停增長的用戶需求也得盡快滿足。可問題是,永遠也別指望程序員寫出沒有bug的程序,任何架構師也沒有水晶球可以預測將來的花花世界,無論
當時看起來多么完美的代碼,將來也會因為種種原因要被修改(或者被丟棄?)。既然如此,那么我們或許應該想辦法給程序加上點進化能力,讓它能永不停歇地任勞任怨地工作,而同時還不斷地反省自己、糾正自己并茁壯成長。

??? 本文是寫給C++程序員的,如果你的工具是Lisp、Erlang、Ruby這樣的動態語言,那么因為它們牛B的高級動態特性,你壓根就用不著象我們這樣,在二進制層干刀口舔血的勾當。

??? 簡單來說,熱更新就是程序邊運行邊更新。有人一定覺得我在故意(象專家那樣)裝B,把簡單的問題搞得異常復雜,因為動態鏈接庫本身就是可以動態加載和卸載的,只要在新的動態庫build和部署好后通知程序重新加載一下不就搞定了?

??? 在這里,我要語重心長地告訴你們:第一,我沒有裝B,因為不想遭雷劈;第二,這種簡單的方案在少數情況下是行得通的,但在大多數情況下卻不行,因為實際的程序是代碼與數據結構的正確結合,代碼幾乎總是要操作相應的數據結構。舉個例子,A庫的create函數創建了數據對象data,foo函數能正確地操作data,然后我們用B庫熱更新了A庫,這樣現在的create和foo函數都是B庫實現的,而且新的create產生的數據對象與data(二進制布局)格式不同,新的foo也只能正確地操作新的數據對象;假設此后應用程序又需要用(B庫的)foo操作由A庫創建的data(我們無法避免,因為數據的生存周期是和應用邏輯息息相關的),這個時候嚴重的錯誤是不是就極可能發生?所以一個模塊被熱替換掉后,由它創建的所有數據對象也要跟著進行格式轉換,轉換為與新版本兼容的(二進制布局)格式。可是這又帶來了新問題:如何才能找到所有由舊模塊創建的數據對象?我們就象蹩腳數學家一樣,把一個骯臟的問題轉化成了另外一個骯臟的問題。

??? 換一種思路,如果在編程時愿意遵循一定的規范(規范是一種約束,但合理的約束卻常常能提高總體的自由),而這規范使我們能避開找到所有舊版本數據對象這樣的棘手問題,那么就能實現安全的熱更新。

??? 本文建議的規范是采用類似COM、XPCOM這樣的組件對象模型:程序由一個個的組件對象組成,每種對象提供了若干功能,外部只能通過對象的接口來使用相應功能。接口通常都用C++抽象基類來構建,從ABI(Application Binary Interface)的角度來看,C++抽象基類最關鍵的是規定了子類的虛函數表的布局。也可以用其它方法來構建接口機制,但是要保證與C++的虛函數表模型(g++, vc++等主流編譯器在這方面的實現都是一樣的)在ABI層上兼容。模塊是物理上的對象容器,可以包含一個或多個組件對象。模塊最常見的形式就是動態鏈接庫(so或dll),本文探索的動態更新機制便是以模塊為最小單位。

??? 從ABI層來看,通過組件對象接口來調用相關功能實際就是調用該對象虛表中對應項所指向的虛函數實現,正因為調用虛函數需要一個查表才能找到真正函數地址的中間操作,所以才使得我們能夠hook住組件對象的調用,從而有機會把老版本的對象轉換為新版本。那么如何才能hook對象的虛函數調用呢?方法很簡單,修改虛表,讓虛表的每一項都指向我們
的hook代碼,這樣修改之后,無論何時何地外部模塊調用老版本對象都會首先執行hook代碼。有朋友一定覺得這太hack、太不安全了:你咋就能確定虛表的位置,虛表的項數呢?是的,你的質疑一點都沒錯,如果不遵循任何規范,那么對林林總總的詭異的C++編譯器抖這么點小機靈的確是一種非常危險的動作。但幸運的是,如果你采用XPCOM這樣的組件對象模型,那么各大C++編譯器在抽象類的虛表實現上難得的共識就可以保證我們找到虛表的正確位置,而且模型額外提供的(標準C++不具有的)運行期接口類型信息又可以保證我們安全地修改適當個數的虛表項。

??? 現在可以用個簡單的例子來試驗一下上述思路是否行得通。這里提供了代碼的zip包下載,目前僅支持跑在Intel IA32架構CPU上的windows和freebsd平臺(當然,其它unix平臺也應該沒問題,只是頭文件的包含路徑有可能需要調整)的實現,windows平臺需要安裝mingw。解壓后在相應目錄下運行
??? gmake PLAT=windows

??? gmake PLAT=freebsd
就會生成test程序。在freebsd平臺下記得先用
??? export LD_LIBRARY_PATH=./
將當前目錄加入到動態庫搜索路徑中后再運行。

??? 例子包括如下幾個源程序文件:nsIBase.h nsImp1.h nsImp1.cpp nsImp2.h nsImp2.cpp dynahook.cpp test.cpp 。

??? nsIBase.h定義了一個抽象基類nsIBase,它代表著一個接口,其中包含有2個接口方法,分別是Hello和Foo。

??? nsImp1.h和nsImp1.cpp共同組成了nsIBase接口的第一個版本的實現,它們會被build成一個名為libimp1.dll(unix下是libimp1.so,下同)的動態鏈接庫,這就是一個模塊;頭文件中定義了nsImp1具體類,它繼承自nsIBase抽象類。cpp文件中除了包括接口方法的具體實現,還有三個約定的導出函數:<1>create,相當于工廠方法,因為外部模塊不知道組件對象的具體實現,所以只能用它來創建對象實例; <2>on_swapping,當該模塊被新版本模塊熱替換時,該函數會被調用,并且傳遞給它新版本模塊的格式轉換函數指針作參數,該函數應當更改當前版本對象之虛表中的各項函數指針,指向特殊的hook代碼,使得此后外部模塊對這些對象的任何調用都會首先被hook截獲,然后新模塊的格式轉換函數被執行,最后才進行通常的接口函數調用;<3>converter,格式轉換函數,當該模塊熱替換別的模塊時,它用于把老版本的對象轉換為自己版本的對象,這就包括給對象設置新的虛表指針、轉換數據塊等等。因為nsImp1是第一個版本的實現,沒有比它更老的實現需要它替換,所以它的converter是個空函數。

??? nsImp2.h和nsImp2.cpp組成了nsIBase接口的第二個版本的實現,它們被build成libimp2.dll。nsImp2的組織結構和nsImp1差不多,只不過它的converter是真正要做實際的轉換工作的。

??? dynahook.cpp是最有趣的部分,它提供了一個函數:
void dynahook(void **p_old_vtbl, int method_count, converter_t cf),其中p_old_vtbl指向老版本對象的虛函數表,method_count指明表中有多少需要被監控的接口方法, cf是(新模塊提供的)用于轉換老版本對象格式的轉換函數指針,作用就是產生特殊的hook代碼來監控相應的接口方法調用。而這所謂特殊的hook代碼其實也很簡單,它的實現如下(80X86匯編,gnu assembler格式):

??? pushl %ebp????? ; 保存caller的stack frame base
??? movl %esp, %ebp ; 設置自己的stack frame base
??? pushl 8(%ebp)?? ; this指針進棧
??? call *converter ; 調用converter轉換函數,它應該為對象設置新的虛函數表指針
??? subl $4, %esp?? ; 調整棧頂
??? movl 8(%ebp), %eax ; this指針讀入eax
??? movl (%eax), %eax? ; (新版本的)虛函數表首地址讀入eax
??? addl $method_offset, %eax" ; 加上正確的接口函數偏移量
??? movl (%eax), %eax? ; (新版本的)虛函數入口地址讀入eax
??? leave????????????? ; 恢復ebp和esp,注意其后跟的并非通常的ret指令
??? jmp *%eax????????? ; 直接跳轉到(新版本的)接口函數的實現中

??? 上述代碼只是個模板,dynahook對每個需要hook的接口方法都會依照該模板產生一段幾乎一模一樣的機器碼,只是其中converter的地址和method_offset都會被重新設置,因為它們要在運行期才能確定,然后讓虛函數表中的函數指針指向這些動態生成的hook機器碼,這不就實現了動態監控嗎?特別要注意的是,hook的實現依賴于caller通過棧來傳遞接口方法的第一個(隱含)參數――this指針。g++能滿足這一點,可是Visual C++系列卻不這樣:即使沒有使用fastcall這樣的調用規范,它也會把this指針放在ecx寄存器中傳遞(以上結論來自對編譯器生成的匯編碼的觀察,如有錯誤請指正)。有興趣的讀者可以自己改一改這段hook代碼,使它也能適合Visual C++編譯器。

??? test.cpp演示了如何使用組件對象和熱替換,部分代碼如下:

??? nsIBase *o1 = (*create_v1)(); // 創建版本為1的組件對象
??? nsIBase *o2 = (*create_v1)(); // 創建版本為1的組件對象

??? printf("create objects of version 1: %p, %p\n",
?????????? o1,o2);

??? o1->Hello();? // 直接調用版本1的實現
??? o2->Foo();??? // 直接調用版本1的實現

??? // 熱替換
??? ......

??? o1->Hello(); // 將會先觸發轉換函數,再調用版本2的實現
??? o2->Foo();?? // 將會先觸發轉換函數,再調用版本2的實現

??? o1->Hello(); // 直接調用版本2的實現
??? o2->Hello(); // 直接調用版本2的實現
??? o1->Foo();?? // 直接調用版本2的實現
??? o2->Foo();?? // 直接調用版本2的實現


程序的輸出則是:

create objects of version 1: 00032BC8, 00032CE0
obj(00032BC8), version1, Hello(), m_data->a = 2008
obj(00032CE0), version1, Foo(), m_data->a = 2008
convert object(00032BC8) from version 1 to 2
obj(00032BC8) version 2, Hello(), m_data->a = 2008, m_data->b:77
convert object(00032CE0) from version 1 to 2
obj(00032CE0) version 2, Foo(), m_data->a:2008, m_data->b:77
obj(00032BC8) version 2, Hello(), m_data->a = 2008, m_data->b:77
obj(00032CE0) version 2, Hello(), m_data->a = 2008, m_data->b:77
obj(00032BC8) version 2, Foo(), m_data->a:2008, m_data->b:77
obj(00032CE0) version 2, Foo(), m_data->a:2008, m_data->b:77

??? 現在來談談這種方案的缺點和局限性。

??? 首先,方案要求新版本模塊能知道舊版本對象的二進制布局,這就意味著要有舊的組件對象的定義文件等。如果舊版本對象聚合了第三方的(非組件型)對象,而且對象的內部狀態又不提供接口復制,那么新版本的實現就沒有辦法替換掉第三方對象。這是一個極大的限制。

??? 其次,即使擁有舊的組件對象的定義,也需要保證每個模塊的關鍵編譯參數(比如結構體的字節對齊數)相同,否則轉換函數訪問舊版本對象的數據塊就是一件非常危險的事情。

??? 再者,為了能比較方便地做數據塊的格式轉換,要求每一個版本的實現不能直接內嵌數據成員,只能用一個指針指向一塊結構體,就象nsImp1這樣:
class nsImp1 : public nsIBase
{
public:
??? nsImp1();

??? virtual void Hello();
??? virtual void Foo();

protected:
??? struct data_t
??? {
??????? int a;
??? };

??? data_t *m_data;
};
這無疑使得代碼的編寫更繁瑣。
???
??? 最后,正確地更改虛表需要知道一些諸如接口包含多少方法之類的類型信息,而C++沒有標準的方法去取得,因此組件
框架提供的動態類型信息至關重要。


本文來自CSDN博客,轉載請標明出處:http://blog.csdn.net/soloist/archive/2008/06/23/2580396.aspx

青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            亚洲黄色成人久久久| 欧美激情aⅴ一区二区三区| 午夜日本精品| 欧美天堂在线观看| 亚洲高清免费视频| 久久青青草综合| 亚洲欧美日韩精品久久久久| 欧美日韩国产一区二区三区地区 | 亚洲国产另类 国产精品国产免费| 亚洲男人第一网站| 亚洲精品乱码视频| 欧美jizzhd精品欧美巨大免费| 伊人久久大香线| 久久久久久9999| 亚洲欧美在线网| 国产女主播一区二区| 午夜一级久久| 午夜精品成人在线| 国产一区成人| 久热成人在线视频| 麻豆国产va免费精品高清在线| 在线观看成人一级片| 久久婷婷色综合| 久久综合中文| 亚洲精品在线视频观看| 亚洲欧洲中文日韩久久av乱码| 欧美激情第五页| 宅男噜噜噜66一区二区| 99视频日韩| 国产精品一二一区| 开心色5月久久精品| 欧美xx视频| 在线一区视频| 亚洲一区3d动漫同人无遮挡| 国产欧美一区二区在线观看| 欧美在线观看www| 久久久91精品国产| 日韩视频在线一区| 一区二区免费看| 国产亚洲激情| 欧美丰满高潮xxxx喷水动漫| 久久精品在线| 欧美freesex8一10精品| 国产欧美日韩另类视频免费观看| 午夜亚洲福利在线老司机| 亚洲免费在线观看| 韩国精品久久久999| 欧美电影在线观看完整版| 欧美成人官网二区| 亚洲视频免费观看| 性欧美xxxx大乳国产app| 永久域名在线精品| 91久久在线| 国产美女精品| 91久久线看在观草草青青| 国产精品你懂得| 美女视频黄免费的久久| 欧美精品在线观看| 久久精品最新地址| 欧美精品自拍| 午夜精彩视频在线观看不卡 | 欧美电影免费观看| 亚洲永久在线观看| 久久精品亚洲精品| 亚洲欧美精品| 欧美精品日韩一本| 久久夜色精品国产亚洲aⅴ| 久久婷婷综合激情| 亚洲女同精品视频| 老司机精品视频网站| 亚洲图片在线观看| 久久婷婷国产综合精品青草| 亚洲欧美日韩精品综合在线观看| 看片网站欧美日韩| 久久精品伊人| 国产精品久久久久999| 亚洲激情综合| 亚洲国产成人高清精品| 欧美一级播放| 午夜精品国产更新| 欧美午夜久久| 亚洲人成啪啪网站| 亚洲国产99精品国自产| 久久精品视频免费| 久久精品观看| 国产亚洲精品激情久久| 亚洲综合欧美| 午夜精品一区二区三区在线视 | 日韩一区二区久久| 免费成人高清视频| 欧美1区免费| 激情文学一区| 欧美伊人久久久久久久久影院 | 另类av一区二区| 卡一卡二国产精品| 国产一区美女| 久久精品一本| 美女成人午夜| 在线观看欧美| 免费视频最近日韩| 欧美国产免费| 亚洲激情一区| 欧美巨乳波霸| 国产精品99久久久久久久久| 亚洲成人在线网| 久久久久久久成人| 国产精品青草久久久久福利99| 99精品国产在热久久下载| 亚洲一区3d动漫同人无遮挡| 欧美亚洲成人精品| 亚洲主播在线观看| 久久久久成人精品| 在线播放亚洲一区| 欧美91大片| 99精品欧美一区二区三区| 亚洲一区黄色| 国产午夜精品久久| 毛片av中文字幕一区二区| 亚洲欧洲在线一区| 午夜精品国产更新| 欲香欲色天天天综合和网| 欧美福利精品| 亚洲一区欧美一区| 欧美成人xxx| 一本色道88久久加勒比精品| 国产精品男女猛烈高潮激情| 久久精品理论片| 亚洲人午夜精品免费| 亚洲欧洲av一区二区三区久久| 国产日韩精品久久| 欧美国产日本高清在线| 亚洲综合成人婷婷小说| 嫩草影视亚洲| 亚洲一区免费在线观看| 国产在线精品一区二区中文| 欧美成人激情视频| 欧美一区二区日韩一区二区| 亚洲国产三级网| 午夜精品成人在线视频| 亚洲电影专区| 国产精品大片| 久久视频在线视频| 在线亚洲精品| 欧美国产日韩a欧美在线观看| 一区二区三区四区五区视频 | 国产精品久久久久高潮| 久久精品一区二区国产| 一本色道久久综合精品竹菊| 久久久五月婷婷| 亚洲欧美成aⅴ人在线观看| 亚洲电影免费在线 | 亚洲精品一区中文| 国产精品亚洲人在线观看| 欧美激情va永久在线播放| 久久精品中文字幕一区| 亚洲午夜羞羞片| 亚洲黄色小视频| 欧美大胆人体视频| 久久激情五月丁香伊人| 亚洲一级在线观看| 一区二区不卡在线视频 午夜欧美不卡在 | 玖玖玖国产精品| 亚洲深夜福利在线| 亚洲国产色一区| 精久久久久久| 国内久久视频| 国产乱码精品一区二区三区不卡| 欧美精品国产| 欧美成人一区二区在线| 久久国产精品久久久久久电车 | 国产亚洲一级高清| 国产精品影片在线观看| 国产精品国产三级国产aⅴ9色| 欧美日产在线观看| 欧美精品在线播放| 欧美日韩成人在线| 欧美日韩国产综合新一区| 欧美国产第二页| 欧美激情精品久久久久| 免费成人av在线看| 欧美va天堂va视频va在线| 蜜桃久久精品一区二区| 免费亚洲网站| 欧美激情 亚洲a∨综合| 欧美国产日产韩国视频| 欧美日韩视频第一区| 欧美视频一区在线观看| 国产精品九九久久久久久久| 国产精品剧情在线亚洲| 国产精品一区二区久久精品| 国产毛片精品国产一区二区三区| 国产亚洲欧美另类一区二区三区| 国产综合欧美在线看| 亚洲高清免费在线| 一区二区三区四区精品| 亚洲欧美电影在线观看| 欧美一区日本一区韩国一区| 美国十次成人| 亚洲欧洲中文日韩久久av乱码| 亚洲精品免费在线|