1. 程式人生 > >共享記憶體及其用mmap實現共享記憶體

共享記憶體及其用mmap實現共享記憶體


一、什麼是共享記憶體

顧名思義,共享記憶體就是允許兩個不相關的程序訪問同一個邏輯記憶體。共享記憶體是在兩個正在執行的程序之間共享和傳遞資料的一種非常有效的方式。不同程序之間共享的記憶體通常安排為同一段實體記憶體。程序可以將同一段共享記憶體連線到它們自己的地址空間中,所有程序都可以訪問共享記憶體中的地址,就好像它們是由用C語言函式malloc分配的記憶體一樣。如果某個程序向共享記憶體寫入資料,所做的改動將立即影響到可以訪問同一段共享記憶體的任何其他程序。

 

特別提醒:

共享記憶體並未提供同步機制,也就是說,在第一個程序結束對共享記憶體的寫操作之前,並無自動機制可以阻止第二個程序開始對它進行讀取。所以我們通常需要用其他的機制來同步對共享記憶體的訪問,如訊號量。

 

二、共享記憶體的使用,在Linux中提供了一組函式介面用於使用共享記憶體。

1、共享記憶體的建立函式

函式原型int shmget(key_t key,size_t size,int shmflg);

 

引數

(1)key:與訊號量的semget函式一樣,程式需要提供一個引數(非0整數),有效的為共享記憶體段名,返回一個與key相關的共享記憶體識別符號(非負整數),用於後續的共享記憶體函式。呼叫失敗返回-1。

 

不相關的程序可以通過該函式的返回值訪問同一共享記憶體,它代表程式可能要使用的某個資源,程式對所有共享記憶體的訪問都是間接的,程式先通過呼叫shmget函式並提供一個鍵,再由系統生成一個相應的共享記憶體識別符號(shmget函式的返回值),只有shmget函式才直接使用訊號量鍵,所有其他的訊號量函式使用由semget函式返回的訊號量識別符號。


(2)Size:以位元組為單位指定需要共享的記憶體容量。

(3)Shmflg:許可權標誌,它的作用與open函式的mode引數一樣,如果要想在key標識的共享記憶體不存在時,建立它的話,可以與IPC_CREAT做或操作。共享記憶體的許可權標誌與檔案的讀寫許可權一樣,舉例來說,0644,它表示允許一個程序建立的共享記憶體被記憶體建立者所擁有的程序向共享記憶體讀取和寫入資料,同時其他使用者建立的程序只能讀取共享記憶體。

 

2、啟動對該共享記憶體的訪問,並把共享記憶體連線到當前程序的地址空間。

函式原型:void *shmat(int shm_id,const void *shm_addr,int shmflg)

 

引數;

(1)shm_id :由shmget函式返回的共享記憶體標識,

(2)shm_addr:指定共享記憶體連線到當前程序中的地址位置,通常為空,表示讓系統選擇共享記憶體的地址,

(3)shm_flg:標誌位,通常為0。

呼叫成功返回指向共享記憶體第一個位元組的指標,失敗返回-1。

 

3、用於將共享記憶體從當前程序中分離。

函式原型:int shmdt(const void *shmaddr);

引數:為shmat函式返回的地址空間,呼叫成功返回0,失敗時返回-1.

 

4、控制共享記憶體函式

函式原型:int shmctl(int shm_id,int command,struct shmid_ds *buf)

 

引數

(1)shm_id:共享記憶體識別符號

(2)Command:採取的操作:

IPC_STAT:把shmid_ds結構中的資料設定為共享記憶體的當前關聯值,即用共享記憶體的當前關聯值覆蓋shmid_ds的值。

IPC_SET:如果程序有足夠的許可權,就把共享記憶體的當前關聯值設定為shmid_ds結構中給出的值

IPC_RMID:刪除共享記憶體段

 

(3)buf:結構指標,指向共享記憶體模式和訪問許可權的結構。

 shmid_ds結構如下:

wKioL1eXkPrC-X3KAABrM43-su4856.png-wh_50

測試程式碼:

    shm.h

wKiom1eXkPrwnwlpAAA8iyABe6I062.png-wh_50

    shm.c

wKioL1eXkPqh5ZafAABX5YXYabk427.png-wh_50

wKioL1eXkPuh3xIsAAAoS0PXhUc131.png-wh_50

    test.c

wKiom1eXkPvCh9t6AABCZ6xTKdY190.png-wh_50

wKioL1eXkPvj80qvAAApIqyfG-k977.png-wh_50


共享記憶體mmap

1、特點:

1程序相關的

(2)與XSI共享記憶體一樣,需要與同步原語一起使用

(3)只能是有共同祖先的程序才能使用

 

2、系統呼叫mmap()用於共享記憶體的方式:

(1)使用普通檔案提供的記憶體映像:

適用於任何程序之間。此時,需要開啟或建立一個檔案,然後再呼叫mmap()

典型呼叫程式碼如下:

fd=open(name, flag, mode); if(fd<0) ...

ptr=mmap(NULL, len , PROT_READ|PROT_WRITE, MAP_SHARED , fd , 0);

(2)使用特殊檔案提供匿名記憶體映像:

適用於具有親緣關係的程序之間:

適用於具有親緣關係的程序之間。由於父子程序特殊的親緣關係,在父程序中先呼叫mmap(),然後呼叫fork()。那麼在呼叫fork()之後,子程序繼承父程序匿名對映後的地址空間,同樣也繼承mmap()返回的地址,這樣,父子程序就可以通過對映區域進行通訊了。一般來說,子程序單獨維護從父程序繼承下來的一些變數。而mmap()返回的地址,卻由父子程序共同維護。對於具有親緣關係的程序實現共享記憶體最好的方式應該是採用匿名記憶體對映的方式

 

函式原型:void *mmap(void *addr,size_t len,int prot,int flag,int fd,off_t offset)

 

引數

1、addr:對映區的開始地址,設定為0時表示系統決定對映區的起始地址

2、len:對映區的長度,單位為位元組

3、prot:期望的記憶體保護標誌,取一下幾個值:

PORT_EXEC:頁內容可以被執行  PROT_READ:頁內容可被讀

PROT_WRITE:頁內容可被寫     PROT_NONE:頁內容不可訪問

4、fd:檔案描述符。

5、offset:被對映物件內容的起點。

6、Flags(必須要有MAP_CHARED標誌):指定對映物件的型別,對映選項是否可以和對映頁共存。

MAP_SHARED:與其他所有對映這個物件的程序共享對映空間;

MAP_PRIVATE:建立一個寫入時拷貝的私有對映。記憶體區域的寫入不會影響到原檔案。

MAP_FIXED:使用指定的對映起始地址

測試程式碼:

mmap.c

wKiom1eXkgbg41VBAABNRS3WFWQ495.png-wh_50

wKiom1eXkgayKSpfAAAqc3yxplE056.png-wh_50

wKioL1eXkgahAa1vAAAPoLtcDtg055.png-wh_50

執行結果:

wKioL1eXkgXBl5-8AAAGKGUVwY4592.png-wh_50