linux的两种共享内存方式---mmap和shmat区别

2023-05-16

linux中的两种共享内存。一种是我们的IPC通信System V版本的共享内存,另外的一种就是我们今天提到的存储映射I/O(mmap函数)

在说mmap之前我们先说一下普通的读写文件的原理,进程调用read或是write后会陷入内核,因为这两个函数都是系统调用,进入系统调用后,内核开始读写文件,假设内核在读取文件,内核首先把文件读入自己的内核空间,读完之后进程在内核回归用户态,内核把读入内核内存的数据再copy进入进程的用户态内存空间。实际上我们同一份文件内容相当于读了两次,先读入内核空间,再从内核空间读入用户空间。

Linux提供了内存映射函数mmap, 它把文件内容映射到一段内存上(准确说是虚拟内存上), 通过对这段内存的读取和修改, 实现对文件的读取和修改,mmap()系统调用使得进程之间可以通过映射一个普通的文件实现共享内存。普通文件映射到进程地址空间后,进程可以向访问内存的方式对文件进行访问,不需要其他系统调用(read,write)去操作。

mmap图示例:
在这里插入图片描述
mmap系统调用介绍

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

这就是mmap系统调用的接口,mmap函数成功返回指向内存区域的指针,图上的进程的地址空间的开始地址就是mmap函数的返回值,失败返回MAP_FAILED。

addr,某个特定的地址作为起始地址,当被设置为NULL,系统会在地址空间选择一块合适的内存区域。

length说的是内存段的长度。

prot是用来设定内存段的访问权限。
在这里插入图片描述
fd参数是用来被映射文件对应的文件描述符。通过open系统调用得到。offset设定从何处进行映射。

mmap使用注意事项:

在这里插入图片描述
利用mmap进行非血缘进程间通信代码:

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<fcntl.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<sys/mman.h>
#include<string.h>
 
struct STU
{
	int age;
	char name[20];
	char sex;
};
 
int main(int argc,char *argv[]) //这个进程用于创建映射区进行写。
{
	if(argc != 2)
	{
		printf("./a,out  file");
		exit(1);
	}
 
	struct STU student = {10,"xiaoming",'m'};
 
	int fd = open(argv[1],O_RDWR|O_CREAT|O_TRUNC,0644);
	if(fd < 0)
	{
		perror("open");
		exit(2);
	}
	ftruncate(fd,sizeof(struct STU)); //文件拓展大小。
	
	struct STU *p = (struct STU*)mmap(NULL,sizeof(struct STU),PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);//创建一个结构体大小的共享映射区。共享映射区我们可以当做数组区看待。
	if(p == MAP_FAILED)
	{
		perror("mmap");
		exit(3);
	}
	close(fd); //关闭不用的文件描述符。
	while(1)
	{
		memcpy(p,&student,sizeof(student));
		student.age++;
		sleep(1);
	}
	int ret = munmap(p,sizeof(student));
	if(ret < 0)
	{
		perror("mmumap");
		exit(4);
	}
	return 0;
}

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<fcntl.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<sys/mman.h>
 
struct STU
{
	int age;
	char name[20];
	char sex;
};
 
