Linux学习笔记之八(进程间的共享内存)

Linux

  • 1、引言
  • 2、实现共享内存
    • 2.1、创建一个共享内存
    • 2.2、将共享内存链接到进程空间
    • 2.3、断开与共享内存的链接
    • 2.4、对共享内存进行后续操作
  • 3、应用实例

1、引言

在之前一篇文章Linux学习笔记之六(进程之间的管道通信和信号处理)中我讲了进程间可以通过管道通信。管道通信也是在内存中某个地方开辟一块内存,让不同进程可以访问它,进而实现通信的目的。
而本文所讲的共享内存也大概是这么一个逻辑,不过相较与管道通信,内存共享实现起来更加复杂,也没有读写阻塞功能,但执行起来的效率也更加高,似乎是最快的进程通信方式。

2、实现共享内存

共享内存其任务在于从物理内存中申请一块内存区域,然后将这个区域映射到不同进程的空间中。进而进程访问该共享区域,进行读写操作。下图截取于B站,该图比较清晰明了说明了共享内存的运行逻辑。从这张图可以看出,任何进程都有自己的空间,而这些空间本质是取之于物理内存,但取的顺序则是杂乱的。共享内存无非是一块大家都可以取走的内存块而已。
在这里插入图片描述
接下来说明实现共享内存的基本步骤:
第一步:创建一个共享内存
第二步:将共享内存链接到进程空间
第三步:断开与共享内存的链接
第四步:对共享内存进行后续操作(如销毁)

2.1、创建一个共享内存

创建一个共享内存用到的函数是shmget(share memory get),其函数原型如下:

int shmget(key_t key, size_t size, int shmflag);

参数概览:

key:设置该共享内容的关键字标识。
size:指定共享内存的空间大小。
shmflag:设置共享内存的访问权限。
返回值:创建成功则返回共享内存标识符,失败则返回-1.

参数详解:
1. key参数:key 参数是一个用于标识共享内存段的关键字。它允许多个进程通过使用相同的 key 参数来访问同一个共享内存段。这就是说,如果多个进程想要访问同一个共享内存段,它们必须提供相同的 key 参数值。
通常情况下,key 参数可以是一个由 ftok() 函数生成的键值(通过给定文件路径名和一个项目标识符来生成唯一的键值),也可以是手动指定的一个键值,也可以通过一些宏指定一些key值。
关于ftok函数,其函数原型如下:

key_t ftok(const char *pathname, int proj_id);

第一个参数是真实存在的文件路径,第二个参数是项目的id,可以自己设定。
计算机会根据这两个参数的组合,生成一个独一无二的key值,保证不同进程可以访问同一个内存空间。
2. size参数:一般而言我们会设置成4096的整数倍,因为计算机的最小内存单元就是4kB,但如果不设置成4096的整数倍也无所谓,计算机会根据自动帮你调整到合适的值。比如你设置2009,计算机也会帮你调整到4096.
2. shmflag参数:该参数用于设置共享内存的访问权限,一般由一些宏和四位整数组成,比如0777 | IPC_CREAT。权限的设置相信大家都耳熟能详了,第一位0表示后面的三位数字是八进制数字,777分别表示,用户、用户组、其他人的权限。7会被拆解位111,分别表示rwx(读写执行)三个权限。
常见的宏有:
IPC_CREAT:若共享内存存在则打开,否则创建一个。
IPC_EXCL:若共享内存不存在则创建,否则报错。
IPC_NOWAIT:若该操作需要等待,直接报错。

最后是调用该函数需要头文件(后文提到的所有函数也需要包含这几个头文件):

#include "sys/types.h"
#include "sys/ipc.h"
#include "sys/shm.h"

2.2、将共享内存链接到进程空间

在这一步用到函数是shmat,其函数原型如下:

void *shmat(int shmid, const void *addr, int shmflg);

参数概览:

shmid:这里输入的是共享内存的标识符,其实就是shmget函数的返回值。
void *addr:指定链接到进程中的哪个地址块,一般不需要指定,由计算机自动分配即可。
int shmflg:标志位,一般设置为0即可。
返回值:成功则返回共享内存在进程空间中的链接地址,失败则返回-1。

