【Linux】进程间的通信之共享内存

这篇具有很好参考价值的文章主要介绍了【Linux】进程间的通信之共享内存。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

一、system V 内存共享原理

利用内存共享进行进程间的通信的原理其实分为以下几个步骤:

  1. 在物理内存中创建一块共享内存。
  2. 将共享内存链接到要通信的进程的页表中,并通过页表进行进程地址空间的映射。
  3. 进程地址空间映射完毕以后返回首个虚拟地址,以便于进程之间进行通信。

【Linux】进程间的通信之共享内存

根据共享内存的原理,一旦这样的内存映射到共享它的进程的地址空间,这些进程间数据传递不再涉及到内核,换句话说是进程不再通过执行进入内核的系统调用来传递彼此的数据,这也就大大的提高了进程间通信的速度。


系统中可以用共享内存进行通信,但是系统中可能不是只有一对进程使用共享内存,在任何一个时刻,可能有多个共享内存在被用来进行通信,所以系统中一定会存在很多共享内存块,操作系统也要整体管理所有的共享内存。

所以共享内存,不是我们想的那样,只要在内存中开辟空间即可,系统也要为了管理共享内存,构建对应的描述共享内存的结构体对象! 在Linux中这个结构体是struct shmid_ds,这个结构体里面存放了共享内存的各种属性。

所以:共享内存 = 共享内存的内核数据结构(struct shmid_ds) + 真正开辟的内存空间

二、共享内存的使用

共享内存的使用涉及很多的系统调用,在这里我们边实现代码边进行讲解。

1、ftok函数

【Linux】进程间的通信之共享内存
key_t是一个inttypedef

这个函数的作用就是根据你传递的两个参数,结合函数内部的算法生成一个key_t类型的值,这里我们暂时把这个值记为 key,这个key几乎不可能出现:参数的不同,结果生成相同的key值。

这个key值单独看起来是没有任何作用的,它要与其他的系统调用结合起来才会发挥作用,未来要通信的两个进程就可以在函数ftok输入相同的相同的参数,生成相同的key值,然后通过这个key值就能确定它们想要通信的共享内存是哪一个了。

  • 参数:第一个是路径名,第二个是项目ID(只有最低8位可以使用),我们可以给这两个参数随意赋值,但是这个文件路径必须是现有的,可以访问的的文件,项目ID最低8位必须有效,而且必须非零。

  • 返回值:返回一个key_t类型的值,如果调用失败,返回-1,错误码被设置。

2、shmget函数

【Linux】进程间的通信之共享内存
这个函数的作用是:在物理内存中申请一块共享内存,并返回一个和参数key相关联的这块共享内存的标识符。

  • 参数

    1. 第一个参数是用ftok函数生成的key值。

    2. 第二个参数是设置共享内存的大小(你实际得到的大小),但是这个共享内存的大小实际上操作系统是按照PAGE_SIZE(4KB)对齐,来分配的。

    3. 第三个参数是标志位,这里主要介绍三个标志:IPC_CREATIPC_EXCLmode_flags

      IPC_CREAT:如果与key有关共享内存不存在,就创建,如果已经存在就直接返回共享内存的标识符。

      IPC_EXCL: 这个标志要与IPC_CREAT一起使用,表示共享内存如果不存在就创建,如果存在就直接报错,通过这两个标志位的组合,我们能够保证我们拿到的共享内存一定是最新的,而不是以前其他进程可能使用过的。

      mode_flags : 最低9位有效,表示申请的共享内存的权限。

  • 返回值:调用成功,返回一个和参数key相关联的这块共享内存的标识符,调用失败就返回 -1,错误码被设置。

3、shmat函数

【Linux】进程间的通信之共享内存

这个函数的作用就是将我们申请得到的共享内存与进程地址空间进行映射,映射完毕以后返回给我们一个映射好以后的首个虚拟地址,通过这个虚拟地址我们就可以进行进程间通信了。

  • 参数

    1. 来自于shmget函数得到的共享内存标识符。
    2. 一个虚拟地址,我们想要将共享内存的地址在进程地址空间的哪里开始映射,如果这里设置为NULL操作系统会自动选择一个合适的地址进行映射。
    3. 标志位,当这里传入SHM_RDONLY时,表示映射以后的进程对于此段空间只有读权限,没有写权限,当我们在这里传入0时,表示读写权限都有。
  • 返回值
    如果函数调用成功就返回映射好以后的首个虚拟地址,以便于进行读取和写入,如果调用失败就会返回一个(void *) -1的值

