Linux—进程间通信之System V共享内存

news2024/11/29 16:36:03

在这里插入图片描述

目录

  • 简介
  • System V共享内存
    • 特点及用法
  • 共享内存的创建
  • 共享内存的关联与去关联
  • 共享内存的删除
  • 共享内存通信代码实现
  • 总结

简介

System V共享内存是一种在Unix-like系统中广泛使用的共享内存机制。它是基于System V IPC(Inter-Process Communication,进程间通信)机制的一部分。System V共享内存提供了一块共享的内存区域,可以被多个进程同时访问。这种共享内存区域可以用于高效地在进程之间传递大量的数据,而无需进行数据的复制。相比较于命名管道和匿名管道的管道通信,共享内存区是最快的IPC形式。因为一旦这样的内存映射到共享它的进程地址空间,这些进程间数据传递就不再涉及到
内核,也就是说进程不再通过执行进入内核的系统调用来传递彼此的数据。

System V共享内存

共享内存的实现是通过将一块创建好的内存区域映射到多个进程的地址空间中,实现进程间的数据共享。共享内存的一个主要优势是用户进程可以直接在其地址空间中对共享内存中的数据进行操作,而无需频繁地进入内核空间。这与其他进程间通信机制(如管道)不同,它们通常需要在内核空间执行系统调用来进行数据传输。

当进程将共享内存连接到其地址空间时,操作系统会将共享内存区域的内容映射到进程的虚拟地址空间中的一个地址范围。这意味着进程可以像访问普通内存一样直接读写这个地址范围中的数据,而无需进入内核空间执行系统调用。尽管用户进程可以直接操作共享内存中的数据,但在多进程共享数据时,通常需要使用同步机制(如信号量或互斥锁)来协调进程之间的访问,以避免数据竞争和不一致性。这些同步机制可能需要进入内核空间执行,但这通常是在读写数据之前或之后的较少的操作。

在这里插入图片描述

特点及用法

  1. 创建和获取共享内存: 使用System V共享内存,首先需要使用shmget系统调用创建或获取一个共享内存标识符(ID)。该标识符用于标识共享内存区域。

  2. 连接和分离共享内存: 一旦获得共享内存标识符,可以使用shmat系统调用将共享内存连接到进程的地址空间,使进程可以访问共享内存中的数据。使用shmdt系统调用可以将共享内存从进程的地址空间中分离。

  3. 权限和控制: System V共享内存可以通过权限和控制来限制访问。

  4. 共享内存操作: 一旦共享内存连接到进程的地址空间,进程可以直接读取和写入共享内存中的数据。由于多个进程可以同时访问共享内存,因此需要使用同步机制(如信号量)来确保数据的一致性和避免竞态条件。

  5. 删除共享内存: 当不再需要共享内存时,可以使用shmctl系统调用删除共享内存区域。这将释放共享内存并使其不再可用。

共享内存的创建

创建共享内存需要用到系统调用接口shmget函数,其原型为int shmget(key_t key, size_t size, int shmflg);。其中三个参数作用如下:

  • key_t key:这是一个键值,用于唯一标识共享内存段。多个进程可以使用相同的键值来访问同一个共享内存段。

  • size_t size:指定共享内存段的大小,以字节为单位。这个参数用来确定需要多少内存来存储共享数据。

  • int shmflg:这是一个标志参数,用于指定共享内存的创建和访问权限,以及其他选项。

shmfig常用的标志包括:

IPC_CREAT:如果共享内存不存在,则创建新的共享内存段。
IPC_EXCL:与IPC_CREAT一起使用,如果共享内存已经存在,则返回错误。
例如 IPC_PRIVATE 或 IPC_CREAT | 0666,用于控制共享内存的访问权限。

在这里插入图片描述

使用shmget函数创建共享内存完成后返回一个整数值,通常表示共享内存的标识符。这个标识符用于在后续的共享内存操作中标识和访问特定的共享内存段。如果成功创建新的共享内存段或访问现有的共享内存段,则返回共享内存段的标识符,是一个非负整数。如果出现错误,则返回-1,并设置错误码。

在这里插入图片描述

