文章目錄
- 一、共享內存的原理
- 詳談共享內存的實現過程
- 二、共享內存的接口函數
- 1.shmget
- 2. shmat
- shmdt
- shmctl
- 進程間使用共享內存通信
- 三、共享內存的特性
- 關于代碼
一、共享內存的原理
共享內存是由操作系統維護和管理的一塊內存。
共享內存的本質是內核級的緩沖區。
一個進程向操作系統申請一塊共享區內存,操作系統為該進程創建了一塊內存后,進程要將該共享內存與自己的虛擬地址空間進行映射掛接。
也就是將共享區內存通過頁表建立映射關系后,在進程自己的虛擬地址空間的共享區中就保留了共享內存的起始地址。
同時,進程b也通過頁表映射,將共享區的起始地址映射到自己的虛擬地址空間中,兩個進程就能看到同一份資源,從而能實現通信!!!
那為什么要個操作系統申請內存,而不給進程自己管理呢?
因為操作系統要對各種共享內存進行先描述,再組織
的工作。
所以,共享內存一定有對應的描述該共享內存的對象,保存共享內存及其周邊的各種屬性和信息。
操作系統對這些對象進行管理的過程,本質轉化成對鏈表的增刪查改。
詳談共享內存的實現過程
二、共享內存的接口函數
1.shmget
shmget - allocates a System V shared memory segment
int shmget(key_t key, size_t size, int shmflg);
該接口就是向內存申請一塊共享內存。
參數2:size
該參數就是申請的共享內存塊的大小。
注意:一般申請的共享內存是4096字節(4KB)的整數倍。
如果申請的是4097字節,操作系統會給一塊4096*2字節大小的共享內存,但是能夠使用的只有4097字節,剩下的空間給了也不能用。
參數3:shmflg
這個參數類似于open函數的第三個參數,打開的方式:O_CREAT|O_WRONLY
,shmflg參數的底層也是使用位圖實現的。
重點是這兩個宏定義
- 1.IPC_CREAT單獨使用時,如果不存在,就創建并返回,如果存在,就獲取并返回。
- 2.IPC_CREAT|IPC_EXCL一起使用時,如果不存在,就創建并返回,如果存在,則出錯返回。
- 3.IPC_EXCL不單獨使用
第二點讓人奇怪,解釋如下:
IPC_CREAT|IPC_EXCL
能保證如果能申請到,那么申請到的共享內存是最新的!
參數3:key
key是一個唯一標識符,也就是說每個共享內存都有唯一的key。
ftok - convert a pathname and a project identifier to a System V IPC key
SYNOPSIS
#include <sys/types.h>
#include <sys/ipc.h>
key_t ftok(const char *pathname, int proj_id);
用戶通過傳遞一個路徑名和一個id,返回一個共享內存的唯一標識符。
所以ftok函數的本質就是一個算法。
pathname和proj_id是用戶自己控制的。
為什么不讓操作系統隨機生成呢?
因為操作系統隨機生成的key并不能傳遞給另一個進程,從而讓不同的進程看到同一份資源這個目的。
所以必須讓用戶傳參下來。
key_t key = ftok(pathname.c_str(),proj_id); // 成功返回key,失敗返回-1
返回值:
如果共享內存申請成功,返回的是shmid,其實這個返回值就像是文件fd,創建一個文件,返回該文件在文件數組fd_array中的下標。申請失敗返回-1.
key_t key = ftok(PATH_NAME,proj_id);flag = IPC_CREATE|IPC_EXCL|0666;int shmid = shmget(key,size,flag); // 申請成功返回id,失敗返回-1
所以可見,共享內存的確是由操作系統管理起來的。
所以,共享內存的生命周期是隨操作系統的,進程退出共享內存并不會釋放。除非內核重啟,否則共享內存是不會釋放的。
對比shmid和key:
shmid是共享內存在數組中的下標,只在進程內,用于標識資源的唯一性。
而key是內核級標定共享內存唯一性的。
共享內存的權限問題:
共享內存的權限,可以直接在shmget函數的第二個參數中傳遞。
如何保證讓不同的進程看到同一份內存呢?
2. shmat
該函數是將指定進程與共享內存進行掛接。
第一個參數就是共享內存的id,第二個參數暫時不用管,設置為nullptr即可,第三個參數同樣暫時不管,設置成0.
// 2. 將服務端與共享內存掛接起來
char *shmaddr = (char *)shmat(shmid, nullptr, 0);
// 返回掛接的虛擬地址的起始地址
shmdt
將掛接時獲取的地址傳過去,取消掛接即可,成功返回0,失敗返回-1.
shmctl
就是將共享內存刪除。
參數1傳對應的共享內存,參數2傳IPC_RMID,參數3先不管,穿nullptr;
參數2的命令如下,就是標記對應的共享內存為刪除狀態。
進程間使用共享內存通信
假設進程A申請共享內存。
對進程A來說:
- 1.進程A先調用shmget函數,創建共享內存。
- 2.進程A與對應的共享內存掛接起來。
- 3.通信完成后取消掛接。
- 4.再將共享內存釋放。
對進程B來說:
1.進程B先調用shmget函數,獲取共享內存。
2.進程B與對應的共享內存掛接起來。
3.通信完成后取消掛接。
三、共享內存的特性
1.共享內存沒有同步互斥之類的保護機制
2.共享內存是所有進程間通信中,速度最快的!(拷貝少)
進程想向內存中寫入數據,直接向對應的共享內存進行寫入即可,只需要將用戶層緩沖區拷貝到內存中即可。只需要一次拷貝。
3.共享內存內部的數據,由用戶自己維護!!
關于代碼
代碼地址請移步:gitee——共享內存