4、shmdt函数

【Linux】进程间的通信之共享内存

当我们在进程地址空间中挂起了一个块共享内存之后当我们不需要使用时,也要将进程地址空间中的链接关系进行取消,shmdt函数的作用就是如此。

  • 参数shmat函数返回的地址。
  • 返回值: 如果调用成功就返回0,调用失败就返回-1

5、shmctl函数

【Linux】进程间的通信之共享内存

此函数的功能很强大,里面有许多的标志位,可以完成许多不同的工作,这个函数主要用来控制共享内存。

  • 参数
    1. 来自于shmget函数得到的共享内存标识符。
    2. 是标志位,里面有许多标志,这里我们就介绍两个: IPC_RMIDIPC_STAT

      IPC_RMID: 设置这个标记表示要销毁共享内存,调用者必须是共享内存的创建者,或者是特权用户。

      IPC_STAT: 将shmid相关的内核数据结构中的信息复制到第三个参数buf所指向的shmid_ds结构中,调用者必须要有读权限。
    3. 一个struct shmid_ds类型的指针,在标志位中设置了IPC_STAT,指针所指向的变量里面就能拿到相关的内核信息,如果不关心内核信息可以设置为nullptr
  • 返回值:一般来说,成功返回是0,错误返回结果是-1

6、代码使用

command .hpp

#ifndef __COMMAND_HPP__
#define __COMMAND_HPP__

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

#define NUM 4096

const std::string pathname = "./";
const int proj_id = 66;

#endif

sysserve.cpp

#include "command.hpp"

int main()
{
    // 1.生成相同的key值
    key_t key = ftok(pathname.c_str(), proj_id);
    if (key == -1)
    {
        std::cerr << "错误码" << errno << " " << strerror(errno) << std::endl;
        exit(-1);
    }

    // 2.申请共享内存
    umask(0);
    int shmid = shmget(key, NUM, IPC_CREAT | IPC_EXCL | 0666);
    if (shmid == -1)
    {
        std::cerr << "错误码" << errno << " " << strerror(errno) << std::endl;
        exit(-1);
    }

    // 停顿3s观察连接数变为 1
    sleep(2);

    // 3.将共享内存挂接到进程地址空间
    char *s = (char *)shmat(shmid, nullptr, 0);

    // 4.进行进程间的通信
    sleep(6); // 启动另外一个要通信进程,看到连接数变为2

    // 5.取消挂接
    int err = shmdt(s);
    if (err == -1)
    {
        std::cerr << "错误码" << errno << " " << strerror(errno) << std::endl;
    }
    sleep(3);
    
    // 6.删除共享内存
    shmctl(shmid, IPC_RMID, nullptr);

    return 0;
}

sysclient.cpp

#include "command.hpp"

int main()
{
    // 1.生成相同的key值
    key_t key = ftok(pathname.c_str(), proj_id);
    if (key == -1)
    {
        std::cerr << "错误码" << errno << " " << strerror(errno) << std::endl;
        exit(-1);
    }

    // 2.得到共享内存标识符
    int shmid = shmget(key, NUM, IPC_CREAT);
    if (shmid == -1)
    {
        std::cerr << "错误码" << errno << " " << strerror(errno) << std::endl;
        exit(-1);
    }

    // 3.将共享内存挂接到进程地址空间
    char *s = (char *)shmat(shmid, nullptr, 0);

    // 4.进行进程间的通信
    sleep(3);

    // 5.取消挂接
    int err = shmdt(s);
    if (err == -1)
    {
        std::cerr << "错误码" << errno << " " << strerror(errno) << std::endl;
    }

    return 0;
}

Linux的命令行中我们可以使用ipcs -m命令查看共享内存的详细信息。

【Linux】进程间的通信之共享内存

可以看到第一个是共享内存的key值,第二个是共享内存的标识符,第三个是拥有者,第四个是权限,第五个是共享内存的字节数,第六个是共享内存的连接数,第七个是共享内存的状态。

运行上面的代码,先启动sysserve再启动sysclient,我们可以看到连接数由0->1 ->2 ->1 ->0 ->被删除

【Linux】进程间的通信之共享内存

【Linux】进程间的通信之共享内存

【Linux】进程间的通信之共享内存文章来源地址https://www.toymoban.com/news/detail-488361.html

三、一些细节的补充

  1. 共享内存的生命周期是随内核的,即进程退出以后如果没有删除共享内存,则共享内存不会消失!
  2. 共享内存没有任何保护机制(同步和互斥)

