Linux——进程间通信——system V系列

news2025/1/10 4:02:41

 ✅<1>主页::我的代码爱吃辣
📃<2>知识讲解:Linux——进程间通信——system V系列
☂️<3>开发环境:Centos7
💬<4>前言:system V 版本进程间通信的机制。

目录

一.共享内存介绍

 二.共享内存函数

1.shmget函数

 2.shmat函数

 3.shmdt函数

 4.shmctl函数

 三.代码示例

1.Comm.hpp

2.client.cc

3.server.cc

4.测试结果

四. 消息队列

 五.信号量

1.互斥



system共有三个IPC方案:

  1. System V 共享内存
  2. System V 消息队列
  3. System V 信号量

 今天我们重点讲解 system共享内存。

一.共享内存介绍

共享内存是进程间通信中最简单的方式之一,也是最快的IPC形式,因为进程可以直接读写内存,而不需要任何数据的拷贝 。共享内存允许两个或更多进程访问同一块内存,就如同malloc()函数向不同进程返回了指向同一个物理内存区域的指针。

共享内存在进程空间的映射:使用共享内存通信的一个进程可以将共享内存映射到自己的地址空间中,这样就可以像操作普通变量一样操作这块内存了。另外一个进程也可以通过相同的方式将这块内存映射到自己的地址空间中,然后进行读写操作。

示意图:

 查看内核中的共享内存使用命令:

ipcs

 二.共享内存函数

1.shmget函数

功能:用来创建共享内存。
原型:int shmget(key_t key, size_t size, int shmflg)。
参数:

  1. key:这个共享内存段名字。
  2. size:共享内存大小。
  3. shmflg:由九个权限标志构成,它们的用法和创建文件时使用的mode模式标志是一样的。

返回值:成功返回一个非负整数,即该共享内存段的标识码;失败返回-1。

 2.shmat函数

功能:将共享内存段连接到进程地址空间。
原型:void *shmat(int shmid, const void *shmaddr, int shmflg);
参数

  1. shmid: 共享内存标识。
  2. shmaddr:指定连接的地址。
  3. shmflg:它的两个可能取值是SHM_RND和SHM_RDONLY。

返回值:成功返回一个指针,指向共享内存第一个节;失败返回-1 。

说明:

  • shmaddr为NULL,核心自动选择一个地址。
  • shmaddr不为NULL且shmflg无SHM_RND标记,则以shmaddr为连接地址。
  • shmaddr不为NULL且shmflg设置了SHM_RND标记,则连接的地址会自动向下调整为SHMLBA的整数倍。公式:shmaddr -(shmaddr % SHMLBA)。
  • shmflg=SHM_RDONLY,表示连接操作用来只读共享内存。

 3.shmdt函数

功能:将共享内存段与当前进程脱离。
原型:int shmdt(const void *shmaddr);
参数:shmaddr: 由shmat所返回的指针。
返回值:成功返回0;失败返回-1。
注意:将共享内存段与当前进程脱离不等于删除共享内存段。

 4.shmctl函数

功能:用于控制共享内存。
原型:int shmctl(int shmid, int cmd, struct shmid_ds *buf);
参数

  1. shmid:由shmget返回的共享内存标识码。
  2. cmd:将要采取的动作(有三个可取值)。
  3. buf:指向一个保存着共享内存的模式状态和访问权限的数据结构。

返回值:成功返回0;失败返回-1。

 三.代码示例

基于共享内存实现server,client通信:

1.Comm.hpp

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

using namespace std;

// 项目路径
const char *pathname = "/home/wq/23_10_1";
// 项目编号
const int pro_id = 0664;
// 共享内存大小
const size_t gsize = 4096;

#define CLIENT 1
#define SERVER 0

class Shm
{
public:
    Shm(int type)
        : _type(type)
    {
        getkey();
        if (_type == 0)
            createshmserver();
        else
            createshmclient();
        hookshm();
    }
    char *getstart()
    {
        return _start;
    }
    ~Shm()
    {
        Leaveshm();

        if (_type == 0)
        {
            sleep(3);
            delshm();
        }
    }

private:
    void To_Hex(key_t k)
    {
        printf("0x%x\n", k);
    }

    // 1.获取唯一共享内存段名字key
    void getkey()
    {
        _key = ftok(pathname, pro_id);
        To_Hex(_key);
        if (_key == -1)
        {
            cerr << "shm:" << errno << ":" << strerror(errno) << endl;
        }
    }

