linux内核剖析(十一)进程间通信之-共享内存Shared Memory

【自取】最近整理的,有需要可以领取学习:

共享内存

共享内存是进程间通信中最简单的方式之一。

共享内存是系统出于多个进程之间通讯的考虑,而预留的的一块内存区。

共享内存允许两个或更多进程访问同一块内存,就如同 malloc() 函数向不同进程返回了指向同一个物理内存区域的指针。当一个进程改变了这块地址中的内容的时候,其它进程都会察觉到这个更改。

关于共享内存

当一个程序加载进内存后,它就被分成叫作页的块。

通信将存在内存的两个页之间或者两个独立的进程之间。

总之,当一个程序想和另外一个程序通信的时候,那内存将会为这两个程序生成一块公共的内存区域。这块被两个进程分享的内存区域叫做 共享内存

因为所有进程共享同一块内存,共享内存在各种进程间通信方式中具有最高的效率。访问共享内存区域和访问进程独有的内存区域一样快,并不需要通过系统调用或者其它需要切入内核的过程来完成。同时它也避免了对数据的各种不必要的复制。

如果没有共享内存的概念,那一个进程不能存取另外一个进程的内存部分,因而导致共享数据或者通信失效。因为系统内核没有对访问共享内存进行同步,您必须提供自己的同步措施。

解决这些问题的常用方法是通过使用 信号量进行同步。不过,我们的程序中只有一个进程访问了共享内存,因此在集中展示了共享内存机制的同时,我们避免了让代码被同步逻辑搞得混乱不堪。

为了简化共享数据的完整性和避免同时存取数据,内核提供了一种专门存取共享内存资源的机制。这称为 互斥体或者 mutex对象

例如,在数据被写入之前不允许进程从共享内存中读取信息、不允许两个进程同时向同一个共享内存地址写入数据等。

当一个进程想和另外一个进程通信的时候,它将按以下顺序运行:

  • 获取mutex对象,锁定共享区域。
  • 将要通信的数据写入共享区域。
  • 释放mutex对象。

当一个进程从从这个区域读数据时候,它将重复同样的步骤,只是将第二步变成读取。

内存模型

要使用一块 共享内存

  • 进程必须首先 分配
  • 随后需要 访问这个共享内存块的每一个进程都必须将这个共享内存绑定到自己的地址空间中
  • 当完成通信之后,所有进程都将 脱离共享内存,并且由一个进程 *释放该共享内存块

/proc/sys/kernel/目录下,记录着共享内存的一些限制,如一个共享内存区的最大字节数 shmmax,系统范围内最大共享内存区标识符数 shmmni等,可以手工对其调整,但不推荐这样做。

linux内核剖析(十一)进程间通信之-共享内存Shared Memory

理解 Linux 系统内存模型可以有助于解释这个绑定的过程。

linux系统内存模型

在 Linux 系统中,每个进程的虚拟内存是被分为许多页面的。这些内存页面中包含了实际的数据。每个进程都会维护一个从内存地址到虚拟内存页面之间的映射关系。尽管每个进程都有自己的内存地址,不同的进程可以同时将同一个内存页面映射到自己的地址空间中,从而达到共享内存的目的。

分配一个新的共享内存块会创建新的内存页面。因为所有进程都希望共享对同一块内存的访问,只应由一个进程创建一块新的共享内存。再次分配一块已经存在的内存块不会创建新的页面,而只是会返回一个标识该内存块的标识符。

一个进程如需使用这个共享内存块,则首先需要将它绑定到自己的地址空间中。

这样会创建一个从进程本身虚拟地址到共享页面的映射关系。当对共享内存的使用结束之后,这个映射关系将被删除。

当再也没有进程需要使用这个共享内存块的时候,必须有一个(且只能是一个)进程负责释放这个被共享的内存页面。

所有共享内存块的大小都必须是系统页面大小的整数倍。系统页面大小指的是系统中单个内存页面包含的字节数。在 Linux 系统中,内存页面大小是4KB,不过您仍然应该通过调用 getpagesize 获取这个值。

共享内存的实现分为两个步骤:

  • 创建共享内存,使用shmget函数。
  • 映射共享内存,将这段创建的共享内存映射到具体的进程空间去,使用shmat函数。

用于共享内存的函数

共享内存的使用,主要有以下几个API: ftok()shmget()shmat()shmdt()及shmctl()。

