Linux之进程间通信(system V 共享内存)

news2025/1/20 3:38:58

目录

一、共享内存

1、基本原理

2、共享内存的创建

3、共享内存的释放

4、共享内存的关联

5、共享内存的去关联

6、查看IPC资源

二、完整通信代码

三、共享内存的特点

四、信号量

1、相关概念

2、信号量概念


进程间通信的本质就是让不同的进程看到同一个资源。而前面我们讲到了进程通信的最基础,最传统的方法——管道。我们知道了,无论是匿名管道还是命名管道,它们让不同进程看到同样的资源的方法,就是通过访问同样的文件来看到同样的资源。

进程间是相互独立的,因此进程的各种数据是存储在物理内存的不同区域的。那么,如果两个不同的进程能够访问到同一块内存空间,是不是就相当于看到了同样的资源。那么有没有这样的方法呢?答案是肯定的,system V中的共享内存就是这样的一种进程间通信的方法。

一、共享内存

1、基本原理

在学习了进程地址空间后,我们知道,在堆和栈之间有一块空间叫做共享区。

我们在物理内存中申请了一块空间,然后通过页表将这块空间,映射到不同进程的进程地址空间的共享区中, 然后再将映射到共享区的地址返回给各自的进程。那么,不同的进程就可以通过拿到虚拟地址,找到共享区,接着通过页表映射,找到物理内存。

这样,不同的进程就能够看到同一块空间了。这种工作方式就叫做共享内存。

如果想结束通信,我们直接取消进程和内存的映射关系,释放共享内存。

共享内存的提供者是操作系统,而操作系统中可能会有多个共享内存,所以操作系统必定需要将各个共享内存管理起来,怎么管理呢?当然是,先描述,再组织。

所以,共享内存包括了共享内存块和描述共享内存的内核数据结构。

2、共享内存的创建

shmget :作用是用来创建共享内存。返回值:shmid:共享内存的用户层标识符(类似于fd)。

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

int shmget(key_t key, size_t size, int shmflg);

RETURN VALUE
       On success, a valid shared memory identifier is returned.  On errir, -1 is returned, and errno is set to indicate the error

参数说明

size:创建的共享内存的大小。

shmflg:通常被设置成两个选项: IPC_CREAT、 IPC_EXCL

IPC_CREAT:共享内存不存在,则创建,如果存在则直接获取。IPC_EXCL:无法单独使用IPC_CREAT | IPC_EXCL:如果不存在就创建,如果存在就出错返回。

key:当一个进程将共享内存创建好了,而与之通信的另一个进程怎么保证看到的就是我创建的共享内存呢?key值就可以解决这个问题。

OS中一定会存在很多的共享内存,共享内存本质就是在内存中申请一块空间,而key能对其进行唯一标识。进程如果在内存中创建了共享内存,为了让共享内存在系统中保证唯一的,通过key来进行标识,所以只要让另一个进程也看到同一个key,那么不同的进程就能看到同一个共享内存。而key值就在描述共享内存的内核结构体struct shm中。

 那么,我们应该给key传入什么值呢:使用 ftok函数来形成key。

ftok:

#include <sys/types.h>
#include <sys/ipc.h>
key_t ftok(const char *pathname, int proj_id);

RETURN VALUE
 On success, the generated key_t value is returned.  On failure -1 is returned, with errno indicating the error as for the stat(2) system call.

3、共享内存的释放

因为共享内存是由操作系统提供并管理的,所以共享内存的生命周期是随操作系统,而不是随进程的。所以我们在使用完共享内存后,需要用户亲自删除。

1、使用命令删除:ipcrm -m shmid

2、使用代码函数删除

shmctl:作用是删除共享内存。参数:shmid:删除共享内存的标识符,cmd:控制种类(一般使用 IPC_RMID),buf:控制共享内存的数据结构,可以简单设置为空(nullptr)。

返回值:0表示成功,-1表示失败。

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

int shmctl(int shmid, int cmd, struct shmid_ds *buf);

4、共享内存的关联

shmat:作用是将共享内存与对应的进程关联起来。我们把进程和共享内存建立映射关系的操作称为挂接。

 #include <sys/types.h>
 #include <sys/shm.h>

 void *shmat(int shmid, const void *shmaddr, int shmflg);

参数:shmaddr:指定虚拟地址,但是我们并不了解,直接设置为nullptr即可;shmflg:读取权限,默认为0。

5、共享内存的去关联

shmdt:作用是将共享内存与对应的进程的关联解除。我们把取消进程和内存的映射关系的操作称为去关联。shmaddr:指定虚拟地址。

#include <sys/types.h>
#include <sys/shm.h>
int shmdt(const void *shmaddr);

