linux的通信方案(SYSTEM V)

文章目录

      • 共享内存(Share Memory)
      • 信号队列(Message Queue)
      • 信号量(semaphore)

进程间通信的核心理念:让不同的进程看见同一块资源
linux下的通信方案: SYSTEM V

共享内存(Share Memory)

特点:1.共享内存是进程见通信最最快的
           2.可以提供较大通信空间

注意:共享内存由于裸露给所有使用者,因此是需要维护的

做法:去申请一块空间,让其映射到对应的不同进程的进程地址空间。
如图:
在这里插入图片描述

那么具体是怎么做的呢?

  • linux是生成一个特定的key,有key作为表示这块共享内存的唯一标识。
  • 不同进程在运行的时候凭借这个key拿到共享内存的shmid(类似于文件管理系统的fd),进行挂接到自己的进程地址空间上。
  • 申请的空间是不会自己释放的,要么在程序里面用funtion控制,要么在外部手动释放
  • ipcs -m

  • #查看当前有哪些共享内存

  • ipcrm -m shmid

  • #删除对应的共享内存id

需要用到的系统调用:

shmget #创建共享内存
shmat # 挂接共享内存
shmdt # 取消挂接
shmctl # 操控这块共享内存
unlink # 删除文件

server:

#include "Common.hpp"

class Init
{
public:
    Init()
    {
        bool r = MakeFifo();//用管道是为了进程进行时,具备一定顺序性。
        if (!r)
            return;
        key_t key = GetKey();
        shmid = CreatShm(key);
        std::cout << "shmid:" << shmid << "\n";
        // sleep(5);
        std::cout << "开始将shm映射到进程地址空间\n";
        s = (char *)shmat(shmid, nullptr, 0);
        fd = open(filename.c_str(), O_RDONLY);
    }
    ~Init()
    {
        close(fd);
        std::cout << "将shm从进程地址空间移除\n";
        shmdt(s);

        std::cout << "将共享内存从操作系统中释放\n";
        shmctl(shmid, IPC_RMID, nullptr);
        unlink(filename.c_str());
    }
    int FileDirection()
    {
        return fd;
    }
    const char *ShnPtr()
    {
        return s;
    }

private:
    int shmid;
    int fd;
    char *s;
};

int main()
{
    Init init;
    // struct shmid_ds ds;
    // std::cout<<std::hex<<ds.shm_perm.__key<<"\n";
    // std::cout<<ds.shm_nattch<<"\n";

    while (true)
    {
        int code = 0;
        ssize_t n = read(init.FileDirection(), &code, sizeof(code));
        if (n > 0)
        {
            std::cout << "共享读取:" << init.ShnPtr() << "\n";
        }
        else if (n == 0)
        {
            break;
        }
        else
        {
            std::cerr << "读取错误,错误码:" << errno << "\n";
        }
    }
    return 0;
}

client

#include "Common.hpp"

int main()
{

    key_t key = GetKey();
    int shmid = CreatShmHelper(key, IPC_CREAT | 0644);
    char *s = static_cast<char *>(shmat(shmid, nullptr, 0));
    std::cout << "attach shm done\n";
    int fd = open(filename.c_str(), O_WRONLY);

    for (int c = 0; c < 26; c++)
    {
        s[c] = c + 'a';
        std::cout << "write:" << (char)c + 'a' << "done\n";
        sleep(1);
        int code = 1;
        write(fd, (char *)&code, sizeof(code));
    }
    // sleep(5);

    std::cout << "dettach shm done\n";

    shmdt(s);
    close(fd);
    return 0;
}

.h


#pragma once

#include <iostream>
#include <string>
#include <cstdlib>
#include <unistd.h>
#include <cassert>
#include <cstring>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <fcntl.h>

const std::string pathname = "/home/fuh_cs/Desktop/cpp_learning/linux/ShareMemory/Common.hpp";
const int proj_id = 0x234;
const int size = 4096;
const std::string filename = "fifo";
key_t GetKey()
{
    key_t key = ftok(pathname.c_str(), proj_id);
    if (key < 0)
    {
        std::cerr << "errno:" << errno << ",errnostring:" << std::strerror(errno) << std::endl;
        exit(-1);
    }
    return key;
}