#include
void *shmat(int shm_id, const void *shm_addr, int shmflg);
int shmctl(int shm_id, int cmd, struct shmid_ds *buf);
int shmdt(const void *shm_addr);
int shmget(key_t key, size_t size, int shmflg);

linux内核剖析(十一)进程间通信之-共享内存Shared Memory

与信号量相类似,通常需要在包含shm.h文件之前包含sys/types.h与sys/ipc.h这两个头文件。

用ftok()函数获得一个ID号

应用说明,在IPC中,我们经常用用key_t的值来创建或者打开信号量,共享内存和消息队列。

key_t ftok(<span class="hljs-keyword cye-lm-tag">const <span class="hljs-keyword cye-lm-tag">char *pathname, <span class="hljs-keyword cye-lm-tag">int proj_id);</span></span></span>

参数描述 pathname 一定要在系统中存在并且进程能够访问的 proj_id 一个1-255之间的一个整数值,典型的值是一个ASCII值。

当成功执行的时候,一个key_t值将会被返回,否则-1被返回。我们可以使用strerror(errno)来确定具体的错误信息。

考虑到应用系统可能在不同的主机上应用,可以直接定义一个key,而不用ftok获得:

#define IPCKEY 0x344378

创建共享内存

进程通过调用shmget(Shared Memory GET,获取共享内存)来分配一个共享内存块。

int shmget(key_t key ,int size,int shmflg)

参数描述 key 一个用来标识共享内存块的键值 size 指定了所申请的内存块的大小 shmflg 操作共享内存的标识

返回值:如果成功,返回共享内存表示符,如果失败,返回-1。

  • 该函数的第二个参数key是一个用来标识共享内存块的键值。

彼此无关的进程可以通过指定同一个键以获取对同一个共享内存块的访问。不幸的是,其它程序也可能挑选了同样的特定值作为自己分配共享内存的键值,从而产生冲突。

用特殊常量IPC_PRIVATE作为键值可以保证系统建立一个全新的共享内存块。|

key标识共享内存的键值:0/IPC_PRIVATE。当key的取值为IPC_PRIVATE,则函数shmget将创建一块新的共享内存;如果key的取值为0,而参数中又设置了IPC_PRIVATE这个标志,则同样会创建一块新的共享内存。

  • 该函数的第二个参数size指定了所申请的内存块的大小。

因为这些内存块是以页面为单位进行分配的,实际分配的内存块大小将被扩大到页面大小的整数倍。

  • 第三个参数shmflg是一组标志,通过特定常量的按位或操作来shmget。这些特定常量包括:

IPC_CREAT:这个标志表示应创建一个新的共享内存块。通过指定这个标志,我们可以创建一个具有指定键值的新共享内存块。

IPC_EXCL:这个标志只能与 IPC_CREAT 同时使用。当指定这个标志的时候,如果已有一个具有这个键值的共享内存块存在,则shmget会调用失败。也就是说,这个标志将使线程获得一个”独有”的共享内存块。如果没有指定这个标志而系统中存在一个具有相同键值的共享内存块,shmget会返回这个已经建立的共享内存块,而不是重新创建一个。

模式标志:这个值由9个位组成,分别表示属主、属组和其它用户对该内存块的访问权限。

其中表示执行权限的位将被忽略。指明访问权限的一个简单办法是利用

映射共享内存

shmat()是用来允许本进程访问一块共享内存的函数,将这个内存区映射到本进程的虚拟地址空间。

int shmat(int shmid,char *shmaddr,int flag)

参数描述 shmid 那块共享内存的ID,是shmget函数返回的共享存储标识符 shmaddr 是共享内存的起始地址,如果shmaddr为0,内核会把共享内存映像到调用进程的地址空间中选定位置;如果shmaddr不为0,内核会把共享内存映像到shmaddr指定的位置。所以一般把shmaddr设为0。 shmflag 是本进程对该内存的操作模式。如果是SHM_RDONLY的话,就是只读模式。其它的是读写模式

成功时,这个函数返回共享内存的起始地址。失败时返回-1。

要让一个进程获取对一块共享内存的访问,这个进程必须先调用 shmat(SHared Memory Attach,绑定到共享内存)。

将 shmget 返回的共享内存标识符 SHMID 传递给这个函数作为第一个参数。

该函数的第二个参数是一个指针,指向您希望用于映射该共享内存块的进程内存地址;如果您指定NULL则Linux会自动选择一个合适的地址用于映射。第三个参数是一个标志位,包含了以下选项:

SHM_RND表示第二个参数指定的地址应被向下靠拢到内存页面大小的整数倍。如果您不指定这个标志,您将不得不在调用shmat的时候手工将共享内存块的大小按页面大小对齐。
SHM_RDONLY表示这个内存块将仅允许读取操作而禁止写入。 如果这个函数调用成功则会返回绑定的共享内存块对应的地址。通过 fork 函数创建的子进程同时继承这些共享内存块;

如果需要,它们可以主动脱离这些共享内存块。 当一个进程不再使用一个共享内存块的时候

共享内存解除映射

当一个进程不再需要共享内存时,需要把它从进程地址空间中多里。

<span class="hljs-keyword cye-lm-tag">int shmdt(<span class="hljs-keyword cye-lm-tag">char *shmaddr)</span></span>

参数描述 shmaddr 那块共享内存的起始地址

成功时返回0。失败时返回-1。

应通过调用 shmdt(Shared Memory Detach,脱离共享内存块)函数与该共享内存块脱离。将由 shmat 函数返回的地址传递给这个函数。如果当释放这个内存块的进程是最后一个使用该内存块的进程,则这个内存块将被删除。对 exit 或任何exec族函数的调用都会自动使进程脱离共享内存块。

控制释放

shmctl控制对这块共享内存的使用

函数原型

<span class="hljs-keyword cye-lm-tag">int  <span class="hljs-keyword cye-lm-tag">shmctl( <span class="hljs-keyword cye-lm-tag">int shmid , <span class="hljs-keyword cye-lm-tag">int cmd , struct shmid_ds <span class="hljs-variable cye-lm-tag">*buf );</span></span></span></span></span>

参数描述 shmid 是共享内存的ID。 cmd 控制命令 buf 一个结构体指针。IPC_STAT的时候,取得的状态放在这个结构体中。如果要改变共享内存的状态,用这个结构体指定。

其中cmd的取值如下

cmd描述 IPC_STAT 得到共享内存的状态 IPC_SET 改变共享内存的状态 IPC_RMID 删除共享内存

返回值: 成功:0 失败:-1

调用 shmctl(”Shared Memory Control”,控制共享内存)函数会返回一个共享内存块的相关信息。同时 shmctl 允许程序修改这些信息。

该函数的第一个参数是一个共享内存块标识。
要获取一个共享内存块的相关信息,则为该函数传递 IPC_STAT 作为第二个参数,同时传递一个指向一个 struct shmid_ds 对象的指针作为第三个参数。

要删除一个共享内存块,则应将 IPC_RMID 作为第二个参数,而将 NULL 作为第三个参数。当最后一个绑定该共享内存块的进程与其脱离时,该共享内存块将被删除。

您应当在结束使用每个共享内存块的时候都使用 shmctl 进行释放,以防止超过系统所允许的共享内存块的总数限制。调用 exit 和 exec 会使进程脱离共享内存块,但不会删除这个内存块。 要查看其它有关共享内存块的操作的描述,请参考shmctl函数的手册页。

示例

简单映射一块共享内存

#include
#include

#include
#include
#include

#define IPCKEY 0x366378

typedef struct st_setting
{
    char agen[10];
    unsigned char file_no;
}st_setting;

