目录
前言:
System V IPC(系统5的进程间通信)是在Unix操作系统上实现进程间通信(IPC)的一种机制。它引入了一大类进程间通信方法,包括共享内存、消息队列和信号灯集等IPC对象。每个IPC对象都有唯一的ID,这个ID在创建时由系统分配,并且IPC对象在创建后一直存在,直到被显式地删除或系统关闭时自动释放。
在这篇博客中,我们重点介绍System V IPC中的共享内存部分,以及学习消息队列、信号量的一些接口的使用和 Linux下实现的System V IPC统一的数据结构体系……
1.共享内存
1.1.什么是共享内存
我们已经清楚:进程间通信的本质是让不同的进程看到同一份资源。那么共享内存这种进程间通信方式的实现是通过让不同的进程通过“共享”某一块内存来实现的。
共享内存是通过在物理内存中开辟一块内存,使得这块内存通过页表映射,到两个(多个)不同进程的进程地址空间中,从而让不同的进程可以访问到这一块物理内存,实现间接的数据交互的一种进程间通信方式。
讲完生硬的概念,我们来进入一个场景,共享内存就相当于隔绝了两个村子的河流,而两个进程就相当于这两个村子的两个人,当这两个人(进程)需要传送东西(进行数据交互)就能通过这个河流(共享内存) 来实现,那么通过这个河流,就能实现两个人的通信了……
1.2.共享内存使用接口
shmget函数
在这个图中:我们看到了key是共享内存的名字,那这个key我们怎么获得的呢?
// 通过ftok算法形成一个唯一的key
key_t key = ftok("名称", 传入一个int参数);
因为实际场景下会出现许多个共享内存同时进行通信的情况,这时系统为了对这些共享内存进行管理就需要给他们一个唯一的标识符shmid(类比:文件fd、进程pid),而这个shmid是通过一个唯一的key值来实现的。并且如果我们需要进程进程间通信,那么我们也需要让不同的进程能够访问到同一个key和同一个shmid。
具体实现如下:
// 同一个文件名
const string filename = "/home/Czh_Linux/code/shared_memory";
// 同一个proj_id
const int proj_id = 2022044026;
// 获取唯一标识符
key_t GetKey()
{
// 通过ftok算法形成一个唯一的key
// 通过相同的filename、和project_id
key_t key = ftok(filename.c_str(), proj_id);
if (key < 0)
{
cerr << "errno: " << errno << ", errstring: " << strerror(errno) << endl;
exit(1);
}
return key;
}
这里我们传入文件名的原因:可以让不同的进程更加方便的访问到同一个key……我们在获取了唯一的key之后,接下来就是学习shmget如何使用了。
shmget的具体使用分为两种情形:
创建一段新的共享内存:
// 参数(唯一标识符key,共享内存的大小,打开方式|权限)
int shmid = shmget(key, 4096, IPC_CREAT | IPC_EXCL | 0644); // 共享内存的id
这段代码中 :表示创建一段共享内存,则需传入权限,并且通过IPC_EXCL防止重复创建,并且提供权限
打开一段已有的共享内存:
// 打开方式直接设置为0即可
int shmid = shmget(key, 4096, 0);
但是实际上我们使用时,一般将这两种情况进行封装改造一下:
// 创建/打开 共享内存封装函数
int ShmOption(key_t key, int flag)
{
// 参数(唯一标识符key,共享内存的大小,打开方式|权限)
int shmid = shmget(key, MEMORYSIZE, flag); // 共享内存的id
cout << shmid << endl;
// key作为在内核中标识shm的唯一性
// 对共享内存进行操作时,我们是通过shmid来进行的
if (shmid < 0)
{
cerr << "shmid error: " << errno << ", errstring: " << strerror(errno) << endl;
exit(2);
}
return shmid;
}
// 创建新的共享内存,并返回shmid
int CreateShm(key_t key)
{
cout << "新的共享内存创建完成" << endl;
// 创建,则需传入权限,并且通过O_EXCL防止重复创建,并且提供权限
return ShmOption(key, IPC_CREAT | IPC_EXCL | 0644);
}
// 打开原有共享内存,并返回shmid
int GetShm(key_t key)
{
// 打开,则只需通过O_CREAT打开
return ShmOption(key, 0);
}
所以我们外部调用的时候,通过不同的接口实现即可,另外GetKey()函数在上面有,也是我们自己实现的封装的小模块
值得一提的是:我们可以分别通过下面这两个Linux指令实现对共享内存的查看和删除
// 查看当前的共享内存
ipcs -m
// 删除当前共享内存
ipcrm -m "对应的shmid"
另外:如果我们在进程中通过shmget开辟出一块共享内存,却没有通过shmctl进行释放,我们调用ipcs -m就会发现:即使进程退出后,这块共享内存也依旧保留着物理内存中,也就是:共享内存的生命周期是随内核的