到了这里,关于【Linux】进程间的通信之共享内存的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Linux之进程间通信——system V(共享内存、消息队列、信号量等)

    本文介绍了另一种进程间通信——system V,主要介绍了共享内存,消息队列、信号量,当然消息队列了信号量并非重点,简单了解即可。 共享内存 :不同的进程为了进行通信看到的同一个内存块,该内存块被称为共享内存。 进程具有独立性,它的内核数据结构包括对应的代

    2024年02月08日
    浏览(60)
  • 【探索Linux】—— 强大的命令行工具 P.15(进程间通信 —— system V共享内存)

    在多进程编程中,进程间通信(IPC)是一项关键技术,它使得不同的进程能够相互交换数据和协调行为。而在众多的IPC机制中,System V共享内存是一种高效且强大的通信方式。通过使用共享内存,我们可以将数据从一个进程快速地传递给另一个进程,避免了复制数据的开销,

    2024年02月05日
    浏览(62)
  • 【Linux从入门到精通】通信 | 共享内存(System V)

        本篇文章接着上篇文章通信 | 管道通信(匿名管道 命名管道)进行讲解。本篇文章的中点内容是 共享内存 。 文章目录  一、初识与创建共享内存 1、1 什么是共享内存 1、2 共享内存函数 1、2、1 创建共享内存 shmget 1、2、2 ftok 生成 key 1、2、3 获取共享内存 shmget 1、3 dem

    2024年02月09日
    浏览(42)
  • Linux进程通信——共享内存

    两个进程的PCB创建虚拟地址空间然后映射到物理内存中,每个进程因为是独立的,所以在物理内存中的地址也不同。 那么共享内存是怎么做到的呢? 首先先在物理内存中申请一块内存。 然后讲这块内存通过页表映射分别映射到这两个进程的虚拟地址空间内,让这两个进程都

    2024年02月01日
    浏览(51)
  • 【Linux】进程通信 — 共享内存

    上一章我们由进程通信,引入并讲述了管道,匿名管道和命名管道和匿名管道。本章我们将继续讲解进程通信的另一种方式,通过共享内存的方式来进行进程间的通信。还要学习几个系统调用接口,并用代码实现两个进程通过共享内存来进行通信。目标已经确定,接下来就要

    2024年02月15日
    浏览(44)
  • Linux——进程通信之共享内存

    目录 一.  回顾上文 二.共享内存 1.定义 2.特点: 3.实现步骤: 如下为成功链接共享内存使用权的完整步骤: 4.函数介绍         4.1shmget函数         4.1.2参数介绍                4.2ftok函数:         4.2.1参数介绍                 关于ftok(); shmget();函数的

    2024年02月12日
    浏览(54)
  • (26)Linux 进程通信之共享内存(共享储存空间)

    共享内存是System V版本的最后一个进程间通信方式。 共享内存,顾名思义就是允许两个不相关的进程访问同一个逻辑内存,共享内存是两个正在运行的进程之间共享和传递数据的一种非常有效的方式。不同进程之间共享的内存通常为同一段物理内存。进程可以将同一段物理内

    2024年01月16日
    浏览(46)
  • 【Linux】进程间通信之共享内存

    共享内存比管道快哦~ 文章目录 前言 一、共享内存的实现原理 二、实现共享内存的代码 总结 共享内存区是最快的IPC形式。一旦这样的内存映射到共享它的进程的地址空间,这些进程间数据传递不再涉及到内核,换句话说是进程不再通过执行进入内核的系统调用来传递彼此的

    2024年02月03日
    浏览(51)
  • Linux 共享内存mmap,进程通信

    进程间通信是操作系统中重要的概念之一,使得不同的进程可以相互交换数据和进行协作。其中,共享内存是一种高效的进程间通信机制,而内存映射(mmap)是实现共享内存的一种常见方法。 存储映射 I/O 是 一个磁盘文件 与 存储空间中的一个缓冲区相映射 。于是, 当从缓

    2024年02月13日
    浏览(45)
  • 【Linux】进程间通信——管道/共享内存

    进程间通信( Inter-Process Communication,简称IPC )是指不同进程之间进行数据交换和共享信息的机制和技术。在操作系统中,每个进程都是独立运行的,有自己的地址空间和数据,因此进程之间需要一种机制来进行通信,以便彼此协调工作、共享数据或者进行同步操作。 进程间

    2024年02月16日
    浏览(45)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包