对于创建共享内存的key值,按理来说是可以随便给的,只要保证你想新创建的这个共享内存的key值与之前的已存在的不冲突即可。因此,共享内存的key值一般用ftok系统调用接口来生成。ftok函数是一个用于生成System V IPC键值的函数。它通常与共享内存、消息队列和信号量等进程间通信机制一起使用,以便多个进程可以识别和访问共享资源。

在这里插入图片描述

ftok函数有两个参数,其中pathname是一个指向路径名的字符串,通常用于关联键值。ftok函数会根据该路径名生成唯一的键值。通常,你可以指定一个存在的文件的路径作为参数,以确保不同进程使用相同路径名时生成相同的键值。proj_id是一个用户定义的项目标识符,通常是一个整数。可以为不同的共享资源(例如不同的共享内存)使用不同的proj_id,以确保它们拥有不同的键值。proj_id的范围通常是0到255。

在这里插入图片描述

ftok函数使用这两个参数来生成一个32位的键值(key_t类型)。这个键值在后续的IPC函数中用于识别和访问共享资源。需要注意的是,ftok生成的键值不是全局唯一的,而是在给定的pathname和proj_id组合下唯一。

共享内存的关联与去关联

在创建了共享内存后,还需要将共享内存链接到进程的地址空间中来,在通信结束后还需要将共享内存从自己地址空间中去除关联,因此还需要用到shmatshmdt系统调用接口。

在这里插入图片描述

shmat函数用于将共享内存段附加到进程的地址空间,使得进程可以直接访问共享内存中的数据。他有三个参数,其作用分别为:

  • int shmid:共享内存段的标识符,通常是由shmget函数返回的值。

  • const void *shmaddr:指定共享内存段连接到进程地址空间的地址,通常设置为NULL,由系统选择合适的地址。

  • int shmflg:附加标志,可以是0或包含一些选项的标志,例如SHM_RDONLY表示只读模式。设置为0表示使用默认选项。

在这里插入图片描述

shmat函数返回一个void*指针,指向共享内存段在进程地址空间中的起始地址。如果附加失败,返回值是(void *)-1并设置错误码。


shmdt函数用于将共享内存段从进程的地址空间中分离,即不再让进程能够访问共享内存段中的数据。shmdt只有一个参数shmaddr,这是一个指向共享内存在进程地址空间中的起始地址的指针,通常是由shmat函数返回的地址。shmdt函数如果分离共享内存成功返回0,否则返回-1并设置错误码。

将共享内存段与当前进程脱离不等于删除共享内存段

在这里插入图片描述

共享内存的删除

在进程通信结束,所有进程都与共享内存没有关联时,就需要将共享内存进行删除。删除共享内存需要使用系统调用接口shmctl函数。shmctl函数是用于控制(管理)共享内存的函数,它允许你对共享内存执行各种操作,如删除、获取信息、设置权限等。

在这里插入图片描述
shmctl函数有三个参数,shmid表示共享内存段的标识符,通常是由 shmget 函数返回的值,用于唯一标识一个共享内存段。cmd为命令参数,用于指定要执行的操作。可以取以下常用值:

IPC_STAT:获取共享内存的状态信息,并将结果存储在 struct shmid_ds 结构体中。
IPC_SET:设置共享内存的状态信息,需要提供一个填充好的 struct shmid_ds 结构体。
IPC_RMID:删除共享内存段。

struct shmid_ds *buf是一个指向 struct shmid_ds 结构体的指针,用于存储共享内存的状态信息(当 cmd 参数是 IPC_STAT 时),或者用于提供共享内存的新状态信息(当 cmd 参数是 IPC_SET 时)。当我们不关心状态信息时可以设置为nullptr。

在这里插入图片描述

