ShareMemRep 1.0 - 最佳的共享內存管理器替代方案 |
|
Aimingoo
一般會員 發表:9 回覆:22 積分:6 註冊:2002-05-13 發送簡訊給我 |
ShareMemRep 1.0
--------------------------
簡要描述
--------------------------
在寫書的時候,突發奇想地寫下了這個單元。 感謝FastShareMem及其作者Emil Santos(ems@codexterity.com),是他的這個項目啟發了我的思路。 在近一年前,我修改過FastShareMem的早期版本(1.01),并聯系Emil Santos,由他發布了改后的FastShareMem v1.2。正是由于這次修改,使我對Delphi的ShareMem有了重新的認識,也是因此才會有ShareMemRep。 ShareMemRep與FastShareMem的共同之處在于“在DLL中使用EXE的內存管理器,而無需另外編寫內存管理器的代碼”。只不過兩者實現的機制不同。 使用ShareMemRep,可以徹底地拋開BORLNDMM.DLL和ShareMem.pas單元。你無需再關心ShareMem的細節,因為DLL與EXE使用了同一個內存管理器。由此帶來的另一個好處是:你可以隨意替換EXE中的內存管理器,而無需更改DLL中的任何代碼。:)
-----
http://aiming.ynxx.com/ShareMemRep.htm
http://aiming.ynxx.com/files/ShareMemRep.1.0.zip
http://www.delphibbs.com/delphibbs/dispq.asp?lid=1010642 發表人 - Aimingoo 於 2003/08/05 03:34:47
|
Rain
資深會員 發表:31 回覆:236 積分:268 註冊:2003-02-17 發送簡訊給我 |
|
axsoft
版主 發表:681 回覆:1056 積分:969 註冊:2002-03-13 發送簡訊給我 |
|
Aimingoo
一般會員 發表:9 回覆:22 積分:6 註冊:2002-05-13 發送簡訊給我 |
看看新改的ver 1.1版吧~~~ 哈哈哈,把ShareMem.pas重新實現了一遍,加入了安全性檢測. 代碼增加得不算多哦... 其實昨夜臨睡時就想到了安全性檢測的問題, 但太困了, 就沒改. 沒想到今天是神采奕奕,順道兒還強化了一些功能. ^,^ 謝謝Rain,GetModuleHandle(@ExeName);是寫漏了. 哈哈, 不過奇怪的是, 測試程序居然沒有報錯! WUWa!
至于external 'TestDll.dll', 不加'.dll'也無妨,是默認的. 不過我還是順手改了. 再次謝謝你的細心啊. 下載:
--------------------
http://aiming.ynxx.com/ShareMemRep.htm
http://aiming.ynxx.com/files/ShareMemRep.1.1.zip
http://www.delphibbs.com/delphibbs/dispq.asp?lid=1010642
|
Aimingoo
一般會員 發表:9 回覆:22 積分:6 註冊:2002-05-13 發送簡訊給我 |
to Rain, 此外, borlndmm.dll和ShareMem.pas的實現方案與這個shareMemRep.pas和FastShareMem.pas是不同的. 所以, Delphi的這個ShareMem大概要比后兩者慢8倍速度(這是Emil Santos的測試結果). borlndmm.dll的速度主要犧牲在臨界區和共享內存塊的分配上. 而我和Emil Santos的方案都不使用這種機制. ^,^ borlndmm.dll的大小與方案倒是沒太大的關系, 說實話, 內存管理器沒有可能做得很大.
|
Rain
資深會員 發表:31 回覆:236 積分:268 註冊:2003-02-17 發送簡訊給我 |
Aimingoo大大:
謝謝!又學到一些東西了:)
我發現關於BORLNDMM.DLL在BCB中的DLL單元注釋比DELPHI中的要詳細,
下面一段是DELPHI中沒有的,卻是比較關鍵的地方:
---------------------------------------------------------------------
Adding MEMMGR.LIB to your project will change the DLL and its calling
EXE's to use the BORLNDMM.DLL as their memory manager
----------------------------------------------------------------------
正如您提到的,BORNDMM.DLL中另外實現了一個記憶體管理器(從ShareMem.pas單元看看東西確實不多),提供給EXE使用,相對而言讓DLL和EXE共用一個記憶體管理器確實是個非常好的思路(按照您代碼中實現的,是否是‘在EXE中使用DLL的內存管理器’,和上面的說明會反了過來),我比較好奇的是EXE和DLL中的記憶體管理器有什麼不同,記憶體管理機制是怎麼樣的?這些對我總的來說都屬於頭疼級的,看來我該封閉一段時間去學點東西了:)
|
Aimingoo
一般會員 發表:9 回覆:22 積分:6 註冊:2002-05-13 發送簡訊給我 |
Rain, 看來你還是沒有很好地讀懂ShareMemRep的代碼。^,^ 寫這個內存管理器時,我正在寫書的第6章“Delphi的積木藝術(PE)”,寫到如下一段:
---------------
.EXE和.BPL中的導出表 .EXE中也可以存在導出表,但這種使用方法并不常見。在Windows 2003的System32目錄中的4000多個文件中,僅有10余個.EXE擁有導出表。其中ntoskrnl.exe的導出表有1500多個導出項。 使用關鍵字exports,Delphi中可以方便地在.EXE中導出例程和變量,這與在.DLL中導出沒有任何的不同。 在.EXE中導出的理由之一,是使得其它模塊可以訪問.EXE模塊(或者說HOST進程)中的例程與變量。這樣的技巧通常應用在插件(Plugins)設計或者腳本引擎上。例如,第七章“內存管理的實現”中就使用這樣的方法實現了ShareMemRep。
--------------- 我當時力圖想一段代碼來體現“在.EXE中導出”的意義,然后就“突發奇想地”寫下了ShareMemRep。 你大概是看到ShareMemRep.pas中使用了Exports來導出,便以為是“在EXE中使用DLL的內存管理器”。而事實上,使用這個單元之后,EXE和DLL都會有導出項:
-------------
exports
System.GetMemoryManager,
System.GetHeapStatus,
_GetAllocMemCount name 'GetAllocMemCount',
_GetAllocMemSize name 'GetAllocMemSize';
------------- 在單元初始化時,如果模塊是.DLL,則將從HOST模塊(.EXE)的導出表中取得四個例程地址,然后修改內存管理器和填寫單元聲明中的三個函數變量。 在Delphi中的ShareMem.pas,這是三個函數,而在ShareMemRep中,我把它們聲明成了函數變量。這使得兩者的使用方法一致,但.DLL中的函數變量卻可以被置為與HOST一致的值,也就是HOST導出的值。 所以在單元初始化之后,DLL使用的完全是HOST的內存管理器,而且一些內存管理例程,也指向HOST。所以,是full function implementation。
^,^ .DLL中的這四個導出項沒有什么意義。
|
Aimingoo
一般會員 發表:9 回覆:22 積分:6 註冊:2002-05-13 發送簡訊給我 |
順道兒說說FastShareMem。 FastShareMem的核心也是“在DLL中使用EXE的內存管理器”,但是,為了將EXE中的內存管理器地址傳給DLL,FastShareMem遍歷堆頂,查找一個未用的內存塊,然后將MemMgr的地址寫到該位置上,為了校驗其可靠性,還加入了Sign。 說起來,這樣的方法,類似于一些DOS病毒的內存駐留技巧。^,^ FastShareMem早期版本時僅查找堆頂的一個內存塊,而如果這個內存塊已經被占用,就什么也干不了了。我當時所做的修改是遍歷多個內存塊,并加入MemMgr引用計數,這樣在裝載和卸載時的穩定性就大大增強了。 但是FastShareMem在堆頂使用內存塊來存放值的方法并不是非常可靠,因此我也一直不是太推薦用這個東東。而ShareMemRep就根本沒有這樣的問題,因為它查找Host的MemMgr是依賴的是Winodows的模塊管理機制,而不是內存管理機制。所以根本上來講,就不可能沖突。
|
Aimingoo
一般會員 發表:9 回覆:22 積分:6 註冊:2002-05-13 發送簡訊給我 |
剛吃完午飯(黑黑黑黑),又看看這個貼。 你問到“EXE和DLL中的內存管理器有什么不同”。其實沒什么不同。
在內存使用中,如果EXE與DLL相互獨立。那么,除了DLL使用HOST的棧之外,二者使用完全強立的兩套內存管理器。 如果.EXE與.DLL都是Delphi編寫的,那么,至少表面看起來,兩個MemMgr是兩套完全相同的代碼。也就是那個GetMem.inc。 由于是兩套獨立的代碼,所以它們內部處理引用計數以及內存釋放時都各自獨立,因而會出現“在HOST中釋放過一個字符串,而DLL中認為該字符串還需要再次釋放”的情況,對象亦是如此。
所以,在DLL中不能導出對象,也不能使用字符串參數(事實上,有引用計數機制的類型都不能用)。 如果使用“帶運行期包編譯”,則DLL和EXE都可以使用位于rtl70.bpl中的內存管理器。只要內存管理器一致,這樣就不會出現問題了。正是因為這樣的緣故,BPL中可以導出類,且多個BPL可以共享使用rtl70.bpl。 很顯然,問題是出在“.EXE與.DLL使用各自的內存管理器(至于內存管理器的實現細節,你暫時可以不考慮它)”。因此,解決問題的方法,就是:在EXE與DLL中建立一種機制,使得二者使用同一個管理器。ShareMem.pas就是這樣做的:
---------------------------------- // Code From ShareMem.pas procedure InitMemoryManager; var SharedMemoryManager: TMemoryManager; MM: Integer; begin // force a static reference to borlndmm.dll, so we don't have to LoadLibrary SharedMemoryManager.GetMem := SysGetMem; MM := GetModuleHandle(DelphiMM); SharedMemoryManager.GetMem := GetProcAddress(MM,'@Borlndmm@SysGetMem$qqri'); SharedMemoryManager.FreeMem := GetProcAddress(MM,'@Borlndmm@SysFreeMem$qqrpv'); SharedMemoryManager.ReallocMem := GetProcAddress(MM, '@Borlndmm@SysReallocMem$qqrpvi'); SetMemoryManager(SharedMemoryManager); end; initialization if not IsMemoryManagerSet then InitMemoryManager; end. ----------------------------------由于要求EXE與DLL都引用該單元,因此,在EXE與DLL的單元初始化結束后,兩者都使用了來自Borlndmm.dll的三個內存管理例程。這,其實就是內存管理器的全部。^,^ 現在調過頭來想想,Borland處理這個問題時的確有些笨拙:既然.EXE就能導出例程,那么何必再用borlndmm.dll來作為中介呢??? 剛才又讀了一遍GetMem.inc,發現在核心部分,內存管理與TLS(線程局部存儲)是無關的,但是GetMem.inc使用了一個臨界區,來使得在多線程狀況下能夠安全的分配內存。判定“是否是多線程狀況”時,使用的是全局變量IsMultiThread。 因此,在目前版本(V1.2)的ShareMemRep中,還存在一個BUG。這就是在多線程分配是,DLL和EXE中雖然使用了同一個MemMgr,但是卻沒有訪問同一個IsMultiThread變量。這意味著,在.DLL或.EXE中多線程并發分配內存塊,將會導致錯誤。^,^ 解決問題的方法是簡單的。哈哈~~~ 發表人 - Aimingoo 於 2003/08/06 15:47:31 |
Rain
資深會員 發表:31 回覆:236 積分:268 註冊:2003-02-17 發送簡訊給我 |
|
Aimingoo
一般會員 發表:9 回覆:22 積分:6 註冊:2002-05-13 發送簡訊給我 |
終于完成了這個版本的修改.哈哈哈...居然弄了這么久! 使用替換默認的DllProc的方法, 加入了多線程的支持. 使得DLL的IsMultiThread能夠與HOST同步. 加入了對第三方的內存管理器的支持. 為了能在靜態載入的.DLL中很好地支持第三方內存管理器, 動態載入了psapi. 這不影響其它情況下的使用. 即使沒有psapi.dll, 系統也能比較好的工作. 添加了一大堆的sample和注釋, 并調整了ShareMemRep的結構. 應該比原來易讀些了. 代碼添加得太多了. ;(~~~ 下載:
--------------------
http://aiming.ynxx.com/ShareMemRep.htm
http://aiming.ynxx.com/files/ShareMemRep.1.2.zip
http://www.delphibbs.com/delphibbs/dispq.asp?lid=1010642
|
Aimingoo
一般會員 發表:9 回覆:22 積分:6 註冊:2002-05-13 發送簡訊給我 |
|
Rain
資深會員 發表:31 回覆:236 積分:268 註冊:2003-02-17 發送簡訊給我 |
|
Aimingoo
一般會員 發表:9 回覆:22 積分:6 註冊:2002-05-13 發送簡訊給我 |
|
Aimingoo
一般會員 發表:9 回覆:22 積分:6 註冊:2002-05-13 發送簡訊給我 |
|
Aimingoo
一般會員 發表:9 回覆:22 積分:6 註冊:2002-05-13 發送簡訊給我 |
OK. Ver1.3已經做完了... 哈哈,這個版本改動得可就大了.此外,測試工作也做得更加全面一些. v1.3里剔除了v1.2中使用的psapi.dll,采用構造和維護一個模塊鏈表的方法,來使得HOST可以清楚地知道所有靜態和動態載入的dll的情況. v1.3里使用了"延遲替換"的方法,使靜態DLL的內存管理器能夠得到更好的管理. v1.3中重寫了框架,使以后的維護更加簡單.哈哈... 重要的是,完成了大量的測試程序.我終于不再擔心ShareMemRep.pas的穩定性問題了.hehehe... 代碼量真的是越來越大了.WUWa! 感謝rain提供的錯誤報告.^,^ 誰有興趣的話,記得看一下Samples目錄里的!!Help!!.txt. 下載:
------------------
http://aiming.ynxx.com/ShareMemRep.htm
http://aiming.ynxx.com/files/ShareMemRep.1.3.zip
http://www.delphibbs.com/delphibbs/dispq.asp?lid=1010642
|
本站聲明 |
1. 本論壇為無營利行為之開放平台,所有文章都是由網友自行張貼,如牽涉到法律糾紛一切與本站無關。 2. 假如網友發表之內容涉及侵權,而損及您的利益,請立即通知版主刪除。 3. 請勿批評中華民國元首及政府或批評各政黨,是藍是綠本站無權干涉,但這裡不是政治性論壇! |