共享內存------總結

      共享內存是允許兩個或多個進程共享一給定的區域。因為數據不需要在客户機和服務器之間複製,所以這是最快的一種IPC.使用共享內存的唯一訣竅就是多個進程對一定區域的同步存取。若服務器將數據放到共享內存區,則在服務器做完這一操作之前,客户機不應該去取這些數據。通常信號量被用來實現對共享內存存取的同步。

要使用一塊共享內存首先要分配他,隨後需要訪問這個共享內存塊的每一個進程都必須將這個共享內存綁定到自己的地址空間。當完成通訊之後,所有進程都要脱離綁定。並由一個進程釋放該共享內存。

   分配一個新的共享內存會創建新的內存頁面。因為所有進程都希望共享對同一塊內存的訪問,只應由一個進程創建一個新的共享內存,再次分配一塊已經存在
的內存塊不會創建新的頁面,而只是會返回一個標識該內存塊的標識符。一個進程如果需要使用這塊共享內存塊,則首先將他綁定到自己的地址空間中。這樣會
創建一個從進程本身虛擬地址到共享頁面的映射關係。當對共享內存的使用結束以後,這個映射關係就會被刪除。當再也沒有進程需要使用這個共享內存塊的時候
,必須有一個進程(且只能有一個)負責釋放這個被共享的頁面。
 所有共享內存塊的大小都必須是系統頁面大小的整數倍。系統頁面大小指的是系統中單個內存頁面包含的字節數。在Linux系統中,內存頁面大小是4kb,不過
仍然可以通過調用getpagesize獲取這個值。

系統通過調用shmget()來分配一個共享內存塊,該函數的第一個參數是一個用來標識共享內存塊的鍵值。彼此無關的進程可以通過指定同一鍵來獲取對共享內存塊
的訪問。不幸的是其他程序也可能挑選了同樣的特定值作為自己分配共享內存的鍵值。不幸的是其他程序也可能挑選了同樣的鍵值作為自己分配共享內存的鍵值,從
而產生衝突 。用信號量IPC_PRIVATE作為鍵值可以保證系統建立一個全新的共享內存塊。 IPC_EXCL:這個標誌只能與 IPC_CREAT 同時使用。當指定這個標誌的時候,如果已有一個具有這個鍵值的共享內存塊存在,則shmget會調用失敗。也就是説,這個標誌將使線程獲得一個“獨有”的共享內存塊。如果沒有指定這個標誌而系統中存在一個具有相通鍵值的共享內存塊,shmget會返回這個已經建立的共享內存塊,而不是重新創建一個。 模式標誌:這個值由9個位組成,分別表示屬主、屬組和其它用户對該內存塊的訪問權限。其中表示執行權限的位將被忽略。指明訪問權限的一個簡單辦法是利用<sys/stat.h>中指定,並且在手冊頁第二節stat條目中説明了的常量指定。例如,S_IRUSR和S_IWUSR分別指定了該內存塊屬主的讀寫權限,而 S_IROTH和S_IWOTH則指定了其它用户的讀寫權限。 下面例子中shmget函數創建了一個新的共享內存塊(當shm_key已被佔用時則獲取對一個已經存在共享內存塊的訪問),且只有屬主對該內存塊具有讀寫權限,其它用户不可讀寫。   int segment_id = shmget (shm_key, getpagesize (), IPC_CREAT | S_IRUSR| S_IWUSR ); 如果調用成功,shmget將返回一個共享內存標識符。如果該共享內存塊已經存在,系統會檢查訪問權限,同時會檢查該內存塊是否被標記為等待摧毀狀態。

綁定和脱離

 