int main(int argc, char** argv)
{
    int         shm_id;

linux内核剖析(十一)进程间通信之-共享内存Shared Memory

ipcrm命令删除共享内存

在使用共享内存,结束程序退出后。如果你没在程序中用shmctl()删除共享内存的话,一定要在命令行下用ipcrm命令删除这块共享内存。你要是不管的话,它就一直在那儿放着了。
简单解释一下ipcs命令和ipcrm命令。

取得ipc信息:

usage : ipcs <span class="hljs-attribute cye-lm-tag">-asmq <span class="hljs-attribute cye-lm-tag">-tclup
    ipcs <span class="hljs-preprocessor cye-lm-tag">[<span class="hljs-attribute cye-lm-tag">-s <span class="hljs-attribute cye-lm-tag">-m <span class="hljs-attribute cye-lm-tag">-q<span class="hljs-preprocessor cye-lm-tag">]<span class="hljs-markup cye-lm-tag"> -i id
    ipcs -h for help.

m      &#x8F93;&#x51FA;&#x6709;&#x5173;&#x5171;&#x4EAB;&#x5185;&#x5B58;(shared memory)&#x7684;&#x4FE1;&#x606F;
-q      &#x8F93;&#x51FA;&#x6709;&#x5173;&#x4FE1;&#x606F;&#x961F;&#x5217;(message queue)&#x7684;&#x4FE1;&#x606F;
-s      &#x8F93;&#x51FA;&#x6709;&#x5173;&#x201C;&#x906E;&#x65AD;&#x5668;&#x201D;(semaphore)&#x7684;&#x4FE1;&#x606F;</span></span></span></span></span></span></span></span>

删除ipc

usage: ipcrm [ [-q msqid] [-m shmid] [-s semid]
          [-Q msgkey] [-M shmkey] [-S semkey] <span class="hljs-keyword cye-lm-tag">... ]
</span>

两端通信的程序

读者程序

#include
#include
#include
#include
#include
#include
#include
#include
#include

#define N 64

typedef struct
{
    pid_t pid;
    char buf[N];
} SHM;

void handler(int signo)
{

写者程序

#include
#include
#include
#include
#include
#include
#include
#include
#include

#define N 64

typedef struct
{
    pid_t pid;
    char buf[N];
} SHM;

void handler(int signo)
{

linux内核剖析(十一)进程间通信之-共享内存Shared Memory

Original: https://www.cnblogs.com/alantu2018/p/8991409.html
Author: AlanTu
Title: linux内核剖析(十一)进程间通信之-共享内存Shared Memory

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/8564/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

  • Git—git中分支操作

    前言 众所周知git是版本控制,那么如果我们想要开发或者修改本地代码肯定不能直接在我们主干分支上进行修改。这里就引入了git分支的一个说法。 git分支 我们可以通过创建分支来完成…

    Linux 2022年8月30日
    0240
  • Linux基础学习(一)

    Linux发行版 以软件包格式:rpm:Red Hat Enterprise LinuxCentOSopenSUSEFedoradeb:DebianUbuntulinux mint…

    Linux 2022年9月10日
    0170
  • linux 学习一:安装jdk和tomcat

    使用secureCRT 一、首先安装centos的rzsz、 1.yum自动安装:(yum安装rzsz) yum install lrzsz 2.手动安装方法如下:(包有问题,还是…

    Linux 2022年8月26日
    0290
  • linux版powershell中,tab补全,linux外部命令参数名,的模块介绍

    关键字 linux powershell pwsh 补全 complete bash zsh 摘要:linux用户的福音!在linux版powershell中,补全linux外部命…

    Linux 2022年9月10日
    0220
  • Shell脚本完成IOS平台下的多目录和多架构编译(调用Makefile一起完成)

    请输入博文的阅读密码: Original: https://www.cnblogs.com/cy568searchx/p/5735429.htmlAuthor: 星语海蓝Title…

    Linux 2022年9月14日
    0140
  • 007 Linux 命令三剑客之-awk

    Linux 命令三剑客,sed、grep、awk。 sed:擅长数据修改。 grep:擅长数据查找定位。 awk:擅长数据切片,数据格式化,功能最复杂。 awk 更适合格式化文本,…

    Linux 2022年9月10日
    0240
  • Linux录屏软件

    如何查找录屏软件 apt-cache search screen record libutempter-dev – privileged helper for utmp/wtmp …

    Linux 2022年8月26日
    0300
  • Linux 预设 DNS

    修改 Linux DNS 文件 /etc/resolv.conf nameserver 120.80.80.80 # wwan0 nameserver 221.5.88.88 # …

    Linux 2022年8月24日
    0270
  • 我和linux的第二十二天

    这些天学校里有很多东西,也有空闲时间,但我还没有利用它。前几天听国学课,发现自己的思想还不是很成熟。在过去,我有意识地遇到了一个用君子的方法来相处的绅士,一个恶棍被当做恶人对待。一…

    Linux 2022年8月8日
    0810
  • 如何在运行的linux系统中获取设备树?

    答:使用以下命令即可,dtc -I fs -O dts /sys/firmware/devicetree/base > your.dts Original: https://…

    Linux 2022年8月11日
    0370
  • [svc]sed&awk过滤行及sed常用例子

    – sed&#x8FC7;&#x6EE4;&#x884C; sed ‘2p’ sed ‘2,5p’ sed ‘2p;3p;4p’ – awk&#x8…

    Linux 2022年8月26日
    0240
  • redis主从复制

    Redis 是一个开源(BSD许可)的,内存中的数据结构存储系统,它可以用作数据库、缓存和消息中间件。 特性: 运行在内存中的数据集工作方式 支持多种数据结构 提供不同级别的磁盘持…

    Linux 2022年9月14日
    0180
  • 在Ubuntu20.04上安装Kubernetes-Kubeadm和Minikube

    镜像下载、域名解析、时间同步请点击阿里云开源镜像站 在本文中,我们将了解如何在 Ubuntu 20.04 上安装 Kubernetes。在过去的几年里,容器化为开发人员提供了很大的…

    Linux 2022年9月10日
    0210
  • linux 新建用户、用户组 以及为新用户分配权限(转)

    本文转自https://www.cnblogs.com/clicli/p/5943788.html 感谢作者。 [En] Thanks to the author. Linux 系…

    Linux 2022年8月8日
    0610
  • linux三剑客试题汇总

    1、找出/proc/meminfo文件中以s开头的行,至少用三种方式忽略大小写 2、显示etc目录下以root,centos或者user开头的信息 3、找出/etc/init.d/…

    Linux 2022年9月10日
    0190
  • linux rdate

    检查服务器时间,发现服务器时间与当前时间有很大出入。所以调整一下吧。 [En] Check the server time and find that the server tim…

    Linux 2022年8月13日
    0290
  • Git 修改远端仓库地址

    方法有三种:1.修改命令git remote set-url origin [url] 例如:git remote set-url origin gitlab@gitlab.chu…

    Linux 2022年8月30日
    0270
  • git log控制输出宽度

    % % %>, %>|, %>>, %>< 类似, 分别为 右对齐, 右对齐, 右对齐(如果左边有多余空格则复用), 居中对齐 参考示例: 其中…

    Linux 2022年8月30日
    0190
  • ubuntu14.04 源

    http://chenrongya.blog.163.com/blog/static/8747419620143185103297/ 注意 sudo apt-get update刷…

    Linux 2022年8月26日
    0280
  • linux定时任务crontab的设置

    linux定时任务crontab的设置http://www.blogjava.net/freeman1984/archive/2010/09/23/332715.html vi /…

    Linux 2022年8月24日
    0240
  • Ubuntu 14.04 安装 JDK 8,ubuntu14.04

    1、到 Sun 的官网下载 选择 accept license ,然后选择适合自己机型的JDK下载。 2、解压文件,修改文件名 3、添加环境变量 加入如下内容 4、配置默认JDK版…

    Linux 2022年8月26日
    0260
  • Linux: rename

    1. 简介 用于文件重命名, 通过字符串替换的方式修改文件名 2. 版本 rename –version上述命令返回的信息包含 util-linux 则表示为C语言版本…

    Linux 2022年8月20日
    0240
  • 【Git】【5】提交部分文件

    前言: 有的时候我们只想提交更改的部分文件上去,而不是全部提交 正文: 第1步:查看本地仓库的状态,看看有哪些文件更改过了,以及路径是什么: git status 第2步:添加部分…

    Linux 2022年8月30日
    0280
  • github下载太慢的解决办法

    请输入博文的阅读密码: Original: https://www.cnblogs.com/hustdc/p/11962911.htmlAuthor: PhoenixTree(梧桐…

    Linux 2022年8月30日
    0290
  • Skywalking【分布式链路追踪】

    1、Skywalking概述 1.1、什么是APM系统 1.1.1、APM系统概述 APM(Applicaiton performance management)即应用性能管理系统…

    2022年8月26日
    0680
  • linux shell和windows bat编写

    windows @echo off setlocal enabledelayedexpansion for /f "tokens=1-5" %%a in (‘n…

    Linux 2022年8月11日
    0550

发表回复

登录后才能评论
免费咨询
免费咨询
扫码关注
扫码关注
联系站长

站长Johngo!

大数据和算法重度研究者!

持续产出大数据、算法、LeetCode干货,以及业界好资源!

2022012703491714

微信来撩,免费咨询:xiaozhu_tec

分享本页
返回顶部