Linux中的System V通信标准--共享内存、消息队列以及信号量

        关于 System V 标准,一共有三种通信方式,分别为:共享内存、信号量和消息队列三种通信方式。本篇将较为详细的讲解三种通信方式的实现原理,以及介绍在 Linux 系统下调用这三种的通信方式的接口,其中以共享内存为例,较为详细的讲解和用代码实现这种通信方式。

        最后我们得出这三种通信方式存在很大的共同点,以及总结了操作系统对这三种通信方式的管理。目录如下:

目录

共享内存

1. 实现原理

2. 代码实现进程的共享内存通信

共享内存的创建

共享内存的释放

共享内存挂接

共享内存间通信代码

3. 共享内存的优缺点

4. 获取共享内存的属性

消息队列

1. 消息队列的原理

2. 消息队列的接口

3. 消息队列的指令操作

信号量

1. 信号量的原理

2. 信号量的操作

3. 信号量的指令

操作系统对三种 System V 的管理

共享内存

1. 实现原理

        共享内存为进程间通信方案,则一定会遵守进程间通信的的原则:让不同进程看见同一份资源。对于共享内存的实现原理为:操作系统会在物理内存中专门给需要通信的进程开辟一段物理内存,然后分别映射到不同进程的虚拟地址中,不同的进程就可以看到同一份的内存资源。原理图如下:

        如上的步骤1:操作系统在物理内存中开辟出同于共享内存通信的物理内存;

        步骤2:操作系统将物理共享内存通过页表映射到对应的虚拟地址中去。

共享内存实现的几个关键点:

        1. 关于实现共享内存的所有操作,都是由OS(操作系统)完成的;

        2. OS给需要共享内存通信的进程提供步骤1、2的系统调用,让他们通过系统调用来通信。

        3. 共享内存在系统中可以同时存在多份,不同对进程之间可以同时进行通信。

        4. 操作系统需要对共享内存进行管理,所以会有对应的共享内存数据结构,以及匹配的算法。        

        5. 共享内存 = 内存空间 + 共享内存的属性。

2. 代码实现进程的共享内存通信

共享内存的创建

        关于使用系统调用实现共享内存的一个重要的系统调用,shmget,也就是用来获取在物理内存中的共享内存,使用方法如下:

int shmget(key_t key, size_t size, int shmflg);
系统调用中的参数:
    key:用于唯一的表示物理内存中存在的共享内存;
    size:需要开辟的物理内存的大小,通常建议为4096的倍数
    shmflg:获取共享内存的方式,常用的为IPC_EXCL 和 IPC_CREAT,
            关于这两个的搭配有:
            IPC_CREAT:若不存在共享内存则创建,存在则获取对应的共享内存且返回
                       总能获取一个
            IPC_EXCL:不能单独的使用
            IPC_CREAT | IPC_EXCL:对应的共享内存不存在则创建,存在则出错返回
                                  只能获取新的

        关于在 shmget 中的 key 参数,不同的进程想要进程通信,则填入的 key 值则需要相同,对于这个 key 值,我们一般不建议直接由我们自己来填,而是建议使用系统提供的 ftok 函数给我们随机生成一个,如下:

key_t ftok(const char *pathname, int proj_id);

        只需要提供同样的 pathname 和同样的 proj_id,我们就可以生成同样的 key 值,也就可以让不同的进程之间实现通信。

共享内存的释放

        还有一个需要注意的点:我们使用进程创建出的共享内存,并不会随着进程的结束而释放,进程结束仍然会保留在内存中,因为这是由底层的操作系统创建出的共享内存,所以我们需要在每一次进程结束的时候,将对应的共享内存关闭,防止内存泄漏。(共享内存的生命周期随内核,文件的生命周期随进程)

        我们可以使用 ipcs -m 查看当前系统中存在哪些共享内存,然后使用 ipcrm -m shmid 删除对应的共享内存,如下:

        共享内存的 key 与 shmid 的比较:

        key:属于用户形成,内核使用的一个字段,内核用于区分共享内存的唯一性,用户不能使用 key 来进行共享内存的管理。

        shmid:内核给用户返回的一个标识符,用来进行用户级对共享内存进行管理的 id 值。

        使用系统调用删除共享内存为 shmctl,如下:

int shmctl(int shmid, int cmd, struct shmid_ds *buf);
该系统调用用于对共享内存的控制,可以为增删查改
但本篇只需要将共享内存释放,所以只需要填入的参数为:
共享内存的shmid
选择模式,删除为 IPC_RMID
buf 为 nullptr
共享内存挂接

        现在我们既然已经创建出对应的共享内存,只需要将共享内存挂接到对应的共享内存,然后就可以让对应进程之间开始通信了,如下:

void *shmat(int shmid, const void *shmaddr, int shmflg);
用于挂接共享内存
shmaddr表示将挂接共享内存的位置,通常可以设置为nullptr
shmflg表示共享内存的访问权限
shmat的返回值:挂接成功为共享内存的起始地址,连接失败为nullptr

int shmdt(const void *shmaddr);
用于将挂接上的共享内存给去掉
去挂接成功返回0,失败返回-1
共享内存间通信代码

Shm.hpp

#include <iostream>
#include <string>
#include <cerrno>
#include <cstring>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <unistd.h>

// 路径,随便使用一个路径都可以
const std::string SHPathName = "/home/JZhong/code/linux-code/test_7_20";
const int SHProj_Id = 0x8585;

#define SHCreater 1
#define SHUser    0 
#define SHsize 4096

class Shm {
    std::string ToHex(int n) {
        char buff[128];
        int cx = snprintf(buff, sizeof(buff) - 1, "0x%x", n);
        buff[cx] = '\0';
        return buff;
    }

    int GetShm(int shmflg) {
        int sh = shmget(_key, SHsize, shmflg);
        if (sh == -1) {
            std::cout << "Create shm fail!" << "the reason is " << strerror(errno) << std::endl;
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值