一、什麼是共享內存
共享內存指 (shared memory)在多處理器的計算機系統中,可以被不同中央處理器(CPU)訪問的大容量內存。由於多個CPU需要快速訪問存儲器,這樣就要對存儲器進行緩存(Cache)。任何一個緩存的數據被更新後,由於其他處理器也可能要存取,共享內存就需要立即更新,否則不同的處理器可能用到不同的數據。共享內存是 Linux下的多進程之間的通信方法 ,這種方法通常用於一個程序的多進程間通信,實際上多個程序間也可以通過共享內存來傳遞信息。
二、共享內存的原理
三、共享內存的特點
在匿名管道、命名管道、消息隊列、信號量、共享內存這五種進程間通信的方式中,前兩種生命週期隨進程,後三種生命週期隨內核。而共享內存是這五種方式中效率最高的。雖然共享內存提供了進程間通信的方式,但是他沒有相應的互斥機制,所以一般共享內存和信號量配合起來使用。
四、共享內存的實現原理
在實現之前,我們先來看幾個共享內存的函數
1、shmget獲得或者創建一個共享內存
int shmget(key_t key,size_t size,int flags);
返回值:失敗返回-1,成功返回共享內存的id。
size:表示要申請的共享內存的大小,一般是4k的整數倍。
flags:IPC_CREAT和IPC_EXCL一起使用,沒有就創建一個新的關係內存,否則返回-1。IPC_CREAT單獨使用時返回一個共享內存,有就直接返回,沒有就創建。
2、shmat將申請的共享內存掛接在該進程的頁表上,是的虛擬內存和物理內存向對應。
void *shmat(int shmid);
返回值:返回這塊內存的虛擬地址。
3、shmdt去掛接,將這塊共享內存從頁表上剝離下來。
int shmdt(const void*shmaddr);
返回值:失敗返回-1.
shmaddr:表示這塊物理內存的虛擬地址。
4、shmctl用來設置共享內存的屬性。當cmd是IPC_RMID時可以用來刪除一塊共享內存。
int shmctl(int shmid,int cmd,const void* addr);
瞭解這幾個函數之後,我們就可以來完成共享內存的實現了
/*Makefile*/
/*************************************/
.PHONY:all
all:server client
server:server.c comm.c
gcc -o $@ $^
client:client.c comm.c
gcc -o $@ $^
.PHONY:clean
clean:
rm -f server client
/*************************************/
/*comm.h*/
/*************************************/
/*************************************************************************
> File Name: comm.c
> Author:Lord'WingF'
> Mail:暫無
> Created Time: Sun 04 Jun 2017 02:08:23 AM PDT
************************************************************************/
#ifndef __COMM_H__
#define __COMM_H__
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/wait.h>
#include<sys/shm.h>
#include<sys/ipc.h>
#define PATHNAME "."
#define PROJ_ID 7777
#define SIZE 4096*1
int CreateShm();
int GetShm();
char *AtShm(int shmid);
int DtShm(const void* shmadd);
int Destoryshm(int shmid);
#endif
/*************************************/
/*comm.c*/
/*************************************/
/*************************************************************************
> File Name: comm.c
> Author:Lord'WingF'
> Mail:暫無
> Created Time: Sun 04 Jun 2017 02:08:23 AM PDT
************************************************************************/
#include"comm.h"
/*****************************************/
int CommShm(int flags)
{
key_t _key = ftok(PATHNAME, PROJ_ID);
if(_key < 0){
perror("ftok");
return -1;
}
int shmid = shmget(_key, SIZE, flags);
if(shmid < 0){
perror("shmget");
return -2;
}
return shmid;
}
/*****************************************/
/*****************************************/
int CreateShm()
{
return CommShm(IPC_CREAT | IPC_EXCL | 0666);
}
/*****************************************/
/*****************************************/
int GetShm()
{
return CommShm(IPC_CREAT);
}
/*****************************************/
/*****************************************/
char *AtShm(int shmid)
{
char* addr=shmat(shmid,NULL,0);
return (char*)addr;
}
/*****************************************/
/*****************************************/
int DtShm(const void* shmaddr)
{
return shmdt(shmaddr);
}
/*****************************************/
/*****************************************/
int DestoryShm(int shmid)
{
if(shmctl(shmid,IPC_RMID,NULL)<0)
{
perror("shmctl");
return -1;
}
return 0;
}
/******************************************/
/*************************************/
/*server.c*/
/*************************************/
/*************************************************************************
> File Name: server.c
> Author:LordWingF
> Mail: NULL
> Created Time: Sun 11 Jun 2017 02:10:42 AM PDT
************************************************************************/
#include"comm.h"
int main()
{
int shmid=CreateShm();
char* buf=(char*)AtShm(shmid);
int i=0;
while(i<(SIZE-1))
{
buf[i]='A';
i++;
}
buf[SIZE-1]='\0';
DtShm(buf);
DestoryShm(shmid);
return 0;
}
/**************************************/
/*client.c*/
/**************************************/
/*************************************************************************
> File Name: client.c
> Author:LordWingF
> Mail: NULL
> Created Time: Sun 11 Jun 2017 02:10:53 AM PDT
************************************************************************/
#include"comm.h"
int main()
{
int shmid=GetShm();
char* buf=(char*)AtShm(shmid);
printf("%s\n",buf);
DtShm(buf);
return 0;
}
/**************************************/
程序結果如下
調用ipcs -m來查看創建的共享內存
爲了確保能夠一直使用我們調用ipcrm -m +shmid來釋放這個共享內存
這就是共享內存間內存的簡單通訊
五、共享內存效率高的原因
共享內存區是最快的可用IPC形式,一旦這樣的內存區映射到共享它的進程的地址空間,這些進程間數據的傳遞就不再通過執行任何進入內核的系統調用來傳遞彼此的數據,節省了時間。
共享內存和消息隊列,FIFO,管道傳遞消息的區別:
後者,消息隊列,FIFO,管道的消息傳遞方式一般爲
1:服務器得到輸入
2:通過管道,消息隊列寫入數據,通常需要從進程拷貝到內核。
3:客戶從內核拷貝到進程
4:然後再從進程中拷貝到輸出文件
上述過程通常要經過4次拷貝,才能完成文件的傳遞。
而共享內存只需要
1:從輸入文件到共享內存區
2:從共享內存區輸出到文件
上述過程不涉及到內核的拷貝,所以花的時間較少