Linux之进程间通信(共享内存【mmap实现+系统V】)

目录
  • 共享内存
  • mmap()及其相关的系统调用
    • mmap()
    • munmap()
  • 共享内存的使用
  • 命令管理共享内存
  • 总结

共享内存

  • 共享内存可以说是最有用的进程间通信方式,也是最快的IPC形式,两个不同的进程A、B共享内存的意思就是:同一块物理内存被映射到进程A、B各自的进程地址空间,进程A可以同时看到进程B对共享内存中数据的更新,反之亦然。
  • 由于个多个进程共享同一块内存区域,必然需要某种同步机制、互斥锁和信号量都可以。

好处: 效率高,进程可以直接读写内存,而不需要复制任何数据,而管道、消息队列等通信方式,则需要在内核和用户空间进行四次数据复制。

并且只有在解除映射时,共享内存的内容才会写会文纪念

共享内存通过内核对象,使得不同的进程在自己的虚拟地址空间上分配一块空间映射到相同的物理内存空间上,这块物理内存空间对于映射到上面的每个进程而言都是可以访问的。(临界资源)

共享内存就是允许两个不相关的进程访问同一个逻辑内存

共享内存是在两个正在运行的进程之间共享和传递数据的一种非常有效的方式。

不同进程之间共享的内存通常安排为同一段物理内存。

进程可以将同一段共享内存连接到它们自己的地址空间中,所有进程都可以访问共享内存中的地址,就好像它们是由用C语言函数malloc()分配的内存一样。

而如果某个进程向共享内存写入数据,所做的改动将立即影响到可以 访问同一段共享内存的任何其他进程。

mmap()及其相关的系统调用

mmap是linux操作系统提供给用户空间调用的内存映射函数,很多人仅仅只是知道可以通过mmap完成进程间的内存共享和减少用户态到内核态的数据拷贝次数,但是并没有深入理解mmap在操作系统内部是如何实现的,原理是什么

mmap()系统调用使得进程之间可以通过映射同一个普通文件实现内存共享。普通文件被映射到进程地址空间后,进程可以访问普通内存一样对文件进行访问,不必再调用read和write操作。

注意: mmap并不是完全为了IPC而设计的,只是IPC的一种应用方式,它本身提供了一种像访问普通内存一样的访问对普通文件进行操作的方式。

通过使用带有特殊权限集的虚拟内存段来实现。对这类虚拟内存段的读写会使操作系统去读写磁盘文件中与之对应的部分。

mmap 函数创建一个指向一段内存区域的指针,该内存区域与可以通过一个打开的文件描述符访问的文件的内容相关联

解释如下:

mmap()

#include <sys/mman.h>

void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);

可以通过传递 offset 参数来改变经共享内存段访问的文件中数据的起始偏移值。

打开的文件描述符由 fd 参数给出。

可以访问的数据量(即内存段的长度)由 length 参数设置。

可以通过 addr 参数来请求使用某个特定的内存地址。如果它的取值是零,结果指针就将自动分配。这是推荐的做法,否则会降低程序的可移植性,因为不同系统上的可用地址范围是不一样的。

prot 参数用于设置内存段的访问权限。它是下列常数值的按位或的结果

  • PROT_READ 内存段可读。
  • PROT_WRITE 内存段可写。
  • PROT_EXEC 内存段可执行。
  • PROT_NONE 内存段不能被访问。

flags 参数控制程序对该内存段的改变所造成的影响:

mmap()用于共享内存的量和两种方式如下:

使用普通文件提供的内存映射,适用于任何进程间,使用该方式需要先打开或者创建一个文件,再调用ngmmap,典型调用代码如下:

fd = open(name.falg.mode);
if(fd < 0)
ptr = mmap(NULL,len.PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);

使用特殊文件提供的内存映射,适用于具有亲缘关系的进程之间,由于父子进程特殊的亲缘关系,在父进程中先调用mmap,调用fork,那么在代用fork之后,子进程可以继承父进程匿名映射后的地址空间,同样也继承mmap返回的地址,这样父子进程就可以通过映射区域进行通信了。(注意:一般来说,子进程单独维护从父进程继承而来的一些变量,而mmap()返回的地址由父子进程共同维护)【具体使用实现敬请期待博主整理】

