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

简介: 【Linux】进程间通信之共享内存(上)

前言



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


一、共享内存的实现原理



7c28f9a0dbd846b68d18844805d2cce0.png


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

e220ae7cb1a84c0aa6b633e883d13d8c.png



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


二、实现共享内存的代码



首先我们创建所需要的文件,比如这里我创建了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


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

c756d33a6074413b9a063a50bb4d019b.png


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

40319068e5914fa782ee1531ccd6d10b.png

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


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

157236ff86434f8ab0fd449cccbe3d4b.png


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

d1180925253c4f058f387560595c4c76.png

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

d29122a19cb64dc69a44b12d42c3d745.png


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


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

b789d959739d4636a2cac3f92932b956.png

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

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


05f2ed9c4c6f4c8eaae2f480c380e859.png


如果成功就返回已经创建好的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后下一步我们就可以开始创建共享内存了,从我们刚刚画的图可以看到,一定是有一个进程先创建了共享内存,然后另一个进程去找获取刚刚创建的共享内存,所以我们下一步就是在服务端创建一个共享内存:

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

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是我们要怎么创建的选项,第一次创建必须是一个全新的没有使用过的,这个给服务端用,第二个是打开已有的共享内存是给客户端用的,然后我们在客户端和服务端也把代码写完整:

233f995f8a4242b583911d5f49cef1d6.png6a03a53ff2f349c289ba953c03344bb1.png


目录
相关文章
|
2月前
|
缓存 监控 Linux
Linux内存问题排查命令详解
Linux服务器卡顿?可能是内存问题。掌握free、vmstat、sar三大命令,快速排查内存使用情况。free查看实时内存,vmstat诊断系统整体性能瓶颈,sar实现长期监控,三者结合,高效定位并解决内存问题。
219 0
Linux内存问题排查命令详解
|
6月前
|
缓存 Linux 数据安全/隐私保护
Linux环境下如何通过手动调用drop_caches命令释放内存
总的来说,记录住“drop_caches” 命令并理解其含义,可以让你在日常使用Linux的过程中更加娴熟和自如。
1187 23
|
6月前
|
Web App开发 Linux 程序员
获取和理解Linux进程以及其PID的基础知识。
总的来说,理解Linux进程及其PID需要我们明白,进程就如同汽车,负责执行任务,而PID则是独特的车牌号,为我们提供了管理的便利。知道这个,我们就可以更好地理解和操作Linux系统,甚至通过对进程的有效管理,让系统运行得更加顺畅。
194 16
|
6月前
|
Unix Linux
对于Linux的进程概念以及进程状态的理解和解析
现在,我们已经了解了Linux进程的基础知识和进程状态的理解了。这就像我们理解了城市中行人的行走和行为模式!希望这个形象的例子能帮助我们更好地理解这个重要的概念,并在实际应用中发挥作用。
136 20
|
5月前
|
监控 Shell Linux
Linux进程控制(详细讲解)
进程等待是系统通过调用特定的接口(如waitwaitpid)来实现的。来进行对子进程状态检测与回收的功能。
123 0
|
5月前
|
存储 负载均衡 算法
Linux2.6内核进程调度队列
本篇文章是Linux进程系列中的最后一篇文章,本来是想放在上一篇文章的结尾的,但是想了想还是单独写一篇文章吧,虽然说这部分内容是比较难的,所有一般来说是简单的提及带过的,但是为了让大家对进程有更深的理解与认识,还是看了一些别人的文章,然后学习了学习,然后对此做了总结,尽可能详细的介绍明白。最后推荐一篇文章Linux的进程优先级 NI 和 PR - 简书。
171 0
|
5月前
|
存储 Linux Shell
Linux进程概念-详细版(二)
在Linux进程概念-详细版(一)中我们解释了什么是进程,以及进程的各种状态,已经对进程有了一定的认识,那么这篇文章将会继续补全上篇文章剩余没有说到的,进程优先级,环境变量,程序地址空间,进程地址空间,以及调度队列。
120 0
|
5月前
|
Linux 调度 C语言
Linux进程概念-详细版(一)
子进程与父进程代码共享,其子进程直接用父进程的代码,其自己本身无代码,所以子进程无法改动代码,平时所说的修改是修改的数据。为什么要创建子进程:为了让其父子进程执行不同的代码块。子进程的数据相对于父进程是会进行写时拷贝(COW)。
145 0
|
2月前
|
Linux 应用服务中间件 Shell
二、Linux文本处理与文件操作核心命令
熟悉了Linux的基本“行走”后,就该拿起真正的“工具”干活了。用grep这个“放大镜”在文件里搜索内容,用find这个“探测器”在系统中寻找文件,再用tar把东西打包带走。最关键的是要学会使用管道符|,它像一条流水线,能把这些命令串联起来,让简单工具组合出强大的功能,比如 ps -ef | grep 'nginx' 就能快速找出nginx进程。
385 1
二、Linux文本处理与文件操作核心命令
|
2月前
|
Linux
linux命令—stat
`stat` 是 Linux 系统中用于查看文件或文件系统详细状态信息的命令。相比 `ls -l`,它提供更全面的信息,包括文件大小、权限、所有者、时间戳(最后访问、修改、状态变更时间)、inode 号、设备信息等。其常用选项包括 `-f` 查看文件系统状态、`-t` 以简洁格式输出、`-L` 跟踪符号链接,以及 `-c` 或 `--format` 自定义输出格式。通过这些选项,用户可以灵活获取所需信息,适用于系统调试、权限检查、磁盘管理等场景。
284 137

热门文章

最新文章