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

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

共享内存比管道快哦~

文章目录

  • 前言
  • 一、共享内存的实现原理
  • 二、实现共享内存的代码
  • 总结

前言

共享内存区是最快的IPC形式。一旦这样的内存映射到共享它的进程的地址空间,这些进程间数据传递不再涉及到内核,换句话说是进程不再通过执行进入内核的系统调用来传递彼此的数据.

一、共享内存的实现原理

【Linux】进程间通信之共享内存我们以上图为例,A和B是两个进程,他们都有自己的进程地址空间,进程地址空间经过页表映射到物理内存中,而共享内存是什么呢?就是有一个机制在物理内存中直接开好一块内存,这一块内存经过页表的映射到进程地址空间中的共享区(共享区在堆区和栈区之间),然后把共享区这块空间的起始地址返回给用户,两个进程进行同样的工作不就实现了让不同的进程看到同一份资源吗,那么如何让这两个进程取消关联呢?其实很简单,如下图:

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

 要取消两个进程间的关系只需要修改页表让进程地址空间不再通过页表的映射找到共享内存,并且将开好的空间释放掉,这就完成了进程间的取消关联。以上就是共享内存的基本原理。

二、实现共享内存的代码

首先我们创建所需要的文件,比如这里我创建了shmclient.cc和shmserver.cc以及makefile,然后我们需要用makefile帮我们创建两个可执行程序:

.PHONY:all
all:shmclient shmserver

shmclient:shmclient.cc
	g++ -o $@ $^ -std=c++11

shmserver:shmserver.cc
	g++ -o $@ $^ -std=c++11

.PHONY:clean
clean:
	rm -f shmclient shmserver

当然我们还需要一个存放头文件的文件:

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

 我们为了防止头文件重复包含所以用了条件编译,下面我们开始正式写代码,在这之前我们需要了解shmget函数,这个函数是用来获取共享内存的:

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

 此函数有三个参数,第一个参数key我们稍后讲解,第二个参数size就是要开多大的共享内存,第三个参数开的模式,IPC_CREAT的意思是创建一个共享内存,如果共享内存不存在,就创建,如果已经存在了就获取已经存在的共享内存并返回。IPC_EXCL不能单独使用,要配合IPC_CREAT使用,使用方式是(IPC_CREAT | IPC_EXCL),这两个组合在一起就是创建一个共享内存,如果共享内存不存在,就创建一个新的共享内存,如果已经存在了共享内存,就立马出错返回,那么这有什么意义呢?意义就是保证给我们创建的共享内存一定是最新的没有被使用过。

下面我们来看看shmget函数的返回值:

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

 如果我们创建成功了就给我们返回一个共享内存的标志,如果错误则返回-1.现在我们来讲讲第一个参数key是什么,我们可以看到这个参数的类型key_t,而这个参数实际上我们是需要另一个函数ftok获取的,下面我们来看看ftok函数:

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

 ftok这个函数的第一个参数是路径,第二个参数是一个int类型的id,这两个参数是用户去填的,ftok函数会结合路径和id转化出一个很难被重复的key值,由于系统中一定会同时存在多个共享内存,所以共享内存不是我们想的那样只要在内存中开辟空间即可,而是系统为了管理共享内存,会构建结构体来描述共享内存,所以共享内存 = 共享内存的内核数据结构 + 真正开辟的内存空间。下面我们用画图的方式解释一下这里的关系:

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

 中间的4个共享内存结构体是被系统所管理的,红色的共享内存是被进程A开辟的,那么如何让B进程也指向A开辟的这个共享内存呢,只需要让他们两个进程都用同一个路径和同一个id这样就生成了同样的key值,有了这个值B进程就能指向A开辟的那个共享内存了。

理解了以上的知识我们就可以直接写代码了,先将所有的头文件包好,因为两个进程都需要同样的路径和id所以我们直接define一下:

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

 我们直接将路径定义为一个.,一个点就代表是在当前路径,那么到时候创建共享内存的时候就会创建在当前当前路径了。下面我们直接写一个获取key的函数:

在用ftok函数之前我们先看一下此函数的返回值:

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

 如果成功就返回已经创建好的key值,如果失败则返回-1.

#ifndef __COMM__HPP__
#define __COMM__HPP__

#include <iostream>
#include <cerrno>
#include <cstdio>
#include <cstring>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/types.h>
using namespace std;
#define PATHNAME "."
#define PROJID 0x6666

key_t getKey()
{
    key_t k = ftok(PATHNAME,PROJID);
    if (k==-1)
    {
        cerr<<errno<<" : "<<strerror(errno)<<endl;
        exit(1);
    }
    return k;
}