shmctl 函数的返回值在不同的操作情况下有不同的含义:

  • 当 cmd 参数是 IPC_STAT 时,shmctl 返回0表示成功,并将共享内存的状态信息存储在提供的 struct shmid_ds 结构体中。这时,你可以通过检查 struct shmid_ds 结构体中的字段来获取有关共享内存段的信息。

  • 当 cmd 参数是 IPC_SET 时,shmctl 返回0表示成功,并且新的状态信息已经应用到共享内存段。

  • 当 cmd 参数是 IPC_RMID 时,shmctl 返回0表示成功删除了共享内存段。此时,共享内存段将被销毁,不再可用。

  • 如果 cmd 参数无效或操作失败,shmctl 返回-1,并设置全局变量 errno 以指示错误的类型。你可以使用 perror 函数或 strerror 函数来获取关于错误的更多信息。

总之,shmctl 函数的返回值主要用于指示操作是否成功,成功时返回0,失败时返回-1,并且错误信息可以通过 errno 获取。

共享内存通信代码实现

简单设计两个进程Server和client,让Server创建共享内存并负责接收Client发送来的数据,Client直接连接Server创建好的共享内存,然后向Server发送数据。最后Client发送完成后,去掉与共享内存的关联,然后退出。Server在接受完成后也去掉关联并删除共享内存。这样就完成了一次共享内存的通信过程。于是就可以写出这样的代码:

//comm.hpp
#include <iostream>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/types.h>
#include <cstring>
#include <cassert>
#include <string>
#include <sys/stat.h>

//共享内存不随进程,随os
using namespace std;

#define pathname "."
#define proj_id 0x454

const int gsize = 4096;

key_t getKey()
{
    key_t k = ftok(pathname, proj_id);
    if(k == -1)
    {
        cout << errno << " : " << strerror(errno) << endl;
        exit(1);
    }
    return k;
}

string toHex(int x)
{
    char buffer[64];
    snprintf(buffer, 64, "0x%x", x);
    return buffer;
}

int createHelper(key_t k, int size, int flag)
{
    int shmid = shmget(k, size, flag);
    if(shmid == -1)
    {
        cout << errno << " : " << strerror(errno) << endl;
        exit(2);
    }
    return shmid;
}

int createShm(key_t key, int size)
{
    umask(0);
    return createHelper(key, size, IPC_CREAT | IPC_EXCL | 0666);
}

int getShm(key_t key, int size)
{
    return createHelper(key, size, IPC_CREAT);
}

void deleteShm(int id)
{
    int n = shmctl(id, IPC_RMID, nullptr);
    assert(n != -1);
}


char* attachShm(int id)
{
    char* start = (char*)shmat(id, nullptr, 0);
    return start;
}

char* detattachShm(char* start)
{
    int n = shmdt(start);
    assert(n != -1);
}


// enum{CLIENT = 0, SERVER};
#define CLIENT 0
#define SERVER 1

class Init
{
public:
    Init(int type) : _type(type)
    {
        key_t key = getKey(); // 获取key
        if(_type == SERVER) _shmid = createShm(key, gsize);
        else _shmid = getShm(key, gsize);
        _start = attachShm(_shmid); //绑定
    }

    char* getStart()
    {
        return _start;
    }

    ~Init()
    {
        detattachShm(_start);
        if(_type == SERVER) deleteShm(_shmid);
    }
private:
    int _type;
    int _shmid;
    char* _start;
};
//Server.cc
#include "comm.hpp"
#include <unistd.h>

int main()
{

    Init s(SERVER);

    char *start = s.getStart();
    for(int i = 0; i < 26; i++)
    {
        sleep(1);
        cout << " get massage : " << start << endl;        
    }
    return 0;
}
//Client.cc
#include "comm.hpp"
#include <unistd.h>
int main()
{  
    Init c(CLIENT);

    char* start = c.getStart();

    char ch = 'A';
    int cur = 0;
    while(cur < 26)
    {       
        start[cur] = ch + cur;
        cur++;
        sleep(1);
    }
    return 0;
}

在这里插入图片描述

总结

文章介绍了共享内存的通信机制,对共享内存的具体通信过程以及实现方式和涉及的系统调用函数都一一做了介绍,对于进程间通信来说,共享内存通信是一种高效的进程间通信方式,适用于需要频繁交换数据的多个进程,但要谨慎使用,以确保正确的同步和互斥,以及正确处理共享内存的生命周期。

最后,码文不易,如果觉得文章对你有帮助的话就点个小小的👍吧!