int main(int argc,char *argv[]) //这个进程读
{
	if(argc != 2)
	{
		printf("./a,out  file");
		exit(1);
	}
	int fd = open(argv[1],O_RDONLY,0644);
	if(fd < 0)
	{
		perror("open");
		exit(2);
	}
	struct STU student;
	struct STU *p = (struct STU*)mmap(NULL,sizeof(struct STU),PROT_READ,MAP_SHARED,fd,0);
	if(p == MAP_FAILED)
	{
		perror("mmap");
		exit(3);
	}
	close(fd);
	int i = 0;
	while(1)
	{
		printf("id = %d\tname = %s\t%c\n",p->age,p->name,p->sex);	
		sleep(2);
	}
	int ret = munmap(p,sizeof(student));
	if(ret < 0)
	{
		perror("mmumap");
		exit(4);
	}
	return 0;

运行结果:
在这里插入图片描述
在这里插入图片描述
分析:因为只创建一个结构体大小的共享内存,后面写入的数据把前面写入的数据覆盖了。

shm共享内存多个进程的地址空间映射到同一个物理内存,多个进程都能看到这块物理内存,共享内存可以提供给服务器进程和客户进程之间进行通信,不需要进行数据的复制,所以速度最快。

shm内存模型图:
在这里插入图片描述

内核为每一个共享内存段维护着一个特殊的数据结构,就是shmid_ds,这个结构在include/linux/shm.h中定义

如下:
ipc_perm 结构定义于中,原型如下:
struct ipc_perm
{
key_t        key;                        调用shmget()时给出的关键字
uid_t           uid;                      /*共享内存所有者的有效用户ID */
gid_t          gid;                       /* 共享内存所有者所属组的有效组ID*/
uid_t          cuid;                    /* 共享内存创建 者的有效用户ID*/
gid_t         cgid;                   /* 共享内存创建者所属组的有效组ID*/
unsigned short   mode;    /* Permissions + SHM_DEST和SHM_LOCKED标志*/
unsignedshort    seq;          /* 序列号*/
};

struct shmid_ds{
      struct ipc_perm shm_perm;/* 操作权限*/
       int shm_segsz;                    /*段的大小(以字节为单位)*/
      time_t shm_atime;          /*最后一个进程附加到该段的时间*/
       time_t shm_dtime;          /*最后一个进程离开该段的时间*/
      time_t shm_ctime;          /*最后一个进程修改该段的时间*/
      unsigned short shm_cpid;   /*创建该段进程的pid*/
       unsigned short shm_lpid;   /*在该段上操作的最后1个进程的pid*/
       short shm_nattch;          /*当前附加到该段的进程的个数*/
/*下面是私有的*/
        unsigned short shm_npages;  /*段的大小(以页为单位)*/
      unsigned long *shm_pages;   /*指向frames->SHMMAX的指针数组*/
      struct vm_area_struct *attaches; /*对共享段的描述*/
};
  1. shmget函数原型
    在这里插入图片描述
    在这里插入图片描述
    在Linux环境中,对开始申请的共享内存空间进行了初始化,初始值为0x00。

如果用shmget创建了一个新的消息队列对象时,则shmid_ds结构成员变量的值设置如下:

Ÿ shm_lpid、shm_nattach、shm_atime、shm_dtime设置为0。

Ÿ msg_ctime设置为当前时间。

Ÿ shm_segsz设成创建共享内存的大小。

Ÿ shmflg的读写权限放在shm_perm.mode中。

Ÿ shm_perm结构的uid和cuid成员被设置成当前进程的有效用户ID,gid和cuid成员被设置成当前进程的有效组ID。

  1. shmat函数原型
    在这里插入图片描述
    在这里插入图片描述
  2. shmdt函数原型
    在这里插入图片描述
  3. shmctl函数原型
    在这里插入图片描述
    在这里插入图片描述
    3.代码实现:

下面的例子是服务器进程进行创建共享内存,然后挂接,客户进程也挂接,然后服务器进程每2秒写入一个字符,

客户进程也接收到,打印,实现内存共享

comm.h

#ifndef _COMM_H_
#define _COMM_H_
 
 
#include<stdio.h>
#include<sys/shm.h>
#include<errno.h>
#include<sys/types.h>
#include<unistd.h>
 
#define PATHNAME "."
#define PROJ_ID 0x6666

int creat_shm(size_t size);
int get_shm();
int destroy_shm(int shmid);
#endif 
comm.c
#include"comm.h"
 
int common(size_t size,int flags)
{
	key_t key = ftok(PATHNAME,PROJ_ID);
	if (key < 0)
	{
		perror("ftok");
		return -1;
	}
	int shmid = shmget(key,size,flags);//得到共享内存的标识符shmid。
	if (shmid < 0)
	{
		perror("shmget");
		return -2;
	}
	return shmid;
}
 
int creat_shm(size_t size)
{
	return common(size,IPC_CREAT|IPC_EXCL|0x666);
}
 
int get_shm()
{
	return common(0,IPC_CREAT);
}
int destroy_shm(int shmid)
{
	if(shmctl(shmid,IPC_RMID,NULL)<0)
	{
		perror("shmctl");
		return -3;
	}
	return 0;
}
server.c

#include"comm.h"
 
int main()
{
	int shmid = creat_shm(4096);
	printf("shmid = %d\n",shmid);
	sleep(3);
	char *buf = shmat(shmid,NULL,0);//挂接//shmat第二个参数为空,表示系统分配地址空间,返回一个虚拟地址空间。
	printf("buf = %p\n",buf);
	int i = 0;
 
	while (1)
	{
		buf[i] = 'A'+i%26;
	i++;
		buf[i] = 0;
		sleep(2);
	}
	shmdt(buf); //去挂接。
	sleep(3);
	destroy_shm(shmid);
 
	return 0;
}
client.c
#include"comm.h"

int main()
{
	int shmid = get_shm();
	sleep(3);
	char *buf = shmat(shmid,NULL,0);//新的进程挂接到那块共享内存空间,能看到共享内存中的东西。
	
	while (1)
	{
		printf("%s\n",buf);
		sleep(2);
	}
	shmdt(buf);
	return 0;
}

在这里插入图片描述
在这里插入图片描述

总结mmap和shm:
1、mmap是在磁盘上建立一个文件,每个进程地址空间中开辟出一块空间进行磁盘文件映射。
而对于shm而言,shm每个进程最终会映射到同一块物理内存。shm保存在物理内存,这样读写的速度要比磁盘要快,但是存储量不是特别大。

2、另外mmap有一个好处是当机器重启,因为mmap把文件保存在磁盘上,这个文件还保存了操作系统同步的映像,所以mmap不会丢失,但是shmget就会丢失。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

linux的两种共享内存方式---mmap和shmat区别 的相关文章

随机推荐

  • 复旦微开发过程中遇到的问题总结(二)

    一 xff0c 将bin文件放到flash中0地址处能识别并且启动吗 xff1f xlinx的放在0地址处可以识别启动 xff0c 我尝试复旦微这个没反应 要用procise生成 xff0c 第一个必须是FSBL out 只能是procis
  • 用链表实现fifo功能缓存和拼接数据功能

    fifo h ifndef LIST QUEUE H define LIST QUEUE H include lt stdio h gt include lt stdlib h gt include 34 xil types h 34 in
  • zynq bootgen配置启动

    一 xff0c Zynq 7000 SoC 启动头文件 0x00 0x1F Arm 矢量表 由 Bootgen 使用虚拟矢量表填充 xff08 Arm 操作代码 0xEAFFFFFE xff0c 即用于捕获未初始化矢量的 branch to
  • 制作四个文件启动的镜像

    一 环境搭建 xff1a vivado2018 3 xff0c petalinux2018 3 xff0c 1 petalinux环境设置 所使用的编译环境需要使用petalinux这个软件 xff0c 第五章Petalinux 的安装 里
  • ubuntu虚拟机更改镜像源(中科大或者阿里云镜像源)

    ubuntu虚拟机更改镜像源 xff08 中科大或者阿里云镜像源 xff09 1 进入终端后 xff0c 编辑源列表文件 xff1a 输入 xff1a sudo vim etc apt sources list 后输入 xff1a i 2
  • 海康威视客户端iVMS-4200连接NVR

    海康威视客户端 iVMS 4200 连接 NVR 陈拓 2021 07 30 2021 08 01 1 概述 iVMS 4200 客户端是一款与网络监控设备配套使用的综合应用软件 xff0c 可满足用户多方面需求 xff0c 如设备管理 人
  • 匿名上位机使用方法分享--总体介绍

    不知不觉 xff0c 匿名科创已经走过了7个年头 xff0c 这里首先要感谢大家这么久以来对匿名的支持与帮助 xff01 匿名为了提供给大家一个更好的调试工具 xff0c 始终在维护开发我们的匿名上位机软件 xff0c 7年时间 xff0c
  • 匿名上位机使用方法分享--高级收码

    匿名上位机总体介绍移步 xff1a https blog csdn net wangjt1988 article details 83684188 本文视频介绍 xff1a https www bilibili com video av35
  • 匿名上位机使用方法分享--波形显示

    匿名上位机总体介绍移步 xff1a https blog csdn net wangjt1988 article details 83684188 波形显示可以说是上位机的功能重点 xff0c 是各种调试 数据分析的有力助手 xff0c 下
  • 匿名数传使用方法分享

    目录 欢迎使用匿名数传模块匿名数传的特点硬件介绍使用介绍指示灯连接匿名飞控建议 欢迎使用匿名数传模块 大家调试各种设备时 xff0c 一般用什么方式呢 xff1f 相比答案大多是上位机 43 串口的方式 如果您还在使用usb转串口芯片然后连
  • 匿名科创--X2212版到手飞套件介绍

    匿名科创到手飞X2212版 xff0c 使用朗宇X2212系列无刷电机 xff0c 配合特制的6mm正反螺纹螺旋桨安装柱 xff0c 可以同时兼容8寸普通螺旋桨和9寸9450自锁螺旋桨 优点 xff1a 可直接使用普通8寸螺旋桨 xff0c
  • vscode最皮实的C++格式化的配置方法

    1 安装C C 43 43 2 在vscode界面 xff0c 按 34 Ctrl 43 34 进入设置界面 xff0c 搜索Format 3 设置保存文件时 xff0c 按格式对代码排版 4 向下拉 xff0c 找到下图选项 xff0c
  • 通过openmv生成apriltag标签

    Apriltag官网提供的tag图片分辨率很低 xff0c 完全无法使用 xff0c 通过openmv生成apriltag标签 生成方法如下 xff1a openmv IDE的下载与安装 openmv官方提供了各种版本的IDE xff0c
  • 串口传输数据错位 的几种解决办法

    1 代码优化等级 2 使用晶振 晶振自身产生时钟信号 xff0c 为各种微处理芯片作时钟参考 无源晶振需要用CPU内部的振荡器信号差接线麻烦石英 gt 陶瓷有源晶振是一个完整的振荡器信号好接线简单灵活性较差 3 使用降低传输速率 xff1f
  • sip 认证分析

    SIP类似Http协议 其认证模式也一样 Http协议 xff08 RFC 2616 xff09 规定可以采用Basic模式和摘要模式 xff08 Digest schema xff09 RFC 2617 专门对两种认证模式做了规定 RFC
  • MicroPython移植

    MicroPython移植 1 目标板 stm32f407zgt6 2 下载移植准备 micropython源码 arm交叉编译工具 sudo apt get install git sudo apt get install gcc arm
  • 了解ESP32睡眠模式及其功耗

    陈拓翻译 2022 05 30 2022 05 30 原文 https lastminuteengineers com esp32 sleep modes power consumption 毫无疑问 xff0c ESP32是许多WiFi
  • 浅谈布隆过滤器

    什么是布隆过滤器 布隆过滤器是一种数据结构 xff0c 比较巧妙的概率型数据结构 xff08 probabilistic data structure xff09 xff0c 特点是高效地插入和查询 xff0c 可以用来告诉你 某样东西一定
  • 浅谈CGI基本原理和底层基本实现

    历史来由 xff1a 早期的Web服务器 xff0c 只能响应浏览器发来的HTTP静态资源的请求 xff0c 并将存储在服务器中的静态资源返回给浏览器 随着Web技术的发展 xff0c 逐渐出现了动态技术 xff0c 但是Web服务器并不能
  • linux的两种共享内存方式---mmap和shmat区别

    linux中的两种共享内存 一种是我们的IPC通信System V版本的共享内存 xff0c 另外的一种就是我们今天提到的存储映射I O xff08 mmap函数 xff09 在说mmap之前我们先说一下普通的读写文件的原理 xff0c 进