為了便於學習,本系列文章轉載於http://www.cppblog.com/suiaiguo/archive/2009/07/20/90619.html,如果需要轉載,請註明轉載原網址。
在Win16環境中,DLL的全域資料對每個載入它的進程來說都是相同的,因為所有的進程用的都收同一塊地址空間;而在Win32環境中,情況卻發生了變化,每個進程都有了它自己的地址空間,DLL函數中的代碼所建立的任何對象(包括變數)都歸調用它的進程所有。當進程在載入DLL時,作業系統自動把DLL地址映射到該進程的私人空間,也就是進程的虛擬位址空間,而且也複製該DLL的全域資料的一份拷貝到該進程空間。(在實體記憶體中,多進程載入DLL時,DLL的程式碼片段實際上是只載入了一次,只是將物理地址映射到了各個調用它的進程的虛擬位址空間中,而全域資料會在每個進程都分別載入)。也就是說每個進程所擁有的相同的DLL的全域資料,它們的名稱相同,但其值卻並不一定是相同的,而且是互不干涉的。
因此,在Win32環境下要想在多個進程中共用資料,就必須進行必要的設定。在訪問同一個Dll的各進程之間共用儲存空間是通過儲存空間對應檔技術實現的。也可以把這些需要共用的資料分離出來,放置在一個獨立的資料區段裡,並把該段的屬性設定為共用。必須給這些變數賦初值,否則編譯器會把沒有賦初始值的變數放在一個叫未被初始化的資料區段中。
在DLL的實現檔案中添加下列代碼:
#pragma data_seg("DLLSharedSection") // 聲明共用資料區段,並命名該資料區段
int SharedData = 123; // 必須在定義的同時進行初始化!!!!
#pragma data_seg()
在#pragma data_seg("DLLSharedSection")和#pragma data_seg()之間的所有變數將被訪問該Dll的所有進程看到和共用。僅定義一個資料區段還不能達到共用資料的目的,還要告訴編譯器該段的屬性,有三種方法可以實現該目的(其效果是相同的),一種方法是在.DEF檔案中加入如下語句:
SETCTIONS
DLLSharedSection READ WRITE SHARED
另一種方法是在項目設定的連結選項(Project Setting --〉Link)中加入如下語句:
/SECTION:DLLSharedSection,rws
還有一種就是使用指令:
#pragma comment(linker,"/section:.DLLSharedSection,rws")
那麼這個資料節中的資料可以在所有DLL的執行個體之間共用了。所有對這些資料的操作都針對同一個執行個體的,而不是在每個進程的地址空間中都有一份。
當進程隱式或顯式調用一個動態庫裡的函數時,系統都要把這個動態庫映射到這個進程的虛擬位址空間裡。這使得DLL成為進程的一部分,以這個進程的身份執行,使用這個進程的堆棧。
下面來談一下在具體使用共用資料區段時需要注意的一些問題:
· 所有在共用資料區段中的變數,只有在資料區段中經過了初始化之後,才會是進程間共用的。如果沒有初始化,那麼進程間訪問該變數則是未定義的。
· 所有的共用變數都要放置在共用資料區段中。如何定義很大的數組,那麼也會導致很大的DLL。
· 不要在共用資料區段中存放進程相關的資訊。Win32中大多數的資料結構和值(比如HANDLE)只在特定的進程上下文中才是有效地。
· 每個進程都有它自己的地址空間。因此不要在共用資料區段中共用指標,指標指向的地址在不同的地址空間中是不一樣的。
· DLL在每個進程中是被映射在不同的虛擬位址空間中的,因此函數指標也是不安全的。
當然還有其它的方法來進行進程間的資料共用,比如檔案記憶體映射等,這就涉及到通用的處理序間通訊了,這裡就不多講了。