munmap()

用于解除内存映射,取消参数start所指的映射内存的起始地址,参数length则是欲取消的内存大小,当进程结束或者利用exec相关函数来执行其他程序时,映射内存会自动解除,但关闭对应的文件描述符时不会解除映射。

#include <sys/mman.h>

int munmap(void *addr, size_t length);

共享内存的使用

与信号量一样,在Linux中也提供了一组函数接口用于使用共享内存,而且使用共享共存的接口还与信号量的非常相似,而且比使用信号量的接口来得简单。它们声明在头文件 sys/shm.h 中。

1.获取或创建内核对象,并且制定共享内存的大小(系统分配物理空间是,按照页进行分配)

int shmget(key_t key, int size, int flag);

只是创建内核对象,并申请物理空间

  • key_t key:与信号量的semget函数一样,程序需要提供一个参数key(非0整数),它有效地为共享内存段命名,不同的进程通过相同的key值来访问同一块共享内存
  • int size:size以字节为单位指定需要共享的内存容量
  • int flag:falg是权限标志,它的作用与open函数的mode参数一样,如果要想在key标识的共享内存不存在时,创建它的话,可以与IPC_CREAT做或操作。共享内存的权限标志与文件的读写权限一样,举例来说,0644,它表示允许一个进程创建的共享内存被内存创建者所拥有的进程向共享内存读取和写入数据,同时其他用户创建的进程只能读取共享内存。

返回值

  • shmget()函数成功时返回一个与key相关的共享内存标识符(非负整数),用于后续的共享内存函数。
  • 调用失败返回-1.

2.分配自己虚拟地址空间映射到共享内存的物理空间上

void *shmat(int shmid,const void *addr, int flag);
  • shmid:shmid是由shmget()函数返回的共享内存标识。
  • void *addr:addr指定共享内存连接到当前进程中的地址位置,通常为NULL,表示让系统来选择共享内存的地址。
  • int flag:flag是一组标志位,通常为0。

调用成功时返回一个指向共享内存第一个字节的指针,如果调用失败返回-1.

3.断开当前进程与共享内存的映射

不使用删除而使用断开的原因是因为:也许还存在其他的进程会继续使用这块共享内存

int shmdt(const void *addr);

4.操作共享内存的方法

int shmctl(int shmid, int cmd, struct shmid_t *buf);
  • int shmid:shmid是shmget()函数返回的共享内存标识符。
  • int cmd:command是要采取的操作,它可以取下面的三个值 :

IPC_STAT:把shmid_ds结构中的数据设置为共享内存的当前关联值,即用共享内存的当前关联值覆盖shmid_ds的值。

IPC_SET:如果进程有足够的权限,就把共享内存的当前关联值设置为shmid_ds结构中给出的值

IPC_RMID:删除共享内存段

  • struct shmid_t *buf:buf是一个结构指针,它指向共享内存模式和访问权限的结构

因为有连接计数器,除非最后一个进程与该共享段断开连接,则删除该共享段。否则,并不会真正删除该共享段,但是共享内存的内核对象会被立即删除,不能使用shmat方法与该段连接。

一个进程调用该方法删除后,不会影响之前已经和该共享存储段连接的进程

下面我们利用共享内存来进行一个简单的测试:

完成下面的过程,

成功在共享内存中读到了数据

#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<string.h>
#include<unistd.h>

#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/shm.h>

#include"sem.h"

#define READSEM 1
#define WRITESEM 0

int main()
{
	int shmid = shmget((key_t)1234,128,0664 | IPC_CREAT);
	assert(shmid != -1);

	char *ptr = (char*)shmat(shmid,NULL,0);
	assert(ptr != (char*)-1);

	int initVal[] = {1,0};
	int semid = SemGet(1234,intVal,2);
	assert(semid != -1);

	//A进程写
	while(1)
	{
		SemP(semid,WRITESEM);
		printf("Input:");

		fgets(ptr,127,stdin);

		SemV(semid,READSEM);

		if(strncmp(ptr,"end",3) == 0)
		{
			break;
		}
	}

	shmdt(ptr);
	exit(0);
}
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<string.h>
#include<unistd.h>

#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/shm.h>

#include"sem.h"

#define READSEM 1
#define WRITESEM 0