RETURN VALUE
On  success  shmdt() returns 0; on error -1 is returned, and errno is set to indi‐cate the cause of the error.

6、查看IPC资源

1、查看共享内存:ipcs -m      bytes:共享内存大小(最后是页PAGE:4096的整数倍)    nattch:与该共享内存关联的进程个数

owner:共享内存的拥有者。 perms:拥有者对于该共享内存的权限

2、删除共享内存:ipcsrm -m  shmid

3、ipcs -q:查看消息队列

4、ipcs -s:查看信号量

二、完整通信代码

shmcoom.hpp

#pragma once

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

using namespace std;

#define SHM_SIZE 4096
#define PATH_NAME "/home/zdl"
#define PROJ_ID 0x66

server.cc

#include "shmcomm.hpp"

int main()
{
    // 1.创建key值
    key_t k = ftok(PATH_NAME, PROJ_ID);
    assert(k != -1);
    printf("key: %d\n", k);

    // 2.创建共享内存
    int shmid = shmget(k, SHM_SIZE, IPC_CREAT | IPC_EXCL | 0666);
    if (shmid == -1)
    {
        perror("shmget");
        exit(1);
    }

    // 3.关联共享内存
    char *shmaddr = (char *)shmat(shmid, nullptr, 0);
    if (shmaddr == nullptr)
    {
        perror("shmat");
        exit(2);
    }

    // 4.进程间通信
    for (;;)
    {
        printf("%s\n", shmaddr);
        if (strcmp(shmaddr, "quit") == 0)
            break;
        sleep(1);
    }

    // 5.去关联
    int m = shmdt(shmaddr);
    assert(m != -1);
    (void)m;

    // 6。删除共享内存
    int n = shmctl(shmid, IPC_RMID, nullptr);
    assert(n != -1);
    (void)n;

    return 0;
}

 client.cc

#include "shmcomm.hpp"

int main()
{
    // 获取key值
    key_t k = ftok(PATH_NAME, PROJ_ID);
    assert(k != -1);
    printf("key: %d\n", k);

    // 获取共享内存
    int shmid = shmget(k, SHM_SIZE, IPC_CREAT);
    if (shmid == -1)
    {
        perror("shmget");
        exit(1);
    }

    // 挂接
    char *shmaddr = (char *)shmat(shmid, nullptr, 0);
    if (shmaddr == nullptr)
    {
        perror("shmat");
        exit(2);
    }

    // 进程间通信
    int count = 0;
    while (count < 10)
    {
        snprintf(shmaddr, SHM_SIZE - 1, "hello server,我是另一个进程,正在和你通信,我的pid:%d count: %d",
                 getpid(), count++);
        sleep(1);
    }
    strcpy(shmaddr, "quit");

    // 去关联
    int m = shmdt(shmaddr);
    assert(m != -1);
    (void)m;

    return 0;
}

运行结果

首先,我们看到的是,两个进程的key值相同,且shmserver在输入了quit后,进程退出,shmclient也退出。 

三、共享内存的特点

优点:共享内存是所有进程间通信速度是最快的,因为共享内存是被双方所共享,只要写入对方就能立即看到,能大大减少数据的拷贝次数。

具体说明:

管道:我们知道,管道的本质是一个文件,所以我们必须像使用文件那样去使用管道,即我们想要通信就必须调用系统接口。具体如下图:

需要通过键盘输入到自己定义的缓冲区char buffer[],将数据拷贝到buffer中,调用write接口在把buffer里的数据拷贝到管道里,另一进程也有定义buffer缓冲区,调用read读取把数据从管道里读取到buffer里,再把数据显示到显示器上。

从上图,我们看到:使用管道要进行四次拷贝。 

共享内存:只需要两次拷贝。

缺点:与管道相比,共享内存没有访问控制,会出现数据不一致问题。即:读取的一方不会因为内存中没有数据或数据还没有写入而停止读取,写入的一方也不会考虑另一方是否读取,而会一直写入。如果想做到访问控制需要用到信号量或使用管道,对共享内存进行保护。

四、信号量

1、相关概念

1、临界资源:我们把多个进程(执行流)看到的公共资源称为临界资源或互斥资源。

2、临界区:我们把自己的进程中,访问临界资源的那部分代码称为临界区。

所以说,如果我们不加保护地访问了临界资源,那么多个执行流在运行时会互相干扰,而在非临界区多个执行流互不影响。

3、互斥:为了更好地保护临界资源,我们要让多个执行流,在任意时刻,只允许一个执行流进入临界资源,去访问临界资源,这种方式就称为互斥。

4、原子性:只要两态,要么不做、要么做完,没有中间状态。

2、信号量概念

