Linux操作系统~基于systemV共享内存的进程间通信

news2024/12/24 11:40:29

目录

一.进程间通信有哪些方式

二.什么是systemV

三.共享内存-双向通信-大致实现思路

四.4个函数about共享内存

1.shmget函数-创建

 ftok函数

​编辑

 e.g.

ipcs/ipcrm指令(ipc资源会被回收吗)

2.shmctl函数-删除/释放

3.shmat函数-挂接

4.shmdt函数-去挂接

5.e.g.

五.总结/共享内存的特性

Q:为什么共享内存的大小建议是4KB整数倍?

六.共享内存的内核数据结构

Q:为什么我们看到的shmid是0,1,2,3,4递增的呢?


一.进程间通信有哪些方式

首先我们先来看一下进程间通信总共有哪些方式?

管道

  1. 匿名管道pipe
  2. 命名管道

System V IPC

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

POSIX IPC

  1. 消息队列
  2. 共享内存
  3. 信号量
  4. 互斥量
  5. 条件变量
  6. 读写锁

这里我讨论的是基于systemV共享内存的进程间通信


二.什么是systemV

        命名管道和匿名管道都是基于文件的通信方式,还有systemV标准的通信方式:

  1. OS层面专门为进程间通信设计的一个方案
  2. OS不相信任何用户,给用户提供功能的时候,采用系统调用
  3. System V进程间通信,一定会存在专门用来通信的接口(system call)

同一主机内的进程间通信方案——system V方案

注意进程间通信的本质:让不同进程看到同一份资源


三.共享内存-双向通信-大致实现思路

1.通过某种调用,在内存中创建一份内存空间

2.通过某种调用,让进程(参与通信的多个进程)“挂接”到这份新开辟的内存空间上(挂接就是通过页表映射到这片物理空间)-让不同的进程看到了同一份资源

3.不用共享内存后,我们需要去关联(去掉页表中的映射)

4.释放共享内存


四.4个函数about共享内存

1.shmget函数-创建

用于创建一个共享内存

  • key:用来唯一表示这个共享内存块(就像路径+文件名),需要用户自己设定。我们可以调用ftok函数,将自定义路径名和项目id,经过算法转换成一个唯一的key,这个key会设置到管理shm共享内存的数据结构中(注意这里的key和shmid是不一样的)。

注:

key_id实际上是一个有符号整数

key只是用来在系统层面进行标识唯一性的,不能用来管理shm

shmid是OS给用户返回的id,用来在用户层面对shm进行管理

        如果硬是要类比的话,shmid就像是文件描述符,key就像是struct_file的地址,OS是使用shmid对struct_file进行管理的

  • size:共享内存大小,建议是4KB的整数倍
  • shmflg:由九个权限标志构成,它们的用法和创建文件时使用的mode模式标志是一样的
  • 返回值:shmid,管理当前共享内存的id;成功返回一个非负整数,即该共享内存段的标识码;失败返回-1

这里常用两个权限标志:

  1. IPC_CREATE:不存在则创建:如果单独使用IPC_CREAT,或者flg为0:创建一个共享内存,如果创建的共享内存已经存在,则直接返回当前已经存在的共享内存。(基本不会空手而归)
  2. IPC_EXCL(单独使用没有意义)

 ftok函数

pathname:路径名

proj_id:自定义项目名ID

        路径名+项目名ID应该是唯一的,通过这两个参数生成的key也就是唯一的了,用于唯一标识共享内存块


 e.g.

1.生成同一个key

我们在server和client中使用同一个获取key值的方法ftok(并且传入相同的路径和项目ID),这样也就保证了两个不同的进程可以看到同一份资源。

int main()
{
    key_t key = ftok(PATH_NAME, PROJ_ID);
    if(key < 0){
        perror("ftok");
        return 1;
    }

    printf("%u\n", key);

    return 0;
}

2.创建一个共享内存

int shmid = shmget(key, SIZE, IPC_CREAT|IPC_EXCL|0666); //创建全新的shm,如果和系统已经存在ID冲突,我们出错返回,这个0666表示共享内存的权限

if(shmid < 0){
    perror("shmget");
    return 2;
}