int main()
{
	int shmid = shmget((key_t)1234,128,0664 | IPC_CREAT);
	assert(shmid != -1);

	char *ptr = (char*)shmat(shmid,NULL,0);
	assert(ptr != (char*)-1);

	int initVal[] = {1,0};
	int semid = SemGet(1234,intVal,2);
	assert(semid != -1);

	//B进程读
	while(1)
	{
		SemP(semid,READSEM);

		if(strncmp(ptr,"end",3) == 0)
		{
			break;
		}

		int i = 0;
		for(;i < strlen(ptr) - 1;i++)
		{
			printf("%c",toupper(ptr[i]));
			fflush(stdout);
			sleep(1);
		}
		printf("\n");
		SemV(semid,WRITESEM);
	}

	shmdt(ptr);
	exit(0);
}

从上面的代码中我们可以看出:

共享内存是最快的IPC,在通信过程中少了两次数据的拷贝。(相较于管道)

命令管理共享内存

  • 查看 ipcs -m
  • 删除 ipcrm -m shmid

总结

以上为个人经验,希望能给大家一个参考,也希望大家多多支持我们。

(0)

相关推荐

  • 浅谈Linux进程间通信方式及优缺点

    1)管道 管道分为有名管道和无名管道 无名管道是一种半双工的通信方式,数据只能单向流动,而且只能在具有亲缘关系的进程间使用.进程的亲缘关系一般指的是父子关系.无明管道一般用于两个不同进程之间的通信.当一个进程创建了一个管道,并调用fork创建自己的一个子进程后,父进程关闭读管道端,子进程关闭写管道端,这样提供了两个进程之间数据流动的一种方式. 有名管道也是一种半双工的通信方式,但是它允许无亲缘关系进程间的通信. 2)信号量 信号量是一个计数器,可以用来控制多个线程对共享资源的访问.,它不是用于交

  • 解决Linux system v 共享内存问题

    system v 共享内存 #include <sys/types.h> #include <sys/shm.h> int shmget(key_t key, size_t size, int shmflg); 建立:进程与共享内存的关联关系 key_t key:16进制的非0数字. 一般有两种方式设置它. 第一种:调用fotk函数 第二章:直接使用IPC_PRIVATE size:共享内存的大小 shmflg: IPC_CREAT IPC_EXCL 用户,组用户,其他用户对这片内

  • linux系统之进程管理详解

    目录 1.进程与线程的概念 2.什么是进程管理 3.进程管理的作用 4.Linux进程的几种状态 5.进程与线程的关系 (1)线程与进程的关系 (2)总结 1.进程与线程的概念 来源百度百科: 进程(Process) 是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础. 在当代面向线程设计的计算机结构中,进程是线程的容器.程序是指令.数据及其组织形式的描述,进程是程序的实体.是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和

  • Linux之进程间通信(共享内存【mmap实现+系统V】)

    目录 共享内存 mmap()及其相关的系统调用 mmap() munmap() 共享内存的使用 命令管理共享内存 总结 共享内存 共享内存可以说是最有用的进程间通信方式,也是最快的IPC形式,两个不同的进程A.B共享内存的意思就是:同一块物理内存被映射到进程A.B各自的进程地址空间,进程A可以同时看到进程B对共享内存中数据的更新,反之亦然. 由于个多个进程共享同一块内存区域,必然需要某种同步机制.互斥锁和信号量都可以. 好处: 效率高,进程可以直接读写内存,而不需要复制任何数据,而管道.消息队列

  • win32下进程间通信(共享内存)实例分析

    一.概述 很多情况下在Windows程序中,各个进程之间往往需要交换数据,进行数据通讯.WIN32 API提供了许多函数使我们能够方便高效的进行进程间的通讯,通过这些函数我们可以控制不同进程间的数据交换. 进程间通讯(即:同机通讯)和数据交换有多种方式:消息.共享内存.匿名(命名)管道.邮槽.Windows套接字等多种技术."共享内存"(shared memory)可以定义为对一个以上的进程是可见的内存或存在于多个进程的虚拟地址空间.例如:如果两个进程使用相同的DLL,只把DLL的代码

  • C#.Net通信共享内存映射文件Memory Mapped

    目录 内存映射文件究竟是个什么? .Net 共享内存 内存映射文件原理 C# .Net 共享内存 演示代码 C# .Net 进程间通信共享内存 IMServer_Message.exe 代码 IMServer_State.exe代码 节点通信存在两种模型:共享内存(Shared memory)和消息传递(Messages passing). 内存映射文件对于托管世界的开发人员来说似乎很陌生,但它确实已经是很远古的技术了,而且在操作系统中地位相当.实际上,任何想要共享数据的通信模型都会在幕后使用它

  • 详解Linux进程间通信——使用共享内存

    一.什么是共享内存 顾名思义,共享内存就是允许两个不相关的进程访问同一个逻辑内存.共享内存是在两个正在运行的进程之间共享和传递数据的一种非常有效的方式.不同进程之间共享的内存通常安排为同一段物理内存.进程可以将同一段共享内存连接到它们自己的地址空间中,所有进程都可以访问共享内存中的地址,就好像它们是由用C语言函数malloc分配的内存一样.而如果某个进程向共享内存写入数据,所做的改动将立即影响到可以访问同一段共享内存的任何其他进程. 特别提醒:共享内存并未提供同步机制,也就是说,在第一个进程结束

  • Python进程间通信之共享内存详解

    前一篇博客说了怎样通过命名管道实现进程间通信,但是要在windows是使用命名管道,需要使用python调研windows api,太麻烦,于是想到是不是可以通过共享内存的方式来实现.查了一下,Python中可以使用mmap模块来实现这一功能. Python中的mmap模块是通过映射同一个普通文件实现共享内存的.文件被映射到进程地址空间后,进程可以像访问内存一样对文件进行访问. 不过,mmap在linux和windows上的API有些许的不一样,具体细节可以查看mmap的文档. 下面看一个例子:

  • Linux共享内存实现机制的详解

    Linux共享内存实现机制的详解 内存共享: 两个不同进程A.B共享内存的意思是,同一块物理内存被映射到进程A.B各自的进程地址空间.进程A可以即时看到进程B对共享内存中数据的更新,反之亦然.由于多个进程共享同一块内存区域,必然需要某种同步机制,互斥锁和信号量都可以. 效率: 采用共享内存通信的一个显而易见的好处是效率高,因为进程可以直接读写内存,而不需要任何数据的拷贝.对于像管道和消息队列等通信方式,则需要在内核和用户空间进行四次的数据拷贝,而共享内存则只拷贝两次数据[1]: 一次从输入文件到

  • linux 共享内存

    概述 如果想在Apache/EAPI中具有共享内存的支持,那么就要建立MM共享内存库.在这种情况下,它将允许mod_ssl使用一种高效的基于RAM的会话(session)缓存代替基于磁盘的会话缓存. 注意事项 下面所有的命令都是Unix兼容的命令. 源路径都为"/var/tmp"(当然在实际情况中也可以用其它路径). 安装在RedHat Linux 6.1下测试通过. 要用"root"用户进行安装. Mm 的版本号是1.0.12. 软件包的来源 MM的主页: 必须确

  • Go语言共享内存读写实例分析

    本文实例分析了Go语言共享内存读写的方法.分享给大家供大家参考.具体分析如下: 前面分析了Go语言指针运算和内嵌C代码的方法,做了一个Go语言共享内存读写的实验. 先大概说下什么是共享内存.我们知道不同进程见的内存是互相独立的,没办法直接互相操作对方内的数据,而共享内存则是靠操作系统提供的内存映射机制,让不同进程的一块地址空间映射到同一个虚拟内存区域上,使不同的进程可以操作到一块共用的内存块.共享内存是效率最高的进程间通讯机制,因为数据不需要在内核和程序之间复制. 共享内存用到的是系统提供的mm

  • PHP共享内存用法实例分析

    本文实例讲述了PHP共享内存用法.分享给大家供大家参考,具体如下: 共享内存主要用于进程间通信 php中的共享内存有两套扩展可以实现 1.shmop  编译时需要开启 --enable-shmop 参数 实例: $shm_key = ftok(__FILE__, 't'); /** 开辟一块共享内存 int $key , string $flags , int $mode , int $size $flags: a:访问只读内存段 c:创建一个新内存段,或者如果该内存段已存在,尝试打开它进行读写

随机推荐