共享内存实际是操作系统在实际物理内存中开辟的一段内存。
共享内存实现进程间通信,是操作系统在实际物理内存开辟一块空间,一个进程在自己的页表中,将该空间和进程地址空间上的共享区的一块地址空间形成映射关系。另外一进程在页表上,将同一块物理空间和该进程地址空间上的共享区的一块地址空间形成映射关系。
当一个进程往该空间写入内容时,另外一进程访问该空间,会得到写入的值,即实现了进程间的通信。
注意:共享内存实现进程间通信是进程间通信最快的。(相比较管道共享内存不需要两次拷贝)
在系统当中可能会有大量的进程在进行通信,因此系统当中就可能存在大量的共享内存,那么操作系统必然要对其进行管理,所以共享内存除了在内存当中真正开辟空间之外,为了维护管理共享内存,系统一定要"描述"共享内存
/* Obsolete, used only for backwards compatibility and libc5 compiles */
struct shmid_ds {
struct ipc_perm shm_perm; /* operation perms */
int shm_segsz; /* size of segment (bytes) *///共享内存空间大小
__kernel_time_t shm_atime; /* last attach time *///挂接时间
__kernel_time_t shm_dtime; /* last detach time *///取消挂接时间
__kernel_time_t shm_ctime; /* last change time *///改变时间
__kernel_ipc_pid_t shm_cpid; /* pid of creator */
__kernel_ipc_pid_t shm_lpid; /* pid of last operator */
unsigned short shm_nattch; /* no. of current attaches *///进程挂接数
unsigned short shm_unused; /* compatibility */
void *shm_unused2; /* ditto - used by DIPC */
void *shm_unused3; /* unused */
};
描述共享内存的数据结构里保存了一个ipc_perm结构体,这个结构体保存了IPC(进程将通信)的关键信息。
/* Obsolete, used only for backwards compatibility and libc5 compiles */
struct ipc_perm
{
__kernel_key_t key;//共享内存的唯一标识符
__kernel_uid_t uid;
__kernel_gid_t gid;
__kernel_uid_t cuid;
__kernel_gid_t cgid;
__kernel_mode_t mode; //权限
unsigned short seq;
};
ftok中的参数可以随便填写,但是要符合格式,ftok只是利用参数,再运用一套算法,算出一个唯一的key值返回。这个key值可以传给共享内存参数,作为struct
ipc_perm中唯一标识共享内存的key。
#include
using namespace std;
#include
#include
#include
#define PATHNAME "/home/zuofangting"
#define PROJ_ID 0x888
#define SIZE 4096
key_t GetKey()
{
key_t ret=ftok(PATHNAME,PROJ_ID);
if(ret==-1) return -1;
cout<<"key:"<<ret<<endl;
return ret;
}
int GetShmget()
{
key_t key=GetKey();
int shmid=shmget(key,SIZE,IPC_CREAT | IPC_EXCL | 0666);
if(shmid==-1)
{
cout<<"GetShmget fail"<<endl;
return -1;
}
cout<<"shmid:"<<shmid<<endl;
return shmid;
}
processa.cc
#include"command.hpp"
int main()
{
GetShmget();
return 0;
}
现象:
注意:IPC(进程将通信)资源生命周期不随进程,而是随内核的,不释放会一直占用,除非重启。所以,shmget创建的共享内存要释放掉,不然会内存泄漏。如图:
可以用命令行来释放共享内存**:ipcrm -m shmid**(shmget返回值)
1 #include<stdio.h>
2 #include<sys/types.h>
3 #include<sys/ipc.h>
4 #include<sys/shm.h>
5 #include"com.h"
6
7 int main(){
8 key_t k = ftok(PATHNAME,PROJ_ID);//获取一个唯一标识符key
9 if(k==-1){
10 perror("ftok error");
11 return 1;
12 }
13
14 printf("ftok : %d\n",k);
15
16 int shmid = shmget(k,SIZE,IPC_CREAT | IPC_EXCL | 0666);//创建共享内存
17 if(shmid == -1){
18 perror("shmget error");
19 return 1;
20 }
21 printf("shmid : %d\n",shmid);
22
23 int sh = shmctl(shmid,IPC_RMID,NULL);//删除共享内存
24 if(sh == -1){
25 perror("shmctl");
26 return 1;
27 }
28 return 0;
29 }
作用:删除共享内存与进程地址空间的映射关系,将页表映射关系删除,释放进程地址空间。
参数:
shmaddr:共享内存映射到进程地址空间的地址。shmat返回值。
返回值:
成功返回0,失败返回-1
makefile
.PHONY:all
all:processa processb
processa:processa.cc
g++ -o $@ $^ -std=c++11
processb:processb.cc
g++ -o $@ $^ -std=c++11
.PHONY:clean
clean:
rm -f processa processb
command.hpp
#include
using namespace std;
#include
#include
#include
#include
#include
#define PATHNAME "/home/zuofangting"
#define PROJ_ID 0x888
#define SIZE 4096
key_t GetKey()
{
key_t ret=ftok(PATHNAME,PROJ_ID);
if(ret==-1) return -1;
cout<<"key:"<<ret<<endl;
return ret;
}
int GetShmget(int flag)
{
key_t key=GetKey();
int shmid=shmget(key,SIZE,flag);
if(shmid==-1)
{
cout<<"GetShmget fail"<<endl;
return -1;
}
cout<<"shmid:"<<shmid<<endl;
return shmid;
}
int CreateShm()
{
return GetShmget(IPC_CREAT | IPC_EXCL | 0666);
}
int GetShm()
{
return GetShmget(IPC_CREAT);
}
processa.cc
#include"command.hpp"
int main()
{
//创建共享内存
int shmid=CreateShm();
//挂接
char *shmaddr=(char*)shmat(shmid,nullptr,0);
//通信
while(true)
{
cout<<"client@:"<<shmaddr<<endl;
}
//去挂接
int ret=shmdt(shmaddr);
//释放共享内存
shmctl(shmid,IPC_RMID,nullptr);
return 0;
}
processb.cc
#include"command.hpp"
int main()
{
//创建共享内存
int shmid=GetShm();
//挂接
char *shmaddr=(char*)shmat(shmid,nullptr,0);
//通信
while(true)
{
char buffer[1024];
cout<<"please enter";
fgets(buffer,sizeof(buffer),stdin);
memcpy(shmaddr,buffer,strlen(buffer)+1);
}
//去挂接
int ret=shmdt(shmaddr);
return 0;
}
共享内存实现的进程间通信底层不提供任何同步与互斥机制。如果想让两进程很好的合作起来,在IPC里要有信号量来支撑。