有了key后下一步我们就可以开始创建共享内存了,从我们刚刚画的图可以看到,一定是有一个进程先创建了共享内存,然后另一个进程去找获取刚刚创建的共享内存,所以我们下一步就是在服务端创建一个共享内存:

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

 首先我们先用一个全局变量来控制共享内存的大小,然后直接用两个函数封装创建共享内存和打开共享内存:

static int createShmHelper(key_t k,int size,int flag)
{
    int shmid = shmget(k,gsize,flag);
    if (shmid==-1)
    {
        cerr<<errno<<" : "<<strerror(errno)<<endl;
        exit(2);
    }
    return shmid;
}
int creatShm(key_t k,int size)
{
    return createShmHelper(k,size,IPC_CREAT|IPC_EXCL);
}
int getShm(key_t k,int size)
{
    return createShmHelper(k,size,IPC_CREAT);
}

第一个静态函数就是我们创建共享内存的步骤,flag是我们要怎么创建的选项,第一次创建必须是一个全新的没有使用过的,这个给服务端用,第二个是打开已有的共享内存是给客户端用的,然后我们在客户端和服务端也把代码写完整:

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

【Linux】进程间通信之共享内存 下面我们将代码运行起来测试一下:

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

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

 我们将程序运行起来发现并没有问题,下面我们查看共享内存是否创建成功,ipcs这个命令可以显示出三条资源:

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

第一条资源为消息队列,第二条资源为共享内存段,第三个为共享内存核心数组,我们主要看第二条,可以看到我们创建的1024大小的共享内存创建成功了。当然我们可以只查第二条,命令是ipcs -m :

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

 下面我们讲一下上面所代表的意思,key就像文件的inode一样,而shmid就像文件的fd一样shmid才是操作共享内存所需要的核心数据。owner是谁创建的,perms是权限,nattch是与这个共享内存的连接数。下面我们在讲一下删除一个共享内存的命令:ipcrm -m +shmid(刚刚讲过shmid是用来操作共享内存的,与文件的fd作用一样):

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

 我们成功的删除了刚刚创建的共享内存。我们接着代码往下写,创建了共享内存后可以让客户端往服务端写一些东西,然后退出的时候需要删除共享内存,我们先来看看如何删除共享内存:

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

 利用系统调用删除共享内存需要shmctl函数,第一个参数就是共享内存的shmid,第二个参数是指令,也就是说对共享内存做什么操作,我们看看指令有哪些:

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

 常见的指令有这么几个,而IPC_RMID就是真正删除共享内存的操作,第一个IPC_STAT是获取共享内存的属性,而第三个参数是一个共享内存的结构体的指针可以查看共享内存对应的属性:

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

 下面我们就写一个删除共享内存函数:

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

 因为我们删除共享内存的时候不关心它的属性,所以我们直接设为空即可。

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

 下面我们给大家演示一下查看共享内存的属性:

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

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

 下面我们可以在创建共享内存的时候将权限也设置好:

int creatShm(key_t k,int size)
{
    umask(0);
    return createShmHelper(k,size,IPC_CREAT|IPC_EXCL|0666);
}

下面我们要将创建出来的共享内存与进程关联起来,要关联得需要shmat函数:

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

 第一个参数我们都知道,第二个参数的意思是可以指定将共享内存挂接到指定的地址处(在这里可以设为nullptr,nullptr后操作系统会自己选择合适的地址挂接),第三个参数是设置共享内存是只读的还是什么其他的权限(我们可以设为0,设为0默认是可读可写)

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

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

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

 当我们将进程和共享内存关联后,下一步就是退出前将他们去关联,去关联同样也需要函数,这个函数是shmdt:

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

 这个函数的参数我们已经讲过了,就是刚刚关联时候给我们返回的那个地址,所以我们可以直接写代码了:

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

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

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

 以上内存的所有点我们就讲完了,下面我们通过一个类封装一下让客户端和服务端的代码可以少一些:

#define SERVER 1
#define CLIENT 0
class Init
{
public:
    Init(int type)
    {
        key_t k = getKey();
        if (type==SERVER)
        {
            shmid = creatShm(k,gsize);
        }
        else 
        {
            shmid = getShm(k,gsize);
        }
        start = attachShm(shmid);
    }
    char* getStart()
    {
        return start;
    }
    ~Init()
    {
        detachShm(start);
        if (type==SERVER)
        {
            delShm(shmid);
        }
    }
private:
    char* start;
    int type;
    int shmid;
}