    // 2.创建共享内存
    int createshm(int flag)
    {
        int shmid = shmget(_key, gsize, flag);

        return shmid;
    }

    // 2.1创建共享内存客户端接口
    void createshmclient()
    {
        // 客户端仅仅找到服务端创建的shm
        _shmid = createshm(IPC_CREAT);
        if (_shmid == -1)
        {
            cerr << "client shm create:" << errno << ":" << strerror(errno) << endl;
            exit(2);
        }
    }

    // 2.2创建共享内存服务端接口
    void createshmserver()
    {
        // 创建shm,如果已经存在就报错,不存在创建一个新的共享内存
        _shmid = createshm(IPC_CREAT | IPC_EXCL | 0666);
        if (_shmid == -1)
        {
            cerr << "server shm create:" << errno << ":" << strerror(errno) << endl;
            exit(3);
        }
    }

    // 3.将共享内存段连接到进程地址空间
    void hookshm()
    {
        _start = (char *)shmat(_shmid, NULL, SHM_RND);
        if (_start == NULL)
        {
            cerr << "hookshm:" << errno << ":" << strerror(errno) << endl;
        }
    }

    // 4.将共享内存段与到进程地址空间断开
    void Leaveshm()
    {
        int n = shmdt(_start);
        if (n == -1)
        {
            cerr << "shmdt:" << errno << ":" << strerror(errno) << endl;
        }
    }

    // 5.删除共享内存
    void delshm()
    {
        int n = shmctl(_shmid, IPC_RMID, NULL);
        if (n == -1)
        {
            cerr << "shmdt:" << errno << ":" << strerror(errno) << endl;
        }
    }

private:
    key_t _key;   // 共享内存段名字
    int _shmid;   // 共享内存段的标识码
    char *_start; // 共享内存映射到地址空间的起始地址,将来当作一段malloc的空间使用
    int _type;    // 身份 —— SERVER,CLIENT
};

2.client.cc

#include "Comm.hpp"

int main()
{
    // 1.创建共享内存对象
    Shm shm(CLIENT);
    // 2.得到内存块
    char *start = shm.getstart();
    // 3.读取数据
    while (1)
    {
        sleep(2);
        printf("%s", start);
    }

    return 0;
}

3.server.cc

#include "Comm.hpp"

int main()
{
    // 1.创建共享内存对象
    Shm shm(SERVER);
    // 2.得到内存块
    char *start = shm.getstart();
    // 3.写入数据
    while (1)
    {
        char buff[1024] = {0};
        char *str = fgets(buff, sizeof(buff), stdin);
        buff[strlen(str)] = 0;
        for (int i = 0; i < strlen(str); i++)
        {
            start[i] = buff[i];
        }
        start[strlen(str)] = 0;
    }
    sleep(20);

    return 0;
}

 makefile:

.PHONY:all
all:server client

server:server.cc
	g++ -o $@ $^ -std=c++11

client:client.cc
	g++ -o $@ $^ -std=c++11

.PHONY:clean
clean:
	rm -rf server client 

4.测试结果

四. 消息队列

  1. 消息队列提供了一个从一个进程向另外一个进程发送一块数据的方法。
  2. 每个数据块都被认为是有一个类型,接收者进程接收的数据块可以有不同的类型值特性方面。
  3. IPC资源必须删除,否则不会自动清除,除非重启,所以system V IPC资源的生命周期随内核。

 五.信号量

信号量主要用于同步和互斥的,下面先来看看什么是同步和互斥。

1.互斥

  1. 任何一个时刻,都只允许一个执行流在进行共享资源的访问——加锁。
  2. 我们把任何一个时刻,都只允许一个执行流在进行访问的共享资源,叫做临界资源。
  3. 临界资源是要通过代码访问的,凡是访问临界资源的代码,叫做临界区。
  4. 要么不做,要么做完,只有两种确定状态的属性,原子性。
  5. IPC资源必须删除,否则不会自动清除,除非重启,所以system V IPC资源的生命周期随内核。
     

信号量就是对资源的一种预定机制,就像电影院座位和电影票一样。其本身就是一种计数器,信号量有两种操作P操作(申请信号量)计数器++,V操作(释放信号量)计数器--。

