共享内存
共享内存的理解
共享内存是System V版本的一个进程间通信方式。共享内存,顾名思义就是允许两个不相关的进程访问同一个逻辑内存,共享内存是两个正在运行的进程之间共享和传递数据的一种非常有效的方式。不同进程之间共享的内存通常为同一段物理内存。进程可以将同一段物理内存连接到他们自己的地址空间中,所有的进程都可以访问共享内存中的地址。如果某个进程向共享内存写入数据,所做的改动将立即影响到可以访问同一段共享内存的任何其他进程。
特别提醒:共享内存并未提供同步机制,也就是说,在第一个进程结束对共享内存的写操作之前,并无自动机制可以阻止第二个进程开始对它进行读取,所以我们通常需要用其他的机制来同步对共享内存的访问,例如信号量。
共享内存通信原理
在之前学习管道时就说过多个进程之间要实现通信的话,就得让它们看到同一个资源,通过这个资源来实现通信。
在共享内存中是让他们看到同一块内存。在Linux中,每个进程都有属于自己的进程控制块(PCB)和地址空间(Addr Space),并且都有一个与之对应的页表,负责将进程的虚拟地址与物理地址进行映射,通过内存管理单元(MMU)进行管理。两个不同的虚拟地址通过页表映射到物理空间的同一区域,它们所指向的这块区域即共享内存
当两个进程通过页表将虚拟地址映射到物理地址时,在物理地址中有一块共同的内存区,即共享内存,这块内存可以被两个进程同时看到。这样当一个进程进行写操作,另一个进程读操作就可以实现进程间通信。但是,我们要确保一个进程在写的时候不能被读,因此我们使用信号量来实现同步与互斥 。
但是我们要怎么判断两个进程是挂载到了同一块内存空间,因为内存空间中可能不止一个共享内存,这里我们了解一个创建key值的函数ftok()函数,这里的key值就是这块共享内存空间的唯一标识符。两个进程通过同一个key值挂载到同一块共享内存。
key_t ftok(const char* pathname,int proj_id);
参数解释:
*pathname就是你指定的文档名
proj_id是子序号
返回值:成功返回一个key值,失败返回-1。
nattch是共享内存的挂载计数器,对于一个共享内存,实现采用的是引用计数的原理,当进程脱离共享存储区后,计数器减一,挂载成功时,计数器加一,只有当计数器变为零时,才能被删除。当进程终止时,它所附加的共享存储区都会自动脱离。
共享内存的接口函数以及指令
指令
1查看系统中的共享储存
2手动删除系统中的共享内存
接口函数
1、创建共享内存
int shmget(key_t key,size_t size, int shmflg)
参数解释:
返回值:
2、挂载共享内存
void* shmat(int shmid,const void *shmaddr,int shmflg);
参数解释:
返回值:
成功返回共享存储段的指针(虚拟地址),并且内核将使其与该共享存储段相关的shmid_ds结构中的shm_nattch计数器加1(类似于引用计数);出错返回-1 。
3、去关联共享内存
当进程之间完成通信时,就需要去关联,需要将该进程从之前连接的共享内存上脱离下来。
int shmdt(const void* shmaddr);
参数解释:
返回值
成功返回0,并将shmid_ds结构体中的 shm_nattch计数器减1;出错返回-1
4、释放共享内存
当shm_nattch为0时就需要释放这块共享内存,否则就会一直占用下去,因为共享内存的生命周期是跟随内核的。
int shmctl(int shmid,int cmd,struct shmid_ds* buf);
参数解释:
返回值:
模拟共享内存
//server.c
#include<stdio.h>
#include<unistd.h>
#include"comm.h"
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/shm.h>
int main()
{
//ftok创建key值
key_t k=ftok(PATHNAME,PROJ_ID);
//根据key值创建共享内存
int shmid=shmget(k,SIZE,IPC_CREAT|IPC_EXCL|0666);
//判断是否创建成功
if(shmid<0)
{
perror("shmget");
return 1;
}
//挂载共享内存
char* str=(char*)shmat(shmid,NULL,0);
//int i=0;
while(1)
{
//i++;
sleep(1);
printf("%s\n",str);
}
//去关联
shmdt(str);
//释放共享内存
shmctl(shmid,IPC_RMID,NULL);
return 0;
}
//client.c
#include<stdio.h>
#include<unistd.h>
#include"comm.h"
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/shm.h>
int main()
{
//ftok创建key值
key_t k=ftok(PATHNAME,PROJ_ID);
//根据key值找到共享内存
int shmid=shmget(k,SIZE,0);
//判断是否创建成功
if(shmid<0)
{
perror("shmget");
return 1;
}
//挂载共享内存
char* str=(char*)shmat(shmid,NULL,0);
char c='a';
for(;c<='z';c++)
{
str[c-'a']=c;
sleep(2);
}
//去关联
shmdt(str);
//这里不用释放共享内存,只需要释放一次就可以,server.c已经释放了。
//shmctl(shmid,IPC_RMID,NULL);
return 0;
}