在这里插入图片描述

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

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

相关文章

2023.09.30使用golang1.18编译Hel10-Web/Databasetools的windows版

#Go 1.21新增的 log/slog 完美解决了以上问题&#xff0c;并且带来了很多其他很实用的特性。 本次编译不使用log/slog 包 su - echo $GOPATH ;echo $GOROOT; cd /tmp; busybox wget --no-check-certificate https://go.dev/dl/go1.18.linux-amd64.tar.gz;\ which tar&&am…

【腾讯云 TDSQL-C Serverless 产品体验】国产数据库遥遥领先

一、为什么选TDSQL-C 1、性能达到每分钟8.14亿笔交易 国产数据库傲立世界之巅&#xff01;腾讯云数据库TDSQL-C性能刷新世界记录。 由TPC发起的TPC-C是针对在线事务处理(OLTP)的基准测试模型&#xff0c;是全球数据库厂商公认的性能评价标准&#xff0c;被誉为数据库领域的“…

解决WIFI网络登录困难的方法

当你遇到手机WIFI网络在连接成功后&#xff0c;总是提示网络受限或者当前网络无法连接互联网&#xff0c;但过一段时间后它又自动恢复正常的的问题&#xff0c;可以尝试用以下方法来解决。 第一步&#xff1a;打开WLAN连接设置界面&#xff0c;选择“更多设置” 第二步&#x…

关于TUM数据集

2、验证回环检测算法&#xff0c;需要有人工标记回环的数据集。然而人工标记回环是很不方便的&#xff0c;我们会考虑根据标准轨迹计算回环。即&#xff0c;如果轨迹中有两个帧的位姿非常相近&#xff0c;就认为它们是回环。请根据TUM数据集给出的标准轨迹&#xff0c;计算出一…

Flink中的状态一致性

1.概念 一致性其实就是结果的正确性。对于分布式系统而言&#xff0c;从不同节点读取时总能得到相同的值&#xff1b;而对于事务而言&#xff0c;是要求提交更新操作后&#xff0c;能够读取到新的数据。 有状态的流处理&#xff0c;内部每个算子任务都可以有自己的状态。对于流…

域环境介绍

一、概述 内网也指局域网&#xff0c;指的是某个区域由多台计算机互连而成的计算机组&#xff0c;范围通常在数千米以内&#xff0c;在局域网中&#xff0c;可以实现文件管理&#xff0c;应用软件共享&#xff0c;打印机共享、工作组内的日程安排、电子邮件和传真通信服务等&a…

【微服务保护】

文章目录 Sentinel 微服务雪崩问题&#xff1a; 微服务中&#xff0c;服务间调用关系错综复杂&#xff0c;一个微服务往往依赖于多个其它微服务。服务D有 故障进而导致服务A有故障&#xff0c;进而导致服务雪崩。 解决雪崩问题的常见方式有四种&#xff1a; 超时处理&#xff1…

iPhone苹果手机复制粘贴内容提示弹窗如何取消关闭提醒?

经常使用草柴APP查询淘宝、天猫、京东商品优惠券拿购物返利的iPhone苹果手机用户&#xff0c;复制商品链接后打开草柴APP粘贴商品链接查券时总是弹窗提示粘贴内容&#xff0c;为此很多苹果iPhone手机用户联系客服询问如何关闭iPhone苹果手机复制粘贴内容弹窗提醒功能的方法如下…

毛玻璃态按钮悬停效果

效果展示 页面结构组成 通过上述的效果展示可以看出如下几个效果 毛玻璃的按钮按钮上斜边背景及动画按钮上下边缘的小按钮和小按钮动画 CSS3 知识点 backdrop-filter 属性transition 属性transform 属性 实现基础按钮结构 <div class"btn"><a href&qu…

第三章 C运算符和控制语句

几乎每一个程序都需要进行运算&#xff0c;对数据进行加工处理&#xff0c;否则程序就没有意义了。要进行运算&#xff0c;就需规定可以使用的运算符。 C语言的运算符范围很宽&#xff0c;把除了控制语句和输人输出以外的几乎所有的基本操作都作为运算符处理。 运算符分类1 除…

