linux的通信方案(SYSTEM V)

news2024/9/30 21:22:29

文章目录

      • 共享内存(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语言的特点,实现了多态,有种通过父类指针访问子类的类似

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

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

相关文章

图论 - 最小生成树(Prime、Kruskal)

文章目录 前言Part 1&#xff1a;Prim算法求最小生成树1.题目描述输入格式输出格式数据范围输入样例输出样例 2.算法 Part 2&#xff1a;Kruskal算法求最小生成树1.题目描述输入格式输出格式数据范围输入样例输出样例 2.算法 前言 本篇博客介绍两种求最小生成树的方法&#xff…

使用OpenCV和mediapipe实现手部信息抓取

目录 运行效果 挨行解读 &#xff08;1&#xff09;初始化MediaPipe Hand模块 &#xff08;2&#xff09;打开摄像头 &#xff08;3&#xff09;初始化计时器 &#xff08;4&#xff09;开始程序主题部分 &#xff08;5&#xff09;读取视频帧 &#xff08;6&#xff09…

c++基础知识补充5

隐式类型转换型初始化&#xff1a;如A a1, 与全局类没有区别&#xff0c;只是受主类的类域限制&#xff0c;内部类天生是主类的友元 采用匿名构造如A()代替A aa可以加速编译器优化 接收建议新创建一个对象

基于springboot+vue的科研工作量管理系统

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

大模型(LLM)的token学习记录-I

文章目录 基本概念什么是token?如何理解token的长度&#xff1f;使用openai tokenizer 观察token的相关信息open ai的模型 token的特点token如何映射到数值&#xff1f;token级操作&#xff1a;精确地操作文本token 设计的局限性 tokenizationtoken 数量对LLM 的影响训练模型参…

c语言--qsort函数(详解)

目录 一、定义二、用qsort函数排序整型数据三、用qsort排序结构数据四、qsort函数的模拟实现 一、定义 二、用qsort函数排序整型数据 #include<stdio.h> scanf_S(int *arr,int sz) {for (int i 0; i < sz; i){scanf("%d", &arr[i]);} } int int_cmp(c…

【GitHub】修改默认分支

GitHub的默认分支为main&#xff0c;但我们常常习惯使用master作为默认分支&#xff0c;那在GitHub上如何将master修改为默认分支呢&#xff1f; 全局修改 点击头像&#xff0c;选择菜单栏中的设置 输入master作为默认分支&#xff0c;然后执行updating即可&#xff01; 单项…

【Java数据结构】——二叉搜索树

目录 &#x1f388;概念 &#x1f388;操作-查找 &#x1f388;操作-插入 &#x1f388;操作-删除&#xff08;难点&#xff09; &#x1f388;概念 二叉搜索树又称二叉排序树&#xff0c;它或者是一棵空树&#xff0c;或者是具有以下性质的二叉树 : 若它的左子树不为空&a…

【Qt学习笔记】(四)Qt窗口

Qt窗口 1 菜单栏1.1 创建菜单栏1.2 在菜单栏中添加菜单1.3 创建菜单项1.4 在菜单项之间添加分割线1.5 给菜单项添加槽函数1.6 给菜单项添加快捷键 2 工具栏2.1 创建工具栏2.2 设置停靠位置2.3 设置浮动属性2.4 设置移动属性2.5 添加 Action 3 状态栏3.1 状态栏的创建3.2 在状态…

Matlab 机器人工具箱 动力学

文章目录 R.dynR.fdynR.accelR.rneR.gravloadR.inertiaR.coriolisR.payload官网:Robotics Toolbox - Peter Corke R.dyn 查看动力学参数 mdl_puma560; p560.dyn;%查看puma560机械臂所有连杆的动力学参数 p560.dyn(2);%查看puma560机械臂第二连杆的动力学参数 p560.links(2)…

mac命令行下计算文件SHA-256散列值

源起 从国内的第三方网站下载了Android sutiod的zip包下载地址&#xff0c;为了安全起见还是得跟Android官网上的对应的zip包的SHA值做下对比。以前是经常使用md5命令的&#xff0c;所以理论在命令行下应该是有对应的命令行工具可以计算SHA值的。后来搜索到可以用 shasum命令来…

Java中线程安全的集合类

在先前的文章中我们已经讲过了原子类(线程安全的基本类型&#xff0c;基于CAS实现)&#xff0c;详见常见锁策略&#xff0c;synchronized内部原理以及CAS-CSDN博客 &#xff0c;我们在来讲一下集合类&#xff0c;在原来的集合类&#xff0c;大多数是线程不安全的&#xff0c;虽…

深入了解 Android 中的 RelativeLayout 布局

RelativeLayout 是 Android 中常用的布局之一&#xff0c;它允许开发者基于子视图之间的相对位置来排列界面元素。在这篇博客中&#xff0c;我们将详细介绍 RelativeLayout 的各种属性&#xff0c;并提供代码示例和解释。 第一个示例 <RelativeLayoutandroid:layout_width…

【数据分享】2001~2023年中国区域MOD17A3HGF GPP数据

各位同学们好&#xff0c;今天和大伙儿分享的是2001~2023年中国区域MOD17A3HGF GPP数据。如果大家有下载处理数据等方面的问题&#xff0c;您可以私信或评论。 Running, S., M. Zhao. <i>MODIS/Terra Net Primary Production Gap-Filled Yearly L4 Global 500m SIN Grid…

标准库`random`函数大全:探索Python中的随机数生成【第107篇—`random`函数大全】

标准库random函数大全&#xff1a;探索Python中的随机数生成 随机数在计算机科学和数据科学领域中扮演着重要角色&#xff0c;Python的标准库中提供了random模块&#xff0c;用于生成各种随机数。本篇博客将深入探讨random模块的各种函数&#xff0c;以及它们的应用场景和代码…

Leetcoder Day35| 动态规划part02

62.不同路径 一个机器人位于一个 m x n 网格的左上角 &#xff08;起始点在下图中标记为 “Start” &#xff09;。 机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角&#xff08;在下图中标记为 “Finish” &#xff09;。 问总共有多少条不同的路径&#xff…

如何在2.2.1版Aduino IDE中开发ESP32

ESP32芯片集成了WIFI和蓝牙&#xff0c;而且关于生态也很不错&#xff0c;越来越多的学习者和开发者选择此类芯片&#xff0c;而不像用keil开发STM32或者51一样&#xff0c;ESP32虽然也有官方的ESP32-IDF开发软甲&#xff0c;但是经过我个人的实操体验&#xff0c;不适合小白或…

基于Springboot的人事管理系统 (有报告)。Javaee项目,springboot项目。

演示视频&#xff1a; 基于Springboot的人事管理系统 &#xff08;有报告&#xff09;。Javaee项目&#xff0c;springboot项目。 项目介绍&#xff1a; 采用M&#xff08;model&#xff09;V&#xff08;view&#xff09;C&#xff08;controller&#xff09;三层体系结构&am…

ctf_show笔记篇(web入门---文件包含)

目录 文件包含 78-79&#xff1a;最基础的文件包含&#xff0c;使用伪协议&#xff0c;大小写绕过或者通配符绕过&#xff0c;再或者使用其他方法 ​编辑80-81&#xff1a;可采用日志文件绕过或者大小写绕过&#xff08;81只能日志文件绕过&#xff09; ####80-86&#xff1…

linux系统---nginx(3)核心配置指令及调优

目录 Nginx 核心配置指令 一、Nginx配置文件详解 1、配置文件目录 2、配置文件结构 二、调优 1、在全局域进行的调优 1.1线程池指令 1.2 工作进程数指令 2.1 工作进程并非数指令 2.2 事件处理机制选择指令 2.3 互斥锁指令 3、在http指令域的调优 3.1 Nginx端口监听…