ipcs/ipcrm指令(ipc资源会被回收吗)

ipcs可以看到systemV进程通信设施状态的指令(信号量,共享内存,消息队列都属于ipc资源)

  1. ipcs -s可以显示信号量的状态
  2. ipcs -m显示共享内存的状态
  3. ipcs -q显示消息队列的状态

        systemV的IPC资源,生命周期是随内核的,只能通过,程序员显示的释放(释放IPC资源的指令或者system call系统调用)或者是OS重启。文件,堆空间,进程退出的时候,这些资源都会被操作系统回收,但是IPC资源不会。

ipcrm指令:

用于删除ipc资源参数:

-M   以shmkey删除共享内存

-m   以shmid删除共享内存

-Q   以msgkey删除消息队列

-q    以msgid删除消息队列

-S    以semkey删除信号量

-s    以semid删除信号量


2.shmctl函数-删除/释放

  • shmid:前面shmget函数的返回值,描述共享内存数据结构中的shmid
  • cmd:cmd指令,我们这里只用删除指令IPC_RMID
  • *buf:操作系统用于描述当亲共享内存的数据结构

shmctl(shmid, IPC_RMID, NULL);

printf("key: 0x%x, shmid: %d -> shm delete success\n", key, shmid);

这样就能够删除shmid对应的共享内存(释放空间)


3.shmat函数-挂接

  • shmid:前面shmget函数的返回值,描述共享内存数据结构中的shmid
  • shmaddr:挂接到哪个位置,一般设为NULL(我们自己不太清楚,但是操作系统清楚)
  • shmflg:由九个权限标志构成,它们的用法和创建文件时使用的mode模式标志是一样的,我们一般传0
  • 返回值:成功返回创建共享内存的起始地址(虚拟地址),就像是malloc的返回值

char *mem = (char*)shmat(shmid, NULL, 0);

printf("attaches shm success\n");

这样就得到了一个指向共享内存的地址mem


 4.shmdt函数-去挂接

        并不是释放共享内存,而是删除共享内存和进程的关系(删除进程页表上进程虚拟地址和共享内存物理地址映射的页表项)

  • shmid:前面shmget函数的返回值,描述共享内存数据结构中的shmid
  • shmaddr:去挂接哪个位置,一般不设置,用缺省值(我们自己不太清楚,但是操作系统清楚)
  • shmflg:由九个权限标志构成,它们的用法和创建文件时使用的mode模式标志是一样的,我们一般不传

    shmdt(mem);

这样当前进程共享内存的挂接就解除了


 5.e.g.

这里模拟实现一个用共享队列进行通信的服务端和客户端。服务端读数据,客户端写数据

server.c

#include <unistd.h>
#include <stdio.h>
#include <sys/ipc.h>
#include <sys/shm.h>

#define PATH_NAME "./"  //用于ftok函数,表示当前的路径名,路径名+项目ID应该是唯一的,随之生成的key也就是唯一的
#define PROJ_ID 0x1234  //用于ftok函数,表示当前的路径名,路径名+项目ID应该是唯一的,随之生成的key也就是唯一的
#define SIZE 4097

int main()
{
    key_t key = ftok(PATH_NAME, PROJ_ID);
    if(key < 0){
        perror("ftok");
        return 1;
    }

    int shmid = shmget(key, SIZE, IPC_CREAT|IPC_EXCL|0666); 
    //创建全新的shm,加上了IPC_EXCL选项,如果和系统已经存在ID冲突,则出错返回,这个0666表示共享内存的权限
    if(shmid < 0){
        perror("shmget");
        return 2;
    }

    printf("key: %u, shmid: %d\n", key, shmid);
    //sleep(1);

    char *mem = (char*)shmat(shmid, NULL, 0);  //将共享内存挂接到当前进程
    printf("attaches shm success\n");
    //sleep(15);

    //此处进行通信逻辑,直接像数组一样对共享内存进行操作即可,服务端进行读操作
    while(1){
        sleep(1);
        printf("%s\n", mem); //server 认为共享内存里面放的是一个长字符串 
    }

    shmdt(mem);  //去挂接

    printf("detaches shm success\n");
    //sleep(5);

    shmctl(shmid, IPC_RMID, NULL);  //释放共享内存
    printf("key: 0x%x, shmid: %d -> shm delete success\n", key, shmid);

    //sleep(10);

    return 0;
}