【Linux】—— 详解动态库和静态库

前言&#xff1a; 本期我将要给大家讲解的是有关 动态库和静态库 的相关知识&#xff01;&#xff01;&#xff01; 目录 序言 见一见库 为什么要有库 &#xff08;一&#xff09;动态库&#xff08;.so&#xff09; 1.基本概念 2.命名规则 3.制作动态库 &#xff0…

No155.精选前端面试题,享受每天的挑战和学习

🤍 前端开发工程师(主业)、技术博主(副业)、已过CET6 🍨 阿珊和她的猫_CSDN个人主页 🕠 牛客高级专题作者、在牛客打造高质量专栏《前端面试必备》 🍚 蓝桥云课签约作者、已在蓝桥云课上架的前后端实战课程《Vue.js 和 Egg.js 开发企业级健康管理项目》、《带你从入…

【KingbaseES】银河麒麟V10 ARM64架构_安装人大金仓数据库KingbaseES_V8R6(CentOS8)

&#x1f341; 博主 "开着拖拉机回家"带您 Go to New World.✨&#x1f341; &#x1f984; 个人主页——&#x1f390;开着拖拉机回家_Linux,Java基础学习,大数据运维-CSDN博客 &#x1f390;✨&#x1f341; &#x1fa81;&#x1f341; 希望本文能够给您带来一定的…

C# 类型、变量与对象

变量一共7种&#xff1a; 静态变量&#xff08;静态字段&#xff09;、实例变量&#xff08;成员变量、字段&#xff09;、数组元素、值参数、引用参数、输出形参、局部变量 狭义的变量就是局部变量 内存的最小单位是比特&#xff08;byte&#xff09;&#xff0c;8个比特为…

【数据结构】【C++】封装哈希表模拟实现unordered_map和unordered_set容器

【数据结构】&&【C】封装哈希表模拟实现unordered_map和unordered_set容器 一.哈希表的完成二.改造哈希表(泛型适配)三.封装unordered_map和unordered_set的接口四.实现哈希表迭代器(泛型适配)五.封装unordered_map和unordered_set的迭代器六.解决key不能修改问题七.实…

Stm32_标准库_5_呼吸灯_按键控制

Stm32按键和输出差不多 PA1为LED供给正电&#xff0c;PB5放置按键&#xff0c;按键一端接PB5,另一端接负极 void Key_Init(void){RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); //APB2总线连接着GPIOBGPIO_InitStructur.GPIO_Mode GPIO_Mode_IPU;GPIO_InitStructur.…

Java下对象的序列化和反序列化(写出和读入)

代码如下&#xff1a; public class MyWork {public static void main(String[] args) throws IOException, ClassNotFoundException {//序列化File f new File("testFile/testObject.txt");ObjectOutputStream oos new ObjectOutputStream(new FileOutputStream(…

数据结构:堆的实现和堆排序及TopK问题

文章目录 1. 堆的概念和性质1.1 堆的概念1.2 堆的性质1.3 堆的作用 2. 堆的声明3. 堆的实现3.1 堆的插入3.2 删除堆顶元素3.3 利用数组建堆3.4 完整代码 4. 堆的应用4.1 堆排序4.2 TopK问题代码实现 物理结构有顺序结构存储和链式结构存储两种,二叉树理所应当也是可以顺序结构存…

实时通信协议

本文旨在简要解释如何在Web上实现客户端/服务器和客户端/客户端之间的实时通信&#xff0c;以及它们的内部工作原理和最常见的用例。 TCP vs UDP TCP和UDP都位于OSI模型的传输层&#xff0c;负责在网络上传输数据包。它们之间的主要区别在于&#xff0c;TCP在传输数据之前会打开…

26960-2011 半自动捆扎机 学习笔记

声明 本文是学习GB-T 26960-2011 半自动捆扎机. 而整理的学习笔记,分享出来希望更多人受益,如果存在侵权请及时联系我们 1 范围 本标准规定了半自动捆扎机(以下简称"捆扎机")的术语和定义、型号、型式与基本参数、技术要求、 试验方法、检验规则及标志、包装、运…