一、什麼是共享內存
顧名思義,共享內存就是允許兩個不相關的進程訪問同一個邏輯內存。共享內存是在兩個正在運行的進程之間共享和傳遞數據的一種非常有效的方式。不同進程之間共享的內存通常安排爲同一段物理內存。進程可以將同一段共享內存連接到它們自己的地址空間中,所有進程都可以訪問共享內存中的地址,就好像它們是由用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結構如下:
測試代碼:
shm.h
shm.c
test.c
共享內存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
運行結果: