【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


相关文章
【Linux进程概念】—— 操作系统中的“生命体”,计算机里的“多线程”
在计算机系统的底层架构中,操作系统肩负着资源管理与任务调度的重任。当我们启动各类应用程序时,其背后复杂的运作机制便悄然展开。程序,作为静态的指令集合,如何在系统中实现动态执行?本文带你一探究竟!
【Linux进程概念】—— 操作系统中的“生命体”,计算机里的“多线程”
【Linux】进程IO|系统调用|open|write|文件描述符fd|封装|理解一切皆文件
本文详细介绍了Linux中的进程IO与系统调用,包括 `open`、`write`、`read`和 `close`函数及其用法,解释了文件描述符(fd)的概念,并深入探讨了Linux中的“一切皆文件”思想。这种设计极大地简化了系统编程,使得处理不同类型的IO设备变得更加一致和简单。通过本文的学习,您应该能够更好地理解和应用Linux中的进程IO操作,提高系统编程的效率和能力。
78 34
|
13天前
|
Linux:守护进程(进程组、会话和守护进程)
守护进程在 Linux 系统中扮演着重要角色,通过后台执行关键任务和服务,确保系统的稳定运行。理解进程组和会话的概念,是正确创建和管理守护进程的基础。使用现代的 `systemd` 或传统的 `init.d` 方法,可以有效地管理守护进程,提升系统的可靠性和可维护性。希望本文能帮助读者深入理解并掌握 Linux 守护进程的相关知识。
28 7
|
12天前
|
Linux 进程前台后台切换与作业控制
进程前台/后台切换及作业控制简介: 在 Shell 中,启动的程序默认为前台进程,会占用终端直到执行完毕。例如,执行 `./shella.sh` 时,终端会被占用。为避免不便,可将命令放到后台运行,如 `./shella.sh &`,此时终端命令行立即返回,可继续输入其他命令。 常用作业控制命令: - `fg %1`:将后台作业切换到前台。 - `Ctrl + Z`:暂停前台作业并放到后台。 - `bg %1`:让暂停的后台作业继续执行。 - `kill %1`:终止后台作业。 优先级调整:
32 5
Linux 进程管理基础
Linux 进程是操作系统中运行程序的实例,彼此隔离以确保安全性和稳定性。常用命令查看和管理进程:`ps` 显示当前终端会话相关进程;`ps aux` 和 `ps -ef` 显示所有进程信息;`ps -u username` 查看特定用户进程;`ps -e | grep &lt;进程名&gt;` 查找特定进程;`ps -p &lt;PID&gt;` 查看指定 PID 的进程详情。终止进程可用 `kill &lt;PID&gt;` 或 `pkill &lt;进程名&gt;`,强制终止加 `-9` 选项。
20 3
|
18天前
|
Linux系统内存使用优化技巧
交换空间(Swap)的优化 禁用 Swap sudo swapoff -a 作用:这个命令会禁用系统中所有的 Swap 空间。swapoff 命令用于关闭 Swap 空间,-a 参数表示关闭 /etc/fstab 文件中配置的所有 Swap 空间。 使用场景:在高性能应用场景下,比如数据库服务器或高性能计算服务器,禁用 Swap 可以减少磁盘 I/O,提高系统性能。
34 3
|
18天前
|
Linux查看内存命令
1. free free命令是最常用的查看内存使用情况的命令。它显示系统的总内存、已使用内存、空闲内存和交换内存的总量。 free -h • -h 选项:以易读的格式(如GB、MB)显示内存大小。 输出示例: total used free shared buff/cache available Mem: 15Gi 4.7Gi 4.1Gi 288Mi 6.6Gi 9.9Gi Swap: 2.0Gi 0B 2.0Gi • to
31 2
c++ linux通过实现独立进程之间的通信和传递字符串 demo
的进程间通信机制,适用于父子进程之间的数据传输。希望本文能帮助您更好地理解和应用Linux管道,提升开发效率。 在实际开发中,除了管道,还可以根据具体需求选择消息队列、共享内存、套接字等其他进程间通信方
68 16
JVM简介—1.Java内存区域
本文详细介绍了Java虚拟机运行时数据区的各个方面,包括其定义、类型(如程序计数器、Java虚拟机栈、本地方法栈、Java堆、方法区和直接内存)及其作用。文中还探讨了各版本内存区域的变化、直接内存的使用、从线程角度分析Java内存区域、堆与栈的区别、对象创建步骤、对象内存布局及访问定位,并通过实例说明了常见内存溢出问题的原因和表现形式。这些内容帮助开发者深入理解Java内存管理机制,优化应用程序性能并解决潜在的内存问题。
JVM简介—1.Java内存区域
JVM实战—2.JVM内存设置与对象分配流转
本文详细介绍了JVM内存管理的相关知识,包括:JVM内存划分原理、对象分配与流转、线上系统JVM内存设置、JVM参数优化、问题汇总。
JVM实战—2.JVM内存设置与对象分配流转
AI助理

你好,我是AI助理

可以解答问题、推荐解决方案等