进程想要访问临界资源,首先申请信号量(P操作),只要我们申请成功未来一定能得到子资源。

访问完临界资源释放信号量(V操作)。

P,V操作,计数器的++或者--,一定要是原子的。

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1057547.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

WebGPU 入门:绘制一个三角形

大家好&#xff0c;我是前端西瓜哥。 今天我们来入门 WebGPU&#xff0c;来写一个图形版本的 Hello World&#xff0c;即绘制一个三角形。 WebGPU 是什么&#xff1f; WebGPU 是一个正在开发中的潜在 Web 标准和 JavaScript API&#xff0c;目标是提供 “现代化的 3D 图形和计…

AutoCAD 产品设计:图形单位

本文讲解 AutoCAD 产品的图形单位功能产品设计&#xff0c;没有任何代码实现。 使用的 AutoCAD 为 2020 版本 图形单位是什么&#xff1f; 图形单位是用于设置 一些属性数据应该用什么格式显示 的命令&#xff0c;命令标识为 un&#xff08;units&#xff09;。 举个例子。 …

操作EXCEL计算3万条数据的NDVI并填入

Python操作EXCEL&#xff0c;计算3万条数据的NDVI并填入 问题描述 现在是有构建好了的查找表&#xff0c;不过构建了3万条数据&#xff0c;在excel中手动计算每行的NDVI值太麻烦了&#xff0c;也不会操作。 就试试python吧&#xff0c;毕竟python自动处理大型EXCEL数据很方便…

黑马头条项目环境搭建

