linux进程间通信:system V 共享内存
文章目錄
- 思維導圖如下
 - 通信原理
 - 優勢
 - 運行流程
 - 編程接口
 - 編程實例
 
思維導圖如下
通信原理
- 多個進程共享物理內存的同一塊區域(通常稱之為“段”:segment)
 - 拋棄了內核態消息轉存處理的過程,讓兩個進程直接通過一塊內存進行通信
 
我們普通的像PIPE,FIFO,消息隊列等的通信方式如下圖:
 
 這種方式的通信不論讀寫,都需要內核態(系統調用 read,write,pipe,mkfifo,msgget,msgsnd,msgrcv等)的介入,而且都需要經過數據從虛擬地址空間到物理地址空間的拷貝。
 而共享內存的通信方式則都避免了以上的通信問題,直接為兩個進程開辟相同的內存空間進行數據交互。
 
優勢
- 減少了內存的拷貝(從用戶拷貝到內核,從內核拷貝到用戶)
 - 減少了2次系統調用(系統調用比較消耗性能,因為CPU處理系統調用時需要從用戶態切換到內核態),提高了系統性能
 
運行流程
- 獲取共享內存對象的ID
 - 將共享內存映射至本進程虛擬內存空間的某個區域(每個用戶進程操作系統都有3G的虛擬進程空間)
 - 不同進程對這塊內存進行讀寫、傳輸數據
 - 當進程不再使用這塊共享內存時,解除映射關系
 - 當沒有進程需要共享內存的時候,則刪除該共享內存
 
編程接口
-  
獲取共享內存的對象ID ,創建或打開一個共享內存對象
a. 頭文件<sys/types.h> <sys/shm.h>
b.int shmget (key_t key, size_t size, int shmflg);
c. 函數參數keyIPC對象的鍵值,一般為IPC_PRIVATE 或者ftok返回的key值size共享內存的大小,一般為物理內存頁(4K)的整數倍shmflg:
IPC_CREAT :如果不存在指定的key值,那么就創建一個
IPC_EXCL: 若key值指定的內存存在,且指定了IPC_CEAT,則回復EXIST錯誤
IPC_HUGETLB 使用巨頁(huge page)
d. 返回值:共享內存的標識ID
 -  
映射共享內存:
a. 頭文件<sys/shm.h>
b.void *shmat(int shmid,const void *shmaddr,int shmflg);
c. 功能:將shmid標識的共享內存引入到當前進程的虛擬地址空間
d. 函數參數:shmid共享內存的IPC對象IDshmaddr
若為NULL:共享內存會被attach到一個合適的虛擬地址空間,建議使用NULL
不為NULL:系統會根據參數以及地址邊界對齊等分配一個合適的地址shmflg
IPC_RDONLY:附加只讀權限,不指定的話返回默認是讀寫權限
IPC_REMAP:替換位于shmaddr處的任意既有映射(共享內存的段或者內存映射)
SHM_RND:將shmaddr四舍五入為SHMMLBA字節的倍數
e. 返回值:共享內存段的地址
 -  
解除共享內存映射
a. 頭文件<sys/shm.h>
b.int shmdt(const void * shmaddr);
c. 功能:解除內粗映射,將共享內存分離出當前進程的地址空間
d. 函數參數:shmaddr共享內存地址
e. TIPS:
- 通過fork創建的子進程會繼承父進程所附加的共享內存,父子進程可以通過共享內存進行IPC通信,在exec系統調用中,所有附加的共享內存段都會被分離
 - 函數
shmdt僅僅是使進程和共享內存脫離關系,將共享內存的引用計數是減1 - 當共享內存的引用計數為0的時候,調用
shmctl的IPC_RMID命令才會刪除共享內存 
 -  
設置共享內存屬性
a. 頭文件<sys/shm.h>
b.int shmctl(int shmid,int cmd,struct shmid_ds *buf);
c. 函數功能:獲取/設置共享內存對象屬性
d. 函數參數
-shmid共享內存對象的ID
-cmd:
IPC_RMID :刪除共享內存及關聯的shmid_ds數據結構
IPC_STAT: 將該共享內存關聯的shmid_ds數據結構拷貝到參數buf中
IPC_SET: 使用buf中的數據更新與該共享內存對象相關聯的shmid_ds
IPC_INFO:獲取系統共享內存相關的信息
IPC_LOCK: 將一個共享內存段鎖進內存,防止被swap出去
IPC_UNLOCK: 將一個共享內存解鎖 -  
shmid_ds *buf數據結構如下struct shmid_ds {struct ipc_perm shm_perm; /* operation permissions */int shm_segsz; /* size of segment in bytes */pid_t shm_lpid; /* pid of last shm op */pid_t shm_cpid; /* pid of creator */short shm_nattch; /* # of current attaches */time_t shm_atime; /* last shmat() time*/time_t shm_dtime; /* last shmdt() time */time_t shm_ctime; /* last change by shmctl() */void *shm_internal; /* sysv stupidity */}; 
編程實例
寫端shm_write.c:
#include<stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <strings.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>int main()
{//key_t key = ftok("./",111);key_t key = 12345;//根據key值生成共享內存對象的唯一標識int shm_id = shmget(key,4096,IPC_CREAT|0666);//將當前進程加入共享內存,這里設置共享內存地址為NULL時//系統會自動為當前進程分配一個合適的內存空間,并返回共享內存的地址char *shm_p = shmat(shm_id , NULL, 0);memset (shm_p,0,4096);//從標準輸入獲取內容到共享內存的地址fgets(shm_p,4096,stdin);//等待輸入完成,刪除共享內存sleep(10);//當前進程創建的共享內存引用計數為0的時候,刪除共享內存shmctl(shm_id,IPC_RMID,NULL);return 0;
} 
讀端shm_read.c
#include<stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <strings.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>int main() {
//    key_t key = ftok("./",111);key_t key = 12345;int shm_id = shmget(key, 4096, 0666);//和寫端訪問到了相同的共享內存區域,并獲取內容存放到指針變量char *shm_p = shmat(shm_id , NULL, 0);printf("From SHM: %s\n",shm_p);//刪除當前進程與共享內存的映射關系,同時共享內存的引用計數減一shmdt(shm_p);return 0;
}
 
通過命令ipcs -m可以查看系統共享內存
 
 需要先執行寫入,再去運行讀端讀出;以上輸出如下:
 
總結
以上是生活随笔為你收集整理的linux进程间通信:system V 共享内存的全部內容,希望文章能夠幫你解決所遇到的問題。
                            
                        - 上一篇: 牛肉加盟多少钱啊?
 - 下一篇: linux进程间通信:system V