目录
1.共享内存:
2.创建与删除
3.attch与detach
4.使用
进程间通信的本质就是:让不同的进程看见同一份资源。
上一篇博客的匿名管道适用于:有血缘关系的进程,父进程子进程兄弟进程。
命名管道适用于:不相关的进程,它是一种特殊的文件。
它们俩的本质都是一片缓冲区。
而这次的共享内存实质是物理内存的一块区域。
不同的进程通过访问内存上的同一片区域,实现通信。
这次学的是System V共享内存,这是时代竞争的产物。
原理:
①在内存中创建共享内存区域。
②通过特定系统接口将这块区域映射关联到进程的上下文中。
创建我们要使用到shmget这个系统调用接口。
它的作用是创建一个 System V级别的共享内存。
①key:标识共享内存的值。
②size:共享内存的大小。建议传(页)4KB的整数倍,因为磁盘和操作系统IO的基本单位是4KB,所以磁盘在将数据拷贝到内存时,会以4KB整数倍的大小来占用内存。
③shmflg:可以传入两个宏,IPC_CREAT 和 IPC_EXCL。传入0的话,默认是IPC_CREAT。
IPC_CREAT:创建共享内存,如果存在就获取它,如果不存在就创建一个。
IPC_EXCL:与IPC_CREAT搭配使用,如果存在就获取它,如果不存在就出错返回。
->它可以保证如果调用shmget函数成功,所创建的一定是一个全新的共享内存。
return value:a valid shared memory identifier is returned.
一个有效的共享内存的标识符被返回。
Ps:
我们怎么知道共享内存存在还是不存在?内核中有相应的数据结构来管理这些共享内存。
在用户层,会有这样的结构体来描述共享内存:
这个结构体中还有一个结构体ipc_perm (IPC(Inter-Process Communication,进程间通信) perm(permission,权限,许可)),也就是进程间通信权限。
这个结构体中第一个成员__Key,是不是很熟悉,就是shmget的一个参数。
它表示共享内存的唯一值。这个key值一般由用户提供。
假设:a进程和b进程要通信,a进程向shmget传入一个key值,b进程通过这个key值访问这块共享内存,这就实现了进程间通信。
那怎么保证我们的唯一key值和系统已存在的共享内存的key值冲突呢?
要使用ftok这个函数帮我生成。
①pathname:任意一个文件所在路径。
②proj_id:你想要设置的值。
因为文件的inode编号是唯一的与你给的数值,依据算法,会给出一个key值。
我们先来使用下这些函数。
为了更好的打印:
#pragma once
#include
#include
std::ostream& output()
{
std::cout<<"For debug:"<
第一步:在head.hpp中使用ftok函数创建key值
#pragma once
#include
#include
#include
#include
#include
#include
#include
#define PATH "/home/Yewei/Code/study20221112"
#define PROJ_ID 0x12
key_t Creat_key()
{
key_t key = ftok(PATH,PROJ_ID);
if(key == -1)
{
std::cerr<<"ftok erro:"<
第二步:获得key值之后,调用shmget函数
const int flag = IPC_CREAT | IPC_EXCL ;
int main()
{
//create key
key_t key = Creat_key();
output()<< key <
运行结果:
会发现这个共享内存总是创建失败,说明声明,随着进程结束,共享内存并不会被销毁。
通过退出码可以看到,是在创建共享内存时报错。
结论是:
System V下的共享内存,它的生命周期随着内核。如果不显示的删除,只能通过kernel(os)重启来解决。
如何显示的删除:
①先知道要删除哪一个
ipcs -m
Ps:
perms:
这是我们所创建共享内存的权限,与对操作的文件的权限相同,避免我们一会使用共享内存出现问题,所以我们现在在flag后面要"|"上对共享内存操作的权限。
nattch:
与共享内存所连接进程的个数。
status:
表示共享内存的状态。
通过命令行删除
ipcrm -m X X:shimd
为什么要用shimd来删除呢,为什么不用key来删除,毕竟key值是唯一标示的共享内存。
因为我们是在用户层,用命令行来用进程将共享内存删除。shmid是由shmget函数返回给用户的标定共享内存的值,在用户层删除当然要用shimd来删除。而key是内核中标定共享内存的值。
②通过系统接口
这次删除就简单点。
shmid:传入shmget的返回值。
cmd:传入IPC_RMID。
buf: 如果传入IPC_RMID,此处就可以传入NULL。
具体的大家可以在man手册中再深入研究一下。
我们使用一下:
//delte shm
int cnt = 3;
while(cnt--)
{
cout<<"删除倒计时::"<
结果:
attch我们要使用shmat。
简单使用。
shmid: 传入shmget的返回值。
shamaddr:所要挂接到地址空间的什么位置,今天我们只用传NULL。
shmflg:传入0,就是默认读写共享内存。
return value:成功,返回挂接共享内存的地址。失败,-1。
关键就是这个返回值,就如同malloc一样,成功创建一块堆区空间,返回地址通过地址来访问堆区。在这一样,我们可以通过返回值来访问共享内存。
代码:
//attch shm
int cnt = 3;
while(cnt--)
{
cout<<"attach倒计时::"<
结果:
detach我们要使用shmdt。
只要将我们获得的共享内存的地址传入即可。
代码:
//detach shm
cnt = 3;
while (cnt--)
{
cout << "detach倒计时::" << cnt << endl;
}
shmdt(shm);
output() << "detach success!" << endl<
我们首先去编写另一个进程,用当前进程和另一个进程共同去使用共享内存。
注意我们创建shm的文件是IpcShmCre.cpp,使用shm的文件是IpcShmUse.cpp。
在IpcShmUse.cpp中这样写:
#include "head.hpp"
#include "output.hpp"
using namespace std;
//使用共享内存
int main()
{
// creat key
key_t key = Creat_key();
cout << key << endl;
// get shm
int shmid = shmget(key, SIZE, IPC_CREAT);
if (shmid == -1)
{
output() << "shmget error :" << strerror(errno) << endl;
return 1;
}
//attch
char* shm = (char*)shmat(shmid,nullptr,0);
//use shm
int index = 0;
while(index != 26)
{
shm[index] = 'A' + index;
++index;
shm[index] = '\0';
sleep(1);
}
//detach shm
shmdt(shm);
}
在IpcShmCre.cpp中这样写:
// use shm
while(1)
{
printf("%s\n",shm);
sleep(1);
}
结果:
运行一段时间后:
导致这样的情况是因为共享内存的的特性,它没有任何访问控制。共享内存被用户直接看到,属于双方的用户空间。
我们还可以这样写:
在IpcShmCre.cpp中这样写:
// use shm
while(1)
{
printf("%s",shm);
sleep(1);
}
在IpcShmUse.cpp中这样写:
while(1)
{
printf("Please import$$");
fflush(stdout);
ssize_t sz = read(0,shm,SIZE);
if(sz > 0)
{
shm[sz] = '\0';
}
}
结果:
感谢观看。