int CreatShmHelper(key_t key, int flag)
{
    int shmid = shmget(key, size, flag); //共享内存也有权限也是需要设置的
    // EXCL保证创建时如果存在就会失败
    if (shmid < 0)
    {
        std::cerr << "errno:" << errno << ",errnostring:" << std::strerror(errno) << std::endl;
        exit(2);
    }
    return shmid;
}

int CreatShm(key_t key)
{
    return shmget(key, size, IPC_CREAT | IPC_EXCL | 0644); //共享内存也有权限也是需要设置的
}

int GetShm(key_t key)
{
    return shmget(key, size, IPC_CREAT); //共享内存也有权限也是需要设置的
}

//为1创建成功
bool MakeFifo()
{
    int n = mkfifo(filename.c_str(), 0666);
    if (n < 0)
    {
        std::cerr << "errno:" << errno << ",errstring" << strerror(errno) << std::endl;
        return 0;
    }
    std::cout << "mkfifo success..." << std::endl;
    return 1;
}

信号队列(Message Queue)

基于SYSTEM V的还有对应的信号队列(Message Queue),信号量
下面展示下Message Queue的简单使用代码。

基本的系统调用函数,在下面代码中有,具体使用可以通过man手册查询。

#pragma once

#include <iostream>
#include <string>
#include <cstdlib>
#include <unistd.h>
#include <cassert>
#include <cstring>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <fcntl.h>
#include <sys/msg.h>
key_t GetKey()
{
    key_t key = ftok(pathname.c_str(), proj_id);
    if (key < 0)
    {
        std::cerr << "errno:" << errno << ",errnostring:" << std::strerror(errno) << std::endl;
        exit(-1);
    }
    return key;
}

//消息队列也是存在于内核之中,不手动关闭的生命周期和内核一起

int main()
{
    key_t key = GetKey();
    int msgid =  msgget(key,IPC_CREAT |IPC_EXCL);
    std::cout<<"msgid:"<<msgid<<'\n';
    struct  msqid_ds ds;
    std::cout<<ds.__msg_cbytes<<'\n';
    std::cout<<ds.msg_perm.__key<<'\n';
    //用msgsend来发送消息
    //用msgrcv来接受消息

    msgctl(msgid,IPC_RMID,nullptr);
    //也可以  ipcrm -q msgid 在bash删除
    return 0;

}   

信号量(semaphore)