注:void *指的是无类型指针,无类型换一种理解方式就是,可以赋予它任意类型。
通过这个步骤,我们拿到了链接地址,再用这个地址来索引相应的内存空间之后,便可以进行读写操作,以实现进程通信。

2.3、断开与共享内存的链接

进程通信结束之后自然是要将进程与共享内存之间的链接断开。这一步用到的函数时shmdt,其函数原型如下:

int shmdt(const void *address);

void *address:该地址指的进程空间中的链接地址。一般就是shmat中的返回值了。
返回值:成功则返回0,失败则返回-1。

2.4、对共享内存进行后续操作

进程通信结束,链接也断开,下一步自然是要考虑如何回收或销毁这个共享内存了。用到的函数时shmctl,其函数原型如下:

int shmctl(int shm_id, int command, struct shmid_ds *buf);

shm_id:共享内存对象的id,即内存的标识符。一般时shmget的返回值。
command:指定对该内存的操作命令,主要有删除命令(IPC_RMID)。
shmid_ds *buf:该参数用于获取内核结构的结构值,一般也不用。故直接设置为0即可。

最后注意一点,如果使用调用shmctl(shmid, IPC_RMID, 0)这句话,最后删除的只是共享内存的标识符,使系统不再维护该内存标识符,而不是真正把共享内存释放掉。如果想要共享内存被释放掉,则需要等到该内存没有被任何进程调用时,系统才会自动被释放掉。

3、应用实例

本例子的目的:创建两个进程A和B,A进程往共享内存写入数据,B进程将数据读出来。
故,首先创建一个进程A,去往一块共享内存写入数据。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "sys/types.h"
#include "sys/ipc.h"
#include "sys/shm.h"int main()
{key_t key  = ftok("$HOME/shared_memory/program_A.c", 9);        //generate a unique keyint shmid = shmget(key, 4096, 0751|IPC_CREAT);                  //create a shared memorychar *addr = shmat(shmid, 0, 0);        //link the shared memory with this program               char buffer[100];           //create a buffer for writing datawhile(1){printf("\nPlease input a character: ");fgets(buffer, sizeof(buffer), stdin);if(strcmp(buffer, "q\n") == 0)              //judge whether what I input is 'q' or notbreak;                                  strncpy(addr, buffer, sizeof(buffer));          //writing data into the shared memory}if(shmdt(addr) == 0){printf("disconnect shared memory successfully!\n");}else{printf("fail to disconnect!\n");}if(shmctl(shmid, IPC_RMID, 0) == 0){printf("delete the identifer successfully!\n");}else{printf("the identifer already be deleted!\n");}return 0;
}

其次,创建一个进程B,从共享内存中读取数据。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "sys/types.h"
#include "sys/ipc.h"
#include "sys/shm.h"int main()
{key_t key  = ftok("$HOME/shared_memory/program_A.c", 9);      int shmid = shmget(key, 4096, 0751|IPC_CREAT);                  char *addr = shmat(shmid, 0, 0);                     while(1){char input;printf("please input a character: ");scanf("%c", &input);if(input == 'q')break;int c;while ((c = getchar()) != '\n' && c != EOF) {}printf("what i receive is: %s\n", addr);}if(shmdt(addr) == 0){printf("disconnect successfully!\n");}else{printf("fail to disconnect!\n");}if(shmctl(shmid, IPC_RMID, 0) == 0){printf("delete successfully!\n");}else{printf("the identifer already be deleted!\n");}return 0;
}

最后,我打开两个终端,同时运行进程A和B,运行结果如下:
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/256959.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Unity中Batching优化的GPU实例化(2)

文章目录 前言一、GPU实例化的Shader准备步骤1、在Pass中声明实例化需要的变体2、UNITY_VERTEX_INPUT_INSTANCE_ID 在顶点着色器的输入(appdata)和输出(v2f可选)中添加(uint instanceID : SV_InstanceID). 前言 在上篇文章中&#xff0c;我们做了一些GPU实例化的前置准备&…

使用arcpy移除遥感影像云层

