2016-09-26 126 views
0

我從Eric Lippert's answer瞭解到「兩個進程可以共享非私有內存頁面,如果有二十個進程全部加載同一個DLL,那麼進程全部共享該代碼的內存頁面,它們不共享虛擬內存地址空間,它們共享記憶。」DLL如何處理來自多個進程的併發?

現在,如果硬盤上的相同DLL文件在加載到應用程序後將共享相同的物理內存(不管是RAM還是頁面文件),而是映射到不同的虛擬內存地址空間,處理併發性相當困難?

據我所知,C++中的併發概念更多的是關於處理線程 - 一個進程可以啓動多個線程,每個線程可以運行在一個單獨的核心上,所以當不同的線程同時調用DLL時,可能會有數據競速,我們需要互斥,鎖定,信號,條件變量等等。

但是,DLL如何處理多進程?數據賽車的相同概念將會發生,不是嗎?有什麼工具可以處理?仍然是相同的工具集?

+0

併發只有當你共享可寫數據的問題。這不是在這裏發生的。 –

+0

我相信這些頁面是在寫入時共享的。因此,如果在DLL內部存在可變狀態(而不僅僅是代碼),每個進程都將獲得它自己的頁面版本。 (對於Windows來說,如果知道這是否可行,或者DLL內存是隻讀的,那麼這種方法不夠完善)。 – Thilo

+0

@KerrekSB所以一個DLL不能擁有自己的可寫內存?一個DLL正在處理的所有內存是否由進程擁有? – athos

回答

2

DLL中包含多個「段」,每個段都有一個描述符告訴Windows它的Characteristics。這是一個32位的DWORD。代碼段明顯具有設置的代碼位,並且通常也是可共享的位。只讀數據也可以共享,而可寫數據通常不具有可共享標誌。

現在你可以額外段組特性的一個不尋常的組合:可寫共享。這不是默認的,確實可能會導致競爭條件。所以你的問題的最終答案是:問題主要由段的缺省特性來避免,其次,具有非標準特性段的任何DLL都必須處理自己產生的問題。

+0

額外的段,可寫和可共享?有趣!是否也在https://msdn.microsoft.com/en-us/library/ms809762.aspx中進行了描述? – athos

+0

@athos:不。Matt Pietrek的文章的觀衆並不是你的平均開發者;他們可以弄清楚它的含義。對於正常的開發者,例如有https://msdn.microsoft.com/en-us/library/h90dkhs0(v=vs.90).aspx。 (我再說一遍:你用這個想法得到的可預見的問題是自己造成的;不要告訴我們我們沒有提醒你) – MSalters

+0

注意到謝謝:) – athos

5

現在,如果硬盤上的相同DLL文件在加載到應用程序後將共享相同的物理內存(不管是RAM還是頁面文件),而是映射到不同的虛擬內存地址空間,則不會這使得併發性處理非常困難?

正如其他答案所指出的那樣,如果共享內存在初始化後永遠不會寫入,那麼併發問題就無關緊要,這通常是DLL的情況。如果您試圖通過寫入內存來更改DLL中的代碼或資源,那麼可能性很大,您在某個地方有一個錯誤的指針,最好的辦法是使用訪問衝突來崩潰。

不過,我想簡要地也跟進您的關注:

...映射到不同的虛擬內存地址空間...

在實踐中,我們非常努力地避免這種情況因爲當它發生時,第一次加載代碼頁時會出現嚴重的用戶注意的性能問題。 (當然,工作集可能會大量增加,這會導致其他性能問題。)

DLL中的代碼通常包含硬編碼的虛擬內存地址,假設代碼將被加載到已知代碼中,編譯時虛擬內存「基址」地址。如果這種假設在運行時被違反 - 例如,那裏已經存在另一個DLL,那麼所有這些硬編碼地址都需要在運行時進行修補,這很昂貴。

如果你想要一些歷史細節,請參見主題雷蒙德的文章:https://blogs.msdn.microsoft.com/oldnewthing/20041217-00/?p=36953/