前言
進程間IPC通信的三大主題,消息隊列,信號量,共享內存,我們接下來說一說共享內存,共享內存可以提供給服務器進程和客戶進程之間進行通信,不需要進行數據的複製,所以速度最快,只需要讓兩個進程通過頁表映射到同一塊物理內存即可,這樣,這塊物理內存是兩個進程都能看到的,這樣當一個進程進行寫操作,另外的一個進程也就可以做讀操作。所以問題關鍵也就是給出一個特定的存儲區。通常情況下,我們需要確保一個進程在寫的時候,另外一個進程不能去讀,所以我們可以使用信號量進行共享內存訪問。
創建共享內存
首先我們利用ftok函數生成key標識。
key_t ftok(const char *pathname, int proj_id);
然後我們使用shmget函數,這個函數可以進行創建共享內存。
int shmget(key_t key, size_t size, int shmflg);
在這你要看好,這個size是需要進行申請的共享內存的大小,需要注意的是,操作系統爲你提供的時候是按照頁來提供的,因爲是按照頁提供的,所以size爲4k的整數倍,然後shmflg和信號量以及消息隊列的類似,如果要創建新的共享內存,那麼就是使用IPC_CREATE,IPC_EXCL。如果是已存在的,那麼只需要使用IPC_CREATE或者傳0。
掛接共享內存
創建好共享的內存以後,這個時候我們就應該考慮的是讓進程去掛接共享內存。
void *shmat(int shmid, const void *shmaddr, int shmflg);
掛接共享內存,shmflg一般表示什麼方式進行掛接,一般都是取0,shmid是掛接的進程號。這個函數返回的是一個地址,我們可以想一下使用malloc的時候如何使用的,這個和那個類似。
去關聯共享內存
當一個進程不需要共享內存的時候,這個時候就需要去關聯,
int shmdt(const void *shmaddr);
這個參數就是我們掛接以後返回的地址。
銷燬共享內存
我們可以使用函數銷燬共享內存,
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
上面的這個函數用來控制共享內存的操作,我們可以用它來刪除共享內存,當cmd取IPC_RMID的時候就可以刪除共享內存,另外我們也可以使用命令來進行操作ipcrm -m xxx
這樣也可以進行銷燬共享內存。
注意點
共享內存並沒有提供互斥和同步機制,所以一般都是我們自己來做這個,可以使用信號量保證互斥,共享內存一般來提供對大塊內存區域的訪問,因爲共享內存不想IPC另外兩個一樣需要拷貝和搬遷,所以共享內存是最快的,所以使用也比較多。
對於一個共享內存,實現也應用了引用計數的原理,當進程脫離了共享內存區之後,計數器會減一,只有當計數器變爲0的時候,就是沒有任何進程來使用這個共享內存區,這個時候共享內存纔會被刪除,當一個進程終止的時候,它所附加的共享內存區都會自動脫離。
實例
下面的例子是服務器進程進行創建共享共享內存,然後掛接,客戶進程是得到關聯共享內存,然後客戶進程一秒發一個a,這個時候服務器進程接受到,打印,從而實現共享內存。
comm.h
#ifndef __COMM_H_
#define __COMM_H_
#include<stdio.h>
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/shm.h>
#define PATHNAME "."
#define PROJID 0x6666
#define SIZE 4096*1
int Comm_Shm(int flags);
int Create_Shm();
int Get_Shm();
int Destory_Shm(int shmid);
char *At_Shm(int shmid);
int Dt_Shm(char *mem_addr);
#endif //!__COMM_H_
comm.c
#include"comm.h"
int Comm_Shm(int flags)
{
key_t shmkey=ftok(PATHNAME,PROJID);
if(shmkey<0)
{
perror("ftok");
return -1;
}
printf("sda");
int shmid=shmget(shmkey,SIZE,flags);
if(shmget<0)
{
perror("shmget");
return -2;
}
printf("create");
return shmid;
}
int Create_Shm()
{
return Comm_Shm(IPC_CREAT|IPC_EXCL|0666);
}
int Get_Shm()
{
return Comm_Shm(0);
}
int Destory_Shm(int shmid)
{
int ret=shmctl(shmid,IPC_RMID,NULL);
if(ret<0)
{
perror("shmctl");
return -1;
}
return 0;
}
char* At_Shm(int shmid)
{
char *shm=(char *)shmat(shmid,NULL,0);
return shm;
}
int Dt_Shm(char *mem_addr)
{
int ret =shmdt(mem_addr);
return 0;
}
client.c
#include"comm.h"
int main()
{
int shmid=Get_Shm();
char *add=At_Shm(shmid);
printf("physic address:%s",add);
int i=0;
while(1)
{
sleep(1);
add[i++] = 'a';
i %= (SIZE-1);
add[i]='\0';
}
Dt_Shm(add);
return 0;
}
server.c
#include"comm.h"
int main()
{
int shmid=Create_Shm();
sleep(5);
char *add=At_Shm(shmid);
while(1)
{
sleep(1);
printf("%s\n",add);
}
sleep(5);
Dt_Shm(add);
Destory_Shm(shmid);
return 0;
}