概念
共享內存(Shared Memory),指兩個或多個進程共享一個給定的存儲區。
特點
共享內存是最快的一種 IPC,因為進程是直接對內存進行存取。
因為多個進程可以同時操作,所以需要進行同步。
信號量+共享內存通常結合在一起使用,信號量用來同步對共享內存的訪問。
原理
創建
1、創建共享內存
2、進程A連接共享內存,寫入數據(這里需要給進程A一個睡眠時間:兩個進程同時操作需要同步,進程A寫入數據后睡眠一定時間,在這個時間內進程B將數據讀取,實現數據交換)
3、進程A斷開連接
4、進程B連接共享內存,讀取數據
5、進程B斷開連接
6、釋放公共內存
常用API
頭文件
#include <sys/types.h>
#include <sys/shm.h>
//以下幾個API都包含以上兩個頭文件
shmget函數
功能
創建或獲取一個共享內存
函數原型
int shmget(key_t key, size_t size, int shmflg);
參數解讀
key | 由ftok生成的key標識,標識系統的唯一IPC資源 |
size | 需要申請共享內存的大小。在操作系統中,申請內存的最小單位為頁,一頁是4k字節,為了避免內存碎片,我們一般申請的內存大小為頁的整數倍,即以兆為單位 |
shmflg | 如果要創建新的共享內存,需要使用IPC_CREAT,IPC_EXCL,后面需要加權限標志,權限標志與文件的讀取操作一樣。如果是已經存在的,可以使用IPC_CREAT或直接傳0(只需獲取而不用創建,yi) |
返回值
成功時返回一個新建或已經存在的的共享內存標識符,取決于shmflg的參數。失敗返回-1并設置錯誤碼。
shmat函數
功能
第一次創建完共享內存時,它還不能被任何進程訪問,shmat函數的作用就是用來啟動對該共享內存的訪問,并把共享內存連接到當前進程的地址空間,即將共享內存映射進進程中。
函數原型
void *shmat(int shm_id, const void *shm_addr, int shmflg);
參數解讀
shm_id | 由shmget函數返回的共享內存標識 |
*shm_addr | 指定共享內存連接到當前進程中的地址位置,通常為空(為0),表示讓系統為我們安排共享內存的地址 |
shmflg | 若指定了SHM RDONLY位,則以只讀方式連接此段,否則以讀寫方式連接此段(輸入0即可,表示映射進的共享內存可讀可寫) |
返回值
成功返回共享存段的指針(虛擬地址),并且內核將使其與該共享存段相關的shmid_ds(第四個函數的第三個參數)結構中的shm_nattch計數器加1 (類似于引用計數,即內存占用計入總內存) ; 出錯返回-1。
shmdt函數
功能
當一個進程不需要共享內存的時候,就需要去關聯。該函數并不刪除所指定的共享內存區,而是將之前用shmat函數連接好的共享內存區脫離目前的進程。
函數原型
int shmdt(const void *shmaddr);?
參數解讀
*shmaddr:是shmat函數返回的地址指針,只需將其返回值的函數變量名(代碼地址)寫入即可
返回值
調用成功時返回0,失敗時返回-1。
shmctl函數
功能
控制共享內存
函數原型
int shmctl(int shm_id, int command, struct shmid_ds *buf);
參數解讀
shm_id | shmget函數返回的共享內存標識符 | ||||||
command | command是要采取的操作,它可以取下面的三個值
| ||||||
*buf | buf是一個結構指針,它指向共享內存模式和訪問權限的結構(不關心,一般寫0) |
返回值
成功時返回0,失敗返回-1并設置錯誤碼。
代碼示例
shmw.c
#include <stdio.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <stdlib.h>
#include <string.h>int main()
{key_t key;int shmid;char *shmaddr = NULL; key = ftok(".",1);//當前目錄建立IPCshmid = shmget(key,1024*4,IPC_CREAT|0666);//以可讀可寫方式開辟一個4兆大小的共享內存if(shmid == -1){printf("create gxdl failed\n");exit(-1);}shmaddr = shmat(shmid,0,0);//將共享內存映射到進程中printf("connect success!\n");strcpy(shmaddr,"hello word!");sleep(5);//因為寫入和讀取是同步,所以此時執行完寫入代碼后需等待讀取數據后一起關閉共享內存shmdt(shmaddr);//斷開連接共享內存shmctl(shmid,IPC_RMID,0);//刪除共享內存printf("over\n");return 0;
}
shmr.c
#include <stdio.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <stdlib.h>
#include <string.h>int main()
{key_t key;int shmid;char *shmaddr = NULL; key = ftok(".",1);shmid = shmget(key,1024*4,0);//這里只需找到已經開辟的共享內存,所以參數為0即可if(shmid == -1){printf("create gxdl failed\n");exit(-1);}shmaddr = shmat(shmid,0,0);//映射共享內存printf("connect success!\n");printf("content is %s\n",shmaddr);//將共享內存數據打印出來shmdt(shmaddr);//斷開共享內存printf("over\n");return 0;
}
shmw.c程序運行,寫入端往共享內存寫入數據,并讓其等待5s,這5s內shmr.c程序運行,讀取端讀取共享內存數據并將內容打印出來,5s后兩者同時關閉共享內存輸出over。
fork函數的補充
功能
系統IPC鍵值的格式轉換函數,系統建立IPC通訊 (消息隊列、信號量和共享內存) 時必須指定一個ID值。通常情況下,該id值通過ftok函數得到。
函數原型
key_t ftok( const char * fname, int id );
參數解讀
fname | 就是你指定的文件名(已經存在的文件名),一般使用當前目錄 |
id | 子序號。雖然是int類型,但是只使用8bits(1-255) |
例如
key_t key;
key = ftok(".", 1); //當前文件只需加.