注册中心网关配置 spring:cloud:gateway:globalcors:add-to-simple-url-handler-mapping: truecorsConfigurations:[/**]:allowedHeaders: "*"allowedOrigins: "*"allowedMethods:- GET- POST- DELETE- PUT- OPTIONroutes:# 平台管理- id: useruri: lb://…

51单片机可调幅度频率波形信号发生器( proteus仿真+程序+原理图+报告+讲解视频)

51单片机可调幅度频率信号发生器( proteus仿真程序原理图报告讲解视频&#xff09; 讲解视频1.主要功能&#xff1a;2.仿真3. 程序代码4. 原理图4. 设计报告5. 设计资料内容清单&&下载链接***[资料下载链接](https://docs.qq.com/doc/DS1daV1BKRXZMeE9u)*** 51单片机可…

数据结构——计数与归并非递归

排序算法 前言一、归并的非递归实现二、计数排序三、序算法复杂度及稳定性分析总结 前言 重要的事说三遍&#xff01; 学习&#xff01;学习&#xff01;学习&#xff01; 努力!努力!努力&#xff01; 一、归并的非递归实现 代码实现&#xff1a; void MergeSortNonR(int* a,…

3分钟在移动盘上安装Ubuntu系统和ROS2

目录 原视频准备烧录 一个usb移动固态硬盘可以干什么呢&#xff1f; 可以用移动盘解决电脑存储空间不足的问题&#xff0c;可以用移动盘存储数据&#xff0c;可以用移动盘装其他系统当做双系统来使用&#xff0c;可以在一个移动固态硬盘里装两个甚至更多的系统… 下面&#xf…

《C++ primer plus》精炼(OOP部分)——对象和类(8)

学习是一项持续的投资&#xff0c;永远不会白费——本杰明富兰克林 文章目录 第13章&#xff1a;类继承一个基类和派生类公有继承的逻辑关系&#xff1a;is-a多态公有继承 第13章&#xff1a;类继承 一个基类和派生类 从一个类派生出另一个类时&#xff0c;原始类称为基类&am…

SLAM简要介绍

过去二十年&#xff0c;计算机视觉和机器学习领域都取得了很多进步。这一切都归功于硬件的改进&#xff0c;这使得研究人员和工程师能够设计和训练更复杂和更准确的模型&#xff0c;同时以以前难以想象的规模处理和分析数据。随着硬件的这些改进&#xff0c;未知环境的映射已经…

基于Java的学校固定资产管理系统设计与实现(源码+lw+部署文档+讲解等)

文章目录 前言具体实现截图论文参考详细视频演示为什么选择我自己的网站自己的小程序&#xff08;小蔡coding&#xff09;有保障的售后福利 代码参考源码获取 前言 &#x1f497;博主介绍&#xff1a;✌全网粉丝10W,CSDN特邀作者、博客专家、CSDN新星计划导师、全栈领域优质创作…

vue 使用 创建二维数组响应数据 渲染 echarts图标

目前我遇到的情况就是用动态的二维数组数据渲染echarts图标&#xff0c;我们从后端收到的接口一般是个一维数组&#xff0c;需要手动构建并且保证响应式。接下来我做了个案例 一、案例总逻辑 1. 先创建一个vue项目 2. 添加 echarts依赖 3. 模拟数据请求&#xff0c;构建二维数组…

支付宝2023年收单外包服务机构评级启动,截止11月15日

9月22日消息&#xff0c;支付宝近日发布公告称&#xff0c;已启动2023年收单外包服务机构评级工作。支付宝表示&#xff0c;收单外包服务机构评级工作是金融监管部门规范引导收单外包服务市场的重要举措&#xff0c;其结果将会向社会公示&#xff0c;直接关系到外包机构的业务开…

QT 之数据库 QSqlQuery CURD 实战

零、参考文档 https://doc.qt.io/archives/qt-6.0/qsqldatabase.html 一、开发环境 Ubuntu 20.04 QT6.0 Microsoft SQL Server 2022 Developer Edition (64-bit) 先修改 /etc/odbc.ini 的数据源配置&#xff0c;指定连接数据库 vdb&#xff0c; sudo vim /etc/odbc.ini[mss…

《发现的乐趣》作者费曼(读书笔记)

目录 一、书简介 二、作者理查德•费曼 费曼式思维 教育与传承 三、个人思考 四、笔记 科学家眼中的花之美 关于偏科 父亲教育我的方式 知道一个概念和真正懂得这个概念有很大区别 我没有义务去成全别人对我的期望 诺贝尔奖——够格吗&#xff1f; 探究世界的游戏规…

基于SpringBoot的商品物品产品众筹平台设计与实现(源码+lw+部署文档+讲解等)

文章目录 前言具体实现截图论文参考详细视频演示为什么选择我自己的网站自己的小程序&#xff08;小蔡coding&#xff09;有保障的售后福利 代码参考源码获取 前言 &#x1f497;博主介绍&#xff1a;✌全网粉丝10W,CSDN特邀作者、博客专家、CSDN新星计划导师、全栈领域优质创作…

内网综合扫描工具-fscan的安装和使用

简介 一款内网综合扫描工具&#xff0c;方便一键自动化、全方位漏扫扫描。 支持主机存活探测、端口扫描、常见服务的爆破、ms17010、redis批量写公钥、计划任务反弹shell、读取win网卡信息、web指纹识别、web漏洞扫描、netbios探测、域控识别等功能。相当强大&#xff01;&…

JavaSE | 初始Java(九) | 包的使用

包 包是对类、接口等的封装机制的体现&#xff0c;是一种对类或者接口等的很好的组织方式&#xff0c;比如&#xff1a;一个包中的类不想被其他包中的类使用。包还有一个重要的作用&#xff1a;在同一个工程中允许存在相同名称的类&#xff0c;只要处在不同的包中即可。 可以…

【Spring】Spring 创建和使用

Spring 创建和使用 一. 创建 Spring 项目1. 创建⼀个 Maven 项目2. 添加 Spring 框架⽀持3. 添加启动类 二. 存储 Bean 对象1. 创建 Bean2. 将 Bean 注册到容器 三. 获取并使⽤ Bean 对象1. 创建 Spring 上下文2. 获取指定的 Bean 对象3. 使用 Bean Spring 就是⼀个包含了众多⼯…

HDF5文件数据读取

1、HDF5文件说明 HDF 是用于存储和分发科学数据的一种自我描述、多对象文件格式。HDF 是由美国国家超级计算应用中心(NCSA)创建的,以满足不同群体的科学家在不同工程项目领域之需要。HDF 可以表示出科学数据存储和分布的许多必要条件。HDF 被设计为: 自述性:对于一个HDF …

Koa学习4:密码加密、验证登录、颁发token、用户认证

请求体 这里遇到了个问题&#xff0c;ctx.request.body 的值是一个字符串。明明已经使用了koa-body中间件 查了一下原因是&#xff1a; ctx.request.body的值可能是一个对象或一个字符串&#xff0c;取决于请求的Content-Type和请求体的格式。 当使用koa-body中间件时&#x…