client.c

#include <unistd.h>
#include <stdio.h>
#include <sys/ipc.h>
#include <sys/shm.h>

#define PATH_NAME "./"
#define PROJ_ID 0x1234
#define SIZE 4097
int main()
{
    key_t key = ftok(PATH_NAME, PROJ_ID);
    if(key < 0){
        perror("ftok");
        return 1;
    }

    printf("%u\n", key);

    //client这里只需要进行获取即可,只需要加上IPC_CREATE选项即可
    int shmid = shmget(key, SIZE, IPC_CREAT);
    if(shmid < 0){
        perror("shmget");
        return 1;
    }
    char *mem = (char*)shmat(shmid, NULL, 0);
    //sleep(5);
    printf("client process attaches success!\n");

    //此处进行通信逻辑,直接像数组一样对共享内存进行操作即可,客户端进行写操作,每隔两秒,多写入一个字母
    char c = 'A';
    while(c <= 'Z'){
        mem[c-'A'] = c;
        c++;
        mem[c-'A'] = 0;
        sleep(2);
    }


    shmdt(mem);
    //sleep(5);
    printf("client process detaches success\n");

    //client不需要释放共享内存的空间,由服务端释放

    return 0;
}

运行结果:我们2s写一次,1s读一次,我们读的时候并没有把数据拿走,所以会读两次

        这里server会一直读取共享内存中的内容,即使共享内存中没有内容可以读,所以不会退出while循环,也不会执行去挂接和释放共享内存的操作。(需要后期自己用ipcrm指令删除,也可以考虑在循环中定义一个计数器,几s后自动退出循环,进行去挂接,释放共享内存的操作)

        这里我们读的时候直接读的是mem这个连续的地址空间,和前面管道中的不同,管道是基于文件的,在一次打开中,读过的内容不会再读了。管道中的内容都不会像普通文件一样保存在磁盘中,读取过的数据就会失效,不能重复读取(包括匿名管道和命名管道)


五.总结/共享内存的特性

1.我们在使用共享内存的时候,没有调用类似pipe or fifo中的read过样的接口,因为匿名管道和命名管道都是基于文件的,read和write也是基于文件的,本质是将数据从内核拷贝到用户,或者从用户拷贝到内核。共享内存并不是基于文件的。

2.共享内存一旦建立好并映射进自己进程的地址空间,进行通信的进程就可以直接看到该共享内存,就如同自己malloc的空间一般,不需要任何系统调用接口,直接当数组用都行。所以共享内存是所有的进程间通信中速度最快的!(共享内存最多拷贝一次,管道可能要4次或者更多)

3.当client没有写入,甚至没有启动的时候,server端也会直接读取shm,不会像管道那样等待client写入。共享内存不提供任何司步或者互斥儿制,需要程序员自行保证数据的安全!

Q:为什么共享内存的大小建议是4KB整数倍?

        共享内存在内核中申请的基本单位是页,内存页,大小为4KB,内核给你的大小是4KB的整数倍。比如我申请4097个字节->内核会给你4096byte*2的空间


六.共享内存的内核数据结构

用户层:是操作系统层的子集

ipc_perm中存放的uid是用户id,gid是组id,key就是我们传入的用于唯一标识共享内存块的id。

除此之外还有当前共享内存的一些基本信息

在内核中,所有的ipc资源都是通过数组组织起来的,这三种资源结构体都不一样,怎么放在同一个数组中呢?原因就是下面的ipc_perm

SystemV三种通信方式的ipc资源,描述ipc资源的数据结构很相似,而且第一个成员都是ipc_perm,这个ipc_perm是完全相同的。