通过以上类的封装,我们的客户端和服务端就能简化为以下这样:

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

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

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

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

 我们可以看到当我们一开始两个端口都运行的时候有两个进程链接在共享内存上,当客户端指令输入完成退出后就只剩下服务端,当服务端也退出后那么连接数就为0了。

 以上就是我们共享内存的所有内容,学了共享内存要知道共享内存是进程间通信的几种方式中访问速度最快的。


总结

使用ipcrm -m 命令删除指定共享内存后,是不会直接释放共享内存的,因为共享内存的生命周期是随操作系统的,只有共享内存当前的映射链接数为0才会被删除释放。

使用ipcrm -a 可以删除所有进程间通信资源(a代表all也就是全部的意思)

ipcrm -m删除共享内存,-q删除消息队列,-s删除信号量

共享内存实现实现通信的原理是因为所有进程操作映射同一块物理内存。

共享内存的操作是非进程安全的,多个进程同时对共享内存读写是有可能造成数据的交叉写入或读取,造成数据混乱。

共享内存的删除操作并非是直接删除,而是拒绝后续映射,只有在当前映射链接数为0的时候表示没有进程访问了,才会真正的被删除。

共享内存生命周期随内核,只要不删除,就一直存在于内核中,除非重启系统或者手动删除。文章来源地址https://www.toymoban.com/news/detail-435200.html

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

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

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

相关文章

  • (26)Linux 进程通信之共享内存(共享储存空间)

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

    2024年01月16日
    浏览(44)
  • Linux 共享内存mmap,进程通信

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

    2024年02月13日
    浏览(42)
  • 进程间通信--共享内存详解【Linux】

    本文详细讲解了共享内存的原理和使用,并且通过实例代码角度来深度理解共享内存,下面就让我们开始吧。 数据传输:一个进程需要将它的数据发送给另一个进程 资源共享:多个进程之间共享同样的资源。 通知事件:一个进程需要向另一个或一组进程发送消息,通知它(

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

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

    2024年02月03日
    浏览(44)
  • 【hello Linux】进程间通信——共享内存

    目录 前言: 1. System V共享内存 1. 共享内存的理解 2. 共享内存的使用步骤 3. 共享内存的使用         1. 共享内存的创建         查看共享内存         2. 共享内存的释放         3. 共享内存的挂接         4. 共享内存的去挂接 4. 共享内存的使用示例 1. 两进

    2024年02月01日
    浏览(90)
  • Linux--进程间的通信-共享内存

    前文: Linux–进程间的通信-匿名管道 Linux–进程间的通信–进程池 Linux–进程间的通信-命名管道 对于两个进程,通过在内存开辟一块空间(操作系统开辟的),进程的虚拟地址通过页表映射到对应的共享内存空间中,进而实现通信 ; 特点和作用: 高效性: 共享内存是一种

    2024年04月26日
    浏览(38)
  • 【Linux】进程间的通信之共享内存

    利用 内存共享 进行进程间的通信的原理其实分为以下几个步骤: 在物理内存中创建一块共享内存。 将共享内存链接到要通信的进程的页表中,并通过页表进行进程地址空间的映射。 进程地址空间映射完毕以后返回首个虚拟地址,以便于进程之间进行通信。 根据共享内存的

    2024年02月09日
    浏览(45)
  • 【Linux】进程间通信——system V共享内存

    目录  写在前面的话 System V共享内存原理 System V共享内存的建立 代码实现System V共享内存 创建共享内存shmget() ftok() 删除共享内存shmctl() 挂接共享内存shmat() 取消挂接共享内存shmdt() 整体通信流程的实现          上一章我们讲了进程间通信的第一种方式 --- 管道,这一章我

    2024年02月14日
    浏览(40)
  • 【Linux】进程间通信 -- system V共享内存

    共享内存区是最快的IPC形式。一旦这样的内存映射到共享它的进程的地址空间,这些进程间数据传递不再涉及到内核,换句话说是进程不再通过执行进入内核的系统调用来传递彼此的数据 理解: 进程间通信,是专门设计的,用来IPC 共享内存是一种通信方式,所有想通信的进程

    2024年02月16日
    浏览(42)
  • Linux学习记录——이십 进程间通信(2)共享内存

    system是一套标准,独立于文件系统之外,是系统专门为通信而设计出来的内核模块,称之为system V的IPC通信机制。 共享内存的主要做法也是让两个毫不相关的进程看到同一份资源。 进程的地址空间内,栈和堆区之间有个共享区,堆是向上增长,栈是向下增长,重合的那个地方

    2023年04月24日
    浏览(35)

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

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

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

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

二维码1

领取红包

二维码2

领红包