要讓一個進程獲取對一塊共享內存的訪問,這個進程必須先調用 shmat(SHared Memory Attach,綁定到共享內存)。將 shmget 返回的共享內存標識符 SHMID 傳遞給這個函數作為第一個參數。該函數的第二個參數是一個指針,指向您希望用於映射該共享內存塊的進程內存地址;如果您指定NULL則Linux會自動選擇一個合適的地址用於映射。第三個參數是一個標誌位,包含了以下選項:   SHM_RND表示第二個參數指定的地址應被向下靠攏到內存頁面大小的整數倍。如果您不指定這個標誌,您將不得不在調用shmat的時候手工將共享內存塊的大小按頁面大小對齊。 SHM_RDONLY表示這個內存塊將僅允許讀取操作而禁止寫入。 如果這個函數調用成功則會返回綁定的共享內存塊對應的地址。通過 fork 函數創建的子進程同時繼承這些共享內存塊;如果需要,它們可以主動脱離這些共享內存塊。 當一個進程不再使用一個共享內存塊的時候應通過調用 shmdt(Shared Memory Detach,脱離共享內存塊)函數與該共享內存塊脱離。將由 shmat 函數返回的地址傳遞給這個函數。如果當釋放這個內存塊的進程是最後一個使用該內存塊的進程,則這個內存塊將被刪除。對 exit 或任何exec族函數的調用都會自動使進程脱離共享內存塊。

 

調用 shmctl("Shared Memory Control",控制共享內存)函數會返回一個共享內存塊的相關信息。同時 shmctl 允許程序修改這些信息。該函數的第一個參數是一個共享內存塊標識。   要獲取一個共享內存塊的相關信息,則為該函數傳遞 IPC_STAT 作為第二個參數,同時傳遞一個指向一個 struct shmid_ds 對象的指針作為第三個參數。   要刪除一個共享內存塊,則應將 IPC_RMID 作為第二個參數,而將 NULL 作為第三個參數。當最後一個綁定該共享內存塊的進程與其脱離時,該共享內存塊將被刪除。   您應當在結束使用每個共享內存塊的時候都使用 shmctl 進行釋放,以防止超過系統所允許的共享內存塊的總數限制。調用 exit 和 exec 會使進程脱離共享內存塊,但不會刪除這個內存塊。 要查看其它有關共享內存塊的操作的描述,請參考shmctl函數的手冊頁。

 

代碼 5.1 中的程序展示了共享內存塊的使用。   

代碼 5.1 (shm.c) 嘗試共享內存  

#include <stdio.h>   
#include <sys/shm.h>   
#include <sys/stat.h>   
int main()   {   
int segment_id;  
 char* shared_memory;  
 struct shmid_ds shmbuffer;  
 int segment_size;  
 const int shared_segment_size = 0x6400; /* 分配一個共享內存塊 */  
 segment_id = shmget(IPC_PRIVATE, shared_segment_size, IPC_CREAT|IPC_EXCL|S_IRUSR|S_IWUSR ); /* 綁定到共享內存塊 */   
shared_memory = (char*)shmat(segment_id, 0, 0);   
printf("shared memory attached at address %p\n", shared_memory); /* 確定共享內存的大小 */  
 shmctl(segment_id, IPC_STAT, &shmbuffer);   segment_size = shmbuffer.shm_segsz;   printf("segment size: %d\n", segment_size);   sprintf(shared_memory, "Hello, world."); /* 在共享內存中寫入一個字符串 */  
 shmdt(shared_memory); /* 脱離該共享內存塊 */ 
  shared_memory = (char*)shmat(segment_id, (void*) 0x500000, 0);/* 重新綁定該內存塊 */ 
  printf("shared memory reattached at address %p\n", shared_memory); 
  printf("%s\n", shared_memory); /* 輸出共享內存中的字符串 */
   shmdt(shared_memory); /* 脱離該共享內存塊 */   
shmctl(segment_id, IPC_RMID, 0);/* 釋放這個共享內存塊 */  
 return 0;   }

 

使用ipcs 命令可用於查看系統中包括共享內存在內的進程間通信機制的信息。指定-m參數以獲取有關共享內存的信息。例如,以下的示例表示有一個編號為1627649的共享內存塊正在使用中:   % ipcs -m ------ Shared Memory Segments -------- key shmid owner perms bytes nattch status 0x00000000 1627649 user 640 25600 0 如果這個共享內存塊在程序結束後沒有被刪除而是被錯誤地保留下來,您可以用ipcrm命令刪除它。   % ipcrm shm 1627649

控制和釋放共享內存塊