这个数组实际上是一个ipc_perm*类型的数组,也就是一个指针数组,然后根据对应的类型强制类型转换。(用C语言实现的切片技术

Q:为什么我们看到的shmid是0,1,2,3,4递增的呢?

        shmid为什么是0,1,2,3,4递增的呢?因为说白了这个shmid就是ipc_perm*数组的下标,共享内存是ipc资源,所有ipc资源都是用一个ipc_perm*数组管理的,shmid就是直接取了这个数组的下标。

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

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

相关文章

汽车以太网简史

一、为啥需要新车载总线 在一个行业中&#xff0c;当一种新技术被开发和启用时&#xff0c;影响该技术成功的因素有很多。其中最重要的是该技术带来的益处以及自身成本。 第一个吃螃蟹的 2004年宝马决定从2008年起在其开始量产&#xff08;SOP&#xff09;的汽车中引入一个中…

c++ 指针

目录 1.指针的基本概念 2.定义指针&#xff0c;使用指针 3.指针所占的内存空间 4.空指针和野指针 4.1空指针 4.2野指针 5.const修饰指针 5.1常量指针 5.2指针常量 5.3const既修饰指针又修饰常量 6.指针和数组,利用指针访问数组 6.1概述 6.2使用数组名和下表访问 6.3使用…

【LIN总线测试】——LIN主节点数据链路层测试

系列文章目录 &#x1f4d2;【LIN总线测试】——LIN主节点物理层测试 &#x1f4d2;【LIN总线测试】——LIN从节点物理层测试 &#x1f4d2;【LIN总线测试】——LIN主节点数据链路层测试 &#x1f4d2;【LIN总线测试】——LIN主节点网络管理测试 &#x1f4d2;【LIN总线测试】…

Docker consul的容器服务更新与发现

1&#xff09;什么是服务注册与发现 服务注册与发现是微服务架构中不可或缺的重要组件。起初服务都是单节点的&#xff0c;不保障高可用性&#xff0c;也不考虑服务的压力承载&#xff0c;服务之间调用单纯的通过接口访问。直到后来出现了多个节点的分布式架构&#xff0c;起初…

一软一硬:记录我的工作电脑两次出现性能问题的分析思路和解决过程

作为一个程序员&#xff0c;每天工作中糟心的事情之一&#xff0c;莫过于自己用来编码的计算机&#xff0c;运行速度忽然变得奇慢无比。尤其像我这种年过四旬仍然在一线从事编码工作的老程序员来说&#xff0c;只有靠不断提高单位时间的产出效率&#xff0c;来弥补和年轻程序员…

网课搜题接口

网课搜题接口 本平台优点&#xff1a; 多题库查题、独立后台、响应速度快、全网平台可查、功能最全&#xff01; 1.想要给自己的公众号获得查题接口&#xff0c;只需要两步&#xff01; 2.题库&#xff1a; 题库&#xff1a;题库后台&#xff08;点击跳转&#xff09; 题库…

【leetcode】 盛最多水的容器

一、题目描述 给定一个长度为 n 的整数数组 height 。有 n 条垂线&#xff0c;第 i 条线的两个端点是 (i, 0) 和 (i, height[i]) 。 找出其中的两条线&#xff0c;使得它们与 x 轴共同构成的容器可以容纳最多的水。 返回容器可以储存的最大水量。 说明&#xff1a;你不能倾…

移动通信:数字调制技术(BPSK, DPSK, QPSK, Π/4 QPSK,BFSK, MSK, GMSK, M-ary)学习笔记

文章目录Factors That Influence the Choice of Digital ModulationBandwidth and Power Spectral Density of Digital SignalsPulse Shaping TechniquesGeometric Representation of Modulation SignalsLinear Modulation TechniquesBPSKDifferential Phase Shift Keying (DPS…

软件定义汽车的关键—车载操作系统

文章目录 前言一、车载系统是什么&#xff1f;二、车载系统在智能汽车中的应用总结前言 智能网联大环境变革情况下&#xff0c;软件定义汽车理念已成行业共识。 传统分布式E/E架构因如下原因&#xff1a; 计算能力不足&#xff08;车规芯片&#xff09;&#xff1b;通讯带宽不…

树和二叉树

目录 1.树的概念及结构 1.1树的概念 1.2.树的表示 1.2.1孩子兄弟表示法 2.2双亲表示法 1.3二叉树在实际中的应用 2.二叉树的概念及结构 2.1二叉树的概念 2.2特殊的二叉树 2.3二叉树的性质 2.4二叉树的实现及其的一些接口&#xff08;链式&#xff09; 2.4.1二叉树的…

配置 4G 模块为WAN口上网

配置 4G 模块为WAN口上网 将Linux板卡配置为交换机&#xff0c;提供类似路由器的上网功能。这里以4G网卡作为WAN进行上网。 1. 配置4G模块为默认网关 先配置好4G模块为默认路由网关。 这里需要先将4G模块配置为路由模式。 断开板卡的有线和无限网络连接&#xff0c;使用pi…

Redis配置与优化

1 关系数据库与非关系型数据库概述 1.1 关系型数据库 一个结构化的数据库&#xff0c;创建在关系模型&#xff08;二维表格模型&#xff09;基础上 一般面向于记录 SQL 语句&#xff08;标准数据查询语言&#xff09;就是一种基于关系型数据库的语言 用于执行对关系型数据库中…

后渗透之流量转发实验

目录 一、实验项目名称 二、实验目的 三、实验内容 四、实验环境 五、实验步骤 六、实验结果 七、实验总结 一、实验项目名称 后渗透之流量转发实验 二、实验目的 1.掌握ssh进行流量转发的方法 2.掌握proxychains代理软件的使用方法 3.掌握Burpsuite工具的全局代…

Java 基础之锁

Java 基础之线程_禽兽先生不禽兽的博客-CSDN博客 上一篇博客中记录了线程的一些概念&#xff0c;那多线程既然能与人方便必然也会带来一些问题&#xff0c;这些问题主要与线程的三大特性有关&#xff0c;Java 的一些关键字和锁机制&#xff0c;可以帮助我们解决这些问题。 一…

如何高效管理自己的电脑?文件再多也不乱!

视频教程&#xff1a;https://www.bilibili.com/video/BV18M41167jd/?share_sourcecopy_web&vd_source58093f138338062a30d9fe854824a90f 如何高效管理自己的电脑?文件再多也不乱!1. 盘符管理C盘只装系统软件&#xff0c;其他软件装到D盘绿色软件就是解压就能用&#xff…

[go学习笔记.第十八章.数据结构] 1.基本介绍,稀疏数组,队列(数组实现),链表

一.基本介绍 1.数据结构(算法)的介绍 (1).数据结构是一门研究算法的学科&#xff0c;自从有了编程语言也就有了数据结构,学好数据结构可以编写出更加漂亮&#xff0c;更加有效率的代码 (2).要学习好数据结构就要多多考虑如何将生活中遇到的问题用程序去实现解决 (3).程序&…

一 H5游戏的种类

游戏类型&#xff08;各举一个例子&#xff09;&#xff1a; 休闲益智:开心消消乐 角色扮演:阴阳师 动作冒险:神庙逃亡 棋牌游戏:欢乐斗地主 体育竞技:最强NBA 射击游戏:绝地求生 音乐游戏:节奏大师 模拟经营:模拟城市 策略游戏:植物大战僵尸 塔防游戏:保卫萝卜 游戏付费方式…

R语言最优聚类数目k改进kmean聚类算法

在本文中&#xff0c;我们将探讨应用聚类算法&#xff08;例如k均值和期望最大化&#xff09;来确定集群的最佳数量时所遇到的问题之一。 最近我们被客户要求撰写关于聚类的研究报告&#xff0c;包括一些图形和统计输出。从数据集本身来看&#xff0c;确定集群数量的最佳值的问…

FX粒子(Niagara系统)、潮湿贴花——简单雨和雨后潮湿

雨&#xff1a; 1、新建粒子&#xff0c;选择Fountain 喷泉模板 2、删除不用模块 3、添加box location并设置其大小&#xff0c;设置Spawn Rate使粒子数变多&#xff0c;这个参数也是后面用来控制开关雨效果的参数。 4、设置粒子形状&#xff0c;设置完粒子方向不对&#xff0…

11.28~12.4日学习总结

星期一&#xff0c;星期二&#xff1a; 把项目中关于评论模块的内容全部写完了&#xff0c;然后处理了一下逻辑。 星期二&#xff0c;星期三&#xff1a;开始学习webSocket的相关概念。 关于webSocket,其实和java的Socket原理相差不大&#xff0c;也是要建立一个通道进行信息…