前置知识:

  • 公共资源:多个执行流看见的同一份资源
  • 多个执行流访问同一份资源,就存在并发访问
  • 为了解决并发访问公共资源的问题,导致的数据不一致、脏读等问题,需要保护资源
  • 因此引发出互斥和同步
  • 互斥:同一时刻,只能有一个执行流访问资源,加锁完成
  • 同步:多个执行流按照预定的先后次序来访问公共资源
  • 被保护起来的资源,称为临界资源
  • 访问临界资源的执行流(或者代码),称为临界区
    分析:
  • 本质是个计数器
  • 当进程需要访问公共资源,先获取信号量,再去访问资源。(相当于信号量是获取资源的凭证,有了信号量,就一定会有资源没获取信号量的进程,就阻塞等待
  • 信号量如果被获取了,就没了,没被获取,就全部都在。是只有两种状态,二元性的因此由此二元性,完成了互斥的功能
  • 不同的进程也需要看到同一份信号量,因此信号量也被纳入IPC体系,也就是说,信号量由操作系统提供
  • 我们知道SYSTEM V的资源是可以看见的,但是信号量是原子的(atomic,不可在分的),即使被进程竞争的访问,也只会出现要么获取了,要么没获取信号量。不会出现获取半个的情况。这种原子的获取操作称之为P操作。相对应原子的释放,称之为V操作。
    信号量系统调用

semget
semctl
semop

linux内核看SYSTEM V设计的共享内存等通信方式
一般来说:

  • 内核里面有一个ipc_id_ary,其是一个柔性数组,存储了一个size表示大小和指针数组,size表示指针数组的个数
  • 这个指针数组所存的指针类型是 **kern_ipc_perm***的指针类型
  • 由SYSTEM V标准下设计出来的共享内存,信号队列等,内核里面都是由一个自己类型的结构体去管理的(例如:msg_queue,就是管理信号队列的结构体,Shmid_Kernel, 就是管理共享内存的结构体)
  • 而这些结构体的第一个元素,都被设计成相似的结构体(信号队列是:q_perm,共享内存是:shm_perm),这些结构体所包含的元素类型,其实和 kern_ipc_perm的结构体元素类型是一样的。
  • 这就使得我们存储kern_ipc_perm的指针,即使存了msg_queue的结构体指针,只需要通过强制类型转换,也是可以访问msg_queue结构体
  • 这样对SYSYTEM V设计下的共享内存,信号队列等可以统一管理
    上面这种内核的设计:实际就是利用了C语言的特点,实现了多态,有种通过父类指针访问子类的类似

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

使用 Docker 部署 Answer 问答平台

1&#xff09;介绍 GitHub&#xff1a;https://github.com/apache/incubator-answer Answer 问答社区是在线平台&#xff0c;让用户提出问题并获得回答。用户可以发布问题并得到其他用户的详细答案、建议或信息。回答可以投票或评分&#xff0c;有助于确定有用的内容。标签和分…

笨办法学 Python3 第五版(预览)(二)

原文&#xff1a;Learn Python the Hard Way, 5th Edition (Early Release) 译者&#xff1a;飞龙 协议&#xff1a;CC BY-NC-SA 4.0 练习 19&#xff1a;函数和变量 现在你将把函数与你从之前练习中了解到的变量结合起来。如你所知&#xff0c;变量给数据片段一个名称&#x…

数据挖掘入门项目二手交易车价格预测之数据分析

文章目录 1. 相关库的引入2. 数据的加载3. 数据概况3.1 统计值查看3.2 查看数据类型 4. 判断缺失值4.1 统计每一列空值的数量4.2 可视化缺失值数量 5. 判断异常值5.1 异常值检测 6. 了解预测值的分布6.1 统计各预测值的分布6.2 总体分布概况6.2 查看预测值的具体频数6.3 查看sk…

基于ssm旅社客房收费管理系统+vue

目 录 目 录 I 摘 要 III ABSTRACT IV 1 绪论 1 1.1 课题背景 1 1.2 研究现状 1 1.3 研究内容 2 2 系统开发环境 3 2.1 vue技术 3 2.2 JAVA技术 3 2.3 MYSQL数据库 3 2.4 B/S结构 4 2.5 SSM框架技术 4 3 系统分析 5 3.1 可行性分析 5 3.1.1 技术可行性 5 3.1.2 操作可行性 5 3…

Git 如何上传本地的所有分支

Git 如何上传本地的所有分支 比如一个本地 git 仓库里定义了两个远程分支&#xff0c;一个名为 origin&#xff0c; 一个名为 web 现在本地有一些分支是 web 远程仓库没有的分支&#xff0c;如何将本地所有分支都推送到 web 这个远程仓库上呢 git push web --all

【ArcGIS超级工具】基于ArcPy的矢量数据批量自动化入库工具

最近&#xff0c;有很多做规划的朋友私信我&#xff0c;想让我帮忙开发一款ArcGIS自动化脚本工具&#xff0c;实现点、线、面的自动化入库操作&#xff0c;帮他们在平时的内业数据处理工作中减少机械式重复性的工作&#xff0c;提高工作效率。为此&#xff0c;我详细了解了下目…

车辆维护和燃油里程跟踪器LubeLogger

什么是 LubeLogger &#xff1f; LubeLogger 是一个自托管、开源、基于网络的车辆维护和燃油里程跟踪器。 LubeLogger 比较适合用来跟踪管理您的汽车的维修、保养、加油的历史记录&#xff0c;比用 Excel 强多了 官方提供了在线试用&#xff0c;可以使用用户名 test 和密码 123…

Covalent Network(CQT)将链下收入引入链上,在全新阶段开启 Token 回购

Covalent Network&#xff08;CQT&#xff09;&#xff0c;是 Web3 领域跨越 225 个链的领先数据索引服务商&#xff0c;通过统一 API 的方式提供结构化数据可用性服务&#xff0c;并正在成为 AI、DeFi、分析和治理等多样化需求的关键参与者。为了支持去中心化技术的采用&#…

Java快读

java的快读 (1)BufferedReader BufferedReader br new BufferedReader(new InputStreamReader(System.in));//定义对象String[] strings br.readLine().split(" ");//读取一行字符串&#xff0c;以空格为分隔转化为字符串数组int n Integer.parseInt(strings[0])…

NUC980 Linux(4.4.289)内核配置SD卡相关参数,设备启动后插入后SD卡没反应

现象:SD卡插入&#xff0c;设备识别不到 原因:1.内核配置问题&#xff1b;2.硬件没有接地&#xff1b; 解决: 1.内核配置 2.硬件上SD卡接地

Java面试——Redis

优质博文&#xff1a;IT-BLOG-CN 一、Redis 为什么那么快 【1】完全基于内存&#xff0c;绝大部分请求是纯粹的内存操作&#xff0c;非常快速。数据存在内存中。 【2】数据结构简单&#xff0c;对数据操作也简单&#xff0c;Redis中的数据结构是专门进行设计的。 【3】采用单线…

IEEE754标准的c语言阐述,以及几个浮点数常量

很多年前&#xff0c;调研过浮点数与整数之间的双射问题&#xff1a; win7 intel x64 cpu vs2013 c语言浮点数精度失真问题 最近重新学习了一下IEEE754标准&#xff0c;也许实际还有很多深刻问题没有被揭示。 计算机程序设计艺术&#xff0c;据说这本书中也有讨论。 参考&…

双周回顾#007 - 前端与后端

前端的问题不是难&#xff0c;而是它面对最终用户。只要用户的喜好和口味发生变化&#xff0c;前端就必须跟上。 这导致前端不得不快速变化&#xff0c;因为用户的口味正在越来越快地改变。 后端不需要面对最终用户&#xff0c;需要解决的都是一些经典的计算机科学问题&#…

8、Redis-Jedis、Lettuce和一个Demo

目录 一、Jedis 二、Lettuce 三、一个Demo Java集成Redis主要有3个方案&#xff1a;Jedis、Lettuce和Redisson。 其中&#xff0c;Jedis、Lettuce侧重于单例Redis&#xff0c;而Redisson侧重于分布式服务。 项目资源在文末 一、Jedis 1、创建SpringBoot项目 2、引入依赖 …

114.龙芯2k1000-pmon(13)- 串口如何用

本文是讲原理图的部分&#xff0c;跟pmon的关系不大&#xff01;&#xff01; 参考手册&#xff1a;《龙芯2K1000处理器用户手册.pdf》 刚刚看数据手册&#xff0c;让我是有点惊讶&#xff0c;但是也让我迷惑。&#xff08;一个串口复用为4个是啥意思&#xff1f;&#xff09;…

MYSQL的优化学习,从原理到索引,在到事务和锁机制,最后的主从复制、读写分离和分库分表

mysql的优化学习 为什么选择Mysql不选择其他的数据库&#xff1f;还有哪些&#xff0c;有什么区别&#xff1f; Mysql&#xff1a;开源免费版本可用&#xff0c;适用于中小型应用 Oracle&#xff1a;适用于大型企业级应用&#xff0c;复杂的业务场景和大量数据的处理&#xf…

ctf_show笔记篇(web入门---命令执行)

目录 命令执行 29&#xff1a;有很多种方法可以使用内联法例如system(cat ls)或者像它提示的一样echo nl fl""ag.php 30&#xff1a;这里与29题原理相同只不过多禁用了一个system和php####请通过29题举一反三 31&#xff1a;这一题有多种解法看自身理解&#xff0…

关于阿里云oss的冗余存储类型问题

不得不说一个问题&#xff0c;阿里云服务方便我们的同时 &#xff0c;他们的文档写的是真的差劲。 东一块&#xff0c;西一块的。非常不好系统的阅读&#xff0c;文档结构比较散。 关于阿里云oss的冗余存储类型问题&#xff0c;这里说一下&#xff0c;简直是个坑。 首页阿里…

基于springboot+vue的在线考试与学习交流平台

博主主页&#xff1a;猫头鹰源码 博主简介&#xff1a;Java领域优质创作者、CSDN博客专家、阿里云专家博主、公司架构师、全网粉丝5万、专注Java技术领域和毕业设计项目实战&#xff0c;欢迎高校老师\讲师\同行交流合作 ​主要内容&#xff1a;毕业设计(Javaweb项目|小程序|Pyt…

【C语言】熟悉文件基础知识

欢迎关注个人主页&#xff1a;逸狼 创造不易&#xff0c;可以点点赞吗~ 如有错误&#xff0c;欢迎指出~ 文件 为了数据持久化保存&#xff0c;使用文件&#xff0c;否则数据存储在内存中&#xff0c;程序退出&#xff0c;内存回收&#xff0c;数据就会丢失。 程序设计中&…