先讲思路&#xff0c;然后上代码&#xff1a; 去除云层 思路1&#xff1a; 如果同一地理区域的多个图像&#xff0c;其中一些部分有丰富的云&#xff0c;而另一些部分没有云&#xff0c;则可以将它们组合起来&#xff0c;以便无云的部分替代多云的部分。这种方法很简单&…

[原创][6]探究C#多线程开发细节-“ConcurrentDictionary<T,T>解决多线程的无顺序性的问题“

[简介] 常用网名: 猪头三 出生日期: 1981.XX.XX QQ联系: 643439947 个人网站: 80x86汇编小站 https://www.x86asm.org 编程生涯: 2001年~至今[共22年] 职业生涯: 20年 开发语言: C/C、80x86ASM、PHP、Perl、Objective-C、Object Pascal、C#、Python 开发工具: Visual Studio、D…

Data Mining数据挖掘—2. Classification分类

3. Classification Given a collection of records (training set) – each record contains a set of attributes – one of the attributes is the class (label) that should be predicted Find a model for class attribute as a function of the values of other attribu…

从传统到胜利:广汽集团汽车产业创新之旅

置身于汽车行业百年未有之大变局&#xff0c;作为传统车企中的排头兵&#xff0c;广汽创新可圈可点&#xff0c;广汽近年来取得了骄人业绩&#xff0c;不论是整体产销规模&#xff0c;还是新能源汽车产业化、新技术领域开拓等&#xff0c;都呈现节节攀升的局面。本文奖从产业变…

基于ssm少儿编程管理系统源码和论文

idea 数据库mysql5.7 数据库链接工具&#xff1a;navcat,小海豚等 环境&#xff1a; jdk8 tomcat8.5 开发技术 ssm 基于ssm少儿编程管理系统源码和论文744 摘要 网络的广泛应用给生活带来了十分的便利。所以把少儿编程管理系统与现在网络相结合&#xff0c;利用java技术建设…

使用Caliper对Fabric地basic链码进行性能测试

如果你需要对fabric网络中地合约进行吞吐量、延迟等性能进行评估&#xff0c;可以使用Caliper来实现&#xff0c;会返回给你一份网页版的直观测试报告。下面是对test-network网络地basic链码地测试过程。 目录 1. 建立caliper-workspace文件夹2. 安装npm等3. calipe安装4. 创建…

6-4 是否二叉搜索树 分数 20

bool IsBST(BinTree T) {//空树 or 只有一个结点if (T NULL || (T->Left NULL) && (T->Right NULL))return true;BinTree cur NULL;cur T->Left;if (cur ! NULL){while (cur->Right)cur cur->Right;if (cur->Data > T->Data)return fals…

vue 商品列表案例

my-tag 标签组件的封装 1. 创建组件 - 初始化 2. 实现功能 (1) 双击显示&#xff0c;并且自动聚焦 v-if v-else dbclick 操作 isEdit 自动聚焦&#xff1a; 1. $nextTick > $refs 获取到dom&#xff0c;进行focus获取焦点 2. 封装v-focus指令 (2) 失去焦点&#xff0c;隐藏…

【Flink系列四】Window及Watermark

3.1、window 在 Flink 中 Window 可以将无限流切分成有限流&#xff0c;是处理有限流的核心组件&#xff0c;现在 Flink 中 Window 可以是时间驱动的&#xff08;Time Window&#xff09;&#xff0c;也可以是数据驱动的&#xff08;Count Window&#xff09;。 Flink中的窗口…

Python----多态

1、什么是多态 多态指的是一类事物有多种形态。 定义&#xff1a;多态是一种使用对象的方式&#xff0c;子类重写父类方法&#xff0c;调用不同子类对象的相同父类方法&#xff0c;可以产生不同的执行结果。 ① 多态依赖继承 ② 子类方法必须要重写父类方法 首先定义一个父类…

VC++使用GetProcessTimes获取进程创建时间、销毁时间、用户态时间、内核态时间

一、GetProcessTimes函数简介&#xff08;微软MSDN&#xff09; 微软提供了一个非常有用的API函数GetProcessTimes用来获取进程创建时间、销毁时间、用户态时间、内核态时间&#xff0c;msdn连接为&#xff1a;GetProcessTimes 函数 (processthreadsapi.h) 其函数原型为&#…