共享記憶體
共享記憶體是Linux下倆程序通信的一種方式,
主要功能是讓兩個程序的虛拟位址都映射到同一片實體位址上,就可以通過這片實體位址進行資料的互動
常用實作方法
mmap()
mmap函數要求核心建立一個新的虛拟記憶體區域,最好是從位址start開始的一個區域,并将檔案描述符df指定的對象的一個連續的片映射到這個新的區域,連續的對象片大小為length位元組,從距檔案開始處偏移量為offser位元組的地方開始.start位址僅僅是個表示,通常定義為NULL]
mamap()實際上是把磁盤上的檔案映射在記憶體上,那麼對檔案的寫入讀出就可以不用IO函數 read(),write()而直接可以在記憶體上進行操作
如果多個程序映射同一個檔案到自己的虛拟位址上,那麼他們就可以同時通路這一片實體記憶體
就實作了利用mmap()函數完成多個程序的通信
其中由于是把檔案映射入虛拟位址空間,第一次進行IO操作肯定會觸發缺頁異常,會把磁盤檔案載入實體記憶體中然後通過頁表建立映射關系
還有如何将多個程序的虛拟位址空間映射在同一實體空間,詳見 https://www.ibm.com/developerworks/cn/linux/l-ipc/part5/index1.html
總結來講mmap()的主要作用是将磁盤檔案載入記憶體中
由于其上述的功能,可以實作程序共享一段實體記憶體空間,是以可以實作程序間的通信
System v的系統調用
與mmap()不同,以下函數是專門進行共享記憶體通訊的函數
shmget()
功能:用來建立共享記憶體
原型:
int shmget(key_t key, size,size_t size,int shemfg)
key:通過ftok函數生成唯一的辨別碼
size:共享記憶體的大小
shmflg:類似檔案建立的權限
傳回值:成功傳回shmid,即該段記憶體的唯一辨別碼
此函數可以說是在實體記憶體上開辟了一段空間,并傳回了該空間的唯一辨別碼
要想使用該段空間,那麼就要将某個程序的虛拟位址空間通過頁表映射到這段實體記憶體上,會用到的函數就是shmat
shmat
原形:
#include <sys/types.h>
#include <sys/shm.h>
void *shmat(int shmid, const void *shmaddr, int shmflg);
說明:
将一片實體空間編号為shmid的記憶體通過頁表映射到該程序的以void* shmaddr為首的連續空間内,如果該參數為NULL作業系統自動配置設定該程序接受位址
shmflg取值可能為SHM_RND和SHM_RDONLY
詳情參考man手冊
shmdt
int shmdt(const void *shmaddr);
shmaddr是通過shmat函數傳回的位址
函數是将該程序與共享記憶體段解除關系
注意 不是删除共享記憶體段
shmctl
#include <sys/ipc.h>
#include <sys/shm.h>
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
此函數是共享記憶體的控制函數
通常與Sytem v程序通信其他的形式,比如消息隊列的接口風格保持一緻,不做過多詳細介紹.
這裡隻是使用此函數删除共享記憶體
cmd即是 IPC_RMID 最後一個參數不關心 設定為NULL即可
一個例子
一個程序向共享記憶體區域寫入a ab abc ….
另一個程序從共享記憶體中讀出 a ab abc ….
comm.h
#ifndef __COMM_H__
#include<stdio.h>
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/shm.h>
#define PATHNAME "/usr/"
#define PROJ_ID 0x666
//通過flags确定是create還是get
//函數給出大小就行了
//傳回值是commShm函數裡的shmid
//重視封裝
int createShm(int size);
int getShm(int size);
int destroyShm(int shmid);
//sharedMemoryAttach函數不用封裝
#endif
comm.c
#include"comm.h"
static int commShm(size_t size,int flags)
{
key_t key = ftok(PATHNAME,PROJ_ID);
if(key < )
{
perror("fork");
return -;
}
int shmid = shmget(key,size,flags);
if(shmid < )
{
perror("shmid");
return -;
}
return shmid;
}
int createShm(int size)
{
return commShm(size,IPC_CREAT|IPC_EXCL|);
}
int getShm(int size)
{
return commShm(size,IPC_CREAT);
}
int destroyShm(int shmid)
{
if(shmctl(shmid,IPC_RMID,NULL) < )
{
perror("shmid");
return -;
}
}
server.c
#include"comm.h"
int main()
{
int shmid = createShm();
//
char* addr = shmat(shmid,NULL,);
sleep();
int i = ;
while(i++ < )
{
printf("%s\r",addr);
fflush(stdout);
sleep();
}
shmdt(addr);
destroyShm(shmid);
sleep();
return ;
}
client.c
#include"comm.h"
int main()
{
int shmid = getShm();
//
char* addr = shmat(shmid,NULL,);
int i = ;
sleep();
while(i < )
{
addr[i] = 'a' + i;
i++;
//a0 ab0
addr[i] = ;
sleep();
}
shmdt(addr);
sleep();
return ;
}
共享記憶體的一些總結
首先,共享記憶體是所有ipc通信中最快的一種,因為當程序和一段記憶體映射後,通路該記憶體就可以在使用者态進行,而不需要傳回核心态通過作業系統完成,相反,比如管道和消息隊列都是作業系統維護的
其次,共享記憶體雖然是臨界資源,但是在這片記憶體不會有任何限制,是以不提供管道和消息隊列的同步與互斥的概念,是以如果不加限制,這片記憶體中的資料是不安全的
我們可以通過加上信号量等方法對臨界區的資源進行限制