引入:为了保护共享资源,我们必须保证进程间是互斥的。如果在同一时刻,不同的执行流要访问的是临界资源的不同区域,那么我们是允许它们同时进入共享内存进行资源访问的。比如,临界资源中有10个区域,那么我们最多就可以同时让10个执行流进入进行资源访问(这10个执行流能够并发地访问临界资源)。而当第11个执行流想要进入时,它就必须要等待,只有它需要的区域没有执行流在访问时,它才能去访问临界资源。

这时,我们就使用了信号量来对临界资源进行保护。

信号量:信号量的本质是一个计数器,通常用来表示临界资源中,资源数的多少。申请信号量实际上就是对临界资源的预定机制。信号量主要用于同步和互斥。

比如,还是上面的例子,信号量会是10,当有不同的10个执行流(访问临界资源的10个不同区域)想要访问临界资源时,它们会先申请信号量(信号量--),然后操作系统一定会在共享内存中为执行流预留好它想要的资源,可以随时访问。而当第11个执行流去申请信号量时,信号量为0,所以该执行流还无法访问临界资源。

只有当其中的一个执行流访问完临界资源,释放信号量后(信号量++),第11个执行流才能够申请信号量,去访问临界资源。

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

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

相关文章

vit细粒度图像分类(四)BT-Net学习笔记

1.摘要 为了改进在细粒度图像分类过程中类别差异难以提取的问题&#xff0c;本文提出了一种基于 Transformer 双线性网络的细粒度网络分类优化方法(BT-Net)。 首先&#xff0c;将输入图像通过不同卷积处理成不同长度的二维向量&#xff0c;然后&#xff0c;构建重复次数不同的…

JVM内存问题排查

本文又名《对JVM一窍不通的我快速开始排查应用内存问题》。主要系统性地整理了排查思路&#xff0c;为大家遇到问题时提供全面的排查流程&#xff0c;不至于漏掉某些可能性误入歧途浪费时间。 基本原则 由于本文的定位是Cookbook,基本原则是让整个流程能够系统化规范化的同时将…

少儿编程:让孩子在数字世界中自由翱翔

在这个信息爆炸的时代&#xff0c;计算机和互联网已经成为我们生活中不可或缺的一部分。从智能手机到智能家居&#xff0c;从网络游戏到在线教育&#xff0c;数字技术已经渗透到我们生活的方方面面。因此&#xff0c;掌握编程技能&#xff0c;就像学习阅读和写作一样&#xff0…

RNN预测下一句文本简单示例

根据句子前半句的内容推理出后半部分的内容&#xff0c;这样的任务可以使用循环的方式来实现。 RNN&#xff08;Recurrent Neural Network&#xff0c;循环神经网络&#xff09;是一种用于处理序列数据的强大神经网络模型。与传统的前馈神经网络不同&#xff0c;RNN能够通过其…

深度推荐模型之DeepFM

一、FM 背景&#xff1a;主要解决大规模稀疏数据下的特征组合遇到的问题&#xff1a;1. 二阶特征参数数据呈指数增长 怎么做的&#xff1a;对每个特征引入大小为k的隐向量&#xff0c;两两特征的权重值通过计算对应特征的隐向量内积 而特征之间计算点积的复杂度原本为 实际应…

橘子学ES实战操作01之集群模式如何实现快照备份

我们知道ES中通过副本在一定意义上实现了数据的备份和高可用。但是我们说万一副本数据丢失了&#xff0c;不小心被rm -f了&#xff0c;你就说逆天不逆天吧&#xff0c;此时要实现数据真正意义上的备份就要使用到快照机制&#xff0c;来把数据持久化备份起来&#xff0c;万一数据…

CAD-autolisp(三)——文件、对话框

目录 一、文件操作1.1 写文件1.2 读文件 二、对话框DCL2.1 初识对话框2.2 常用对话框界面2.2.1 复选框、列表框2.2.2 下拉框2.2.3 文字输入框、单选点框 2.3 Lisp对dcl的驱动2.4 对话框按钮实现拾取2.5 对话框加载图片2.5.1 幻灯片图片制作2.5.1 代码部分 一、文件操作 1.1 写…

TCP 三次握手 四次挥手以及滑动窗口

TCP 三次握手 简介&#xff1a; TCP 是一种面向连接的单播协议&#xff0c;在发送数据前&#xff0c;通信双方必须在彼此间建立一条连接。所谓的 “ 连接” &#xff0c;其实是客户端和服务器的内存里保存的一份关于对方的信息&#xff0c;如 IP 地址、端口号等。 TCP 可以…

CDSP认证:引领数据安全领域的权威之巅!

随着数据安全法和个人信息保护的施行&#xff0c;数据安全领域越来越受到重视。市场上涌现出众多数据安全相关的证书&#xff0c;而数据安全相关职位也成为了2023年最热门的职业之一。 &#x1f4a1;对于想要入门数据安全领域的小伙伴&#xff0c;我强烈推荐CDSP认证&#xff0…

【笔试常见编程题02】字符串中找出连续最长的数字串、数组中出现次数超过一半的数字、计算糖果、进制转换

1. 字符串中找出连续最长的数字串 读入一个字符串str&#xff0c;输出字符串str中的连续最长的数字串 输入描述 个测试输入包含1个测试用例&#xff0c;一个字符串str&#xff0c;长度不超过255。 输出描述 在一行内输出str中里连续最长的数字串。 示例 1 输入 abcd12345ed125s…

【前端】尚硅谷Node.js零基础视频教程笔记

文章目录 1.基础1.1. 基础命令1.2. 注意事项 2. Buffer&#xff08;缓冲器&#xff09;介绍与创建2.1 概念2.2 特点2.3 使用 3. fs模块(file system)3.1 文件写入3.2 文件读取 【前端目录贴】 参考视频: 尚硅谷Node.js零基础视频教程&#xff0c;nodejs新手到高手 1.基础 1.1.…

【MATLAB第92期】基于MATLAB的集成聚合多输入单输出回归预测方法(LSBoost、Bag)含自动优化超参数和特征敏感性分析功能

【MATLAB第92期】基于MATLAB的集成聚合多输入单输出回归预测方法&#xff08;LSBoost、Bag&#xff09;含自动优化超参数和特征敏感性分析功能 本文展示多种非常用多输入单输出回归预测模型效果。 注&#xff1a;每次运行数据训练集测试集为随机&#xff0c;故对比不严谨&…

搜维尔科技:【简报】元宇宙数字人赛道,《救食有道》!

在这个快速发展的数位时代里&#xff0c;本组相信透过制作融合虚拟人物 与 AI 智慧的创新宣传影片&#xff0c;定能为食物银行提高曝光率并让更多人 投身参与并落实减少食物浪费的行动&#xff0c;并与本组共同在生活中宣传食 物银行的理念 学校&#xff1a; 桃园市立中场商业高…

BTC的数据结构Merkle Tree和Hash pointer

比特币是一种基于区块链技术的加密数字货币&#xff0c;其底层数据结构被设计为分布式&#xff0c;去中心化的。它的核心数据结构是一个链式的区块&#xff0c;每个区块都包含了多笔交易记录和一个散列值。 比特币的底层数据结构使用了两个关键概念&#xff1a;hash pointer和…

【计算机网络】IP协议及动态路由算法

对应代码包传送门 IP协议及动态路由算法代码包及思科模拟器资料说明 相关文章 【计算机网络】中小型校园网构建与配置 【计算机网络】Socket通信编程与传输协议分析 【计算机网络】网络应用通信基本原理 目的&#xff1a; 1、掌握IP协议&#xff0c;IP分片&#xff0c;DH…

JCEF学习

JCEF重要概念 CEF CEF&#xff0c;全称Chromium Embedded Framework &#xff0c;它是基于Google Chromium的开源项目&#xff0c;它的目标是能够向第三方程序添加WEB浏览器功能&#xff0c;以及可以使用HTML、CSS和JS渲染界面。 CEF框架是由Marshall Greenblatt 在 2008 年创…

vue项目如何实现运行完项目就跳转到浏览器

在package.json中的启动命令中添加--open参数可以实现在Vue项目编译后自动打开浏览器的功能。 通过这样的设置&#xff0c;在运行npm run dev时&#xff0c;Vue项目编译完成后会自动打开默认浏览器并加载应用程序。

【问题解决】java-word转pdf踩坑

问题情境&#xff1a; 项目中采用word转pdf&#xff0c;最开始使用的pdf相关的apache的pdfbox和itextpdf&#xff0c;后面发现对于有图片背景的word转pdf的情景&#xff0c;word中的背景图会直接占用位置&#xff0c;导致正文不会正确落在背景图上。 解决方案&#xff1a; 采…

计算机网络——虚拟局域网+交换机基本配置实验

1.实验题目 虚拟局域网交换机基本配置实验 2.实验目的 1.了解交换机的作用 2.熟悉交换机的基本配置方法 3.熟悉Packet Tracer 7.0交换机模拟软件的使用 4.掌握在交换机上划分局域网&#xff0c;并且使用局域网与端口连接&#xff0c;检测信号传输 3.实验任务 1.了解交换…

C 变量

目录 1. C变量 2. C变量定义 2.1 变量初始化 2.2 C中的变量声明 3. C中的左值&#xff08;Lvalues&#xff09;和右值&#xff08;Rvalues&#xff09; 1. C变量 在C语言中&#xff0c;变量可以根据其类型分为以下几种基本类型&#xff1a; 整型变量&#xff1a;用…