【Linux进程间通信】四、mmap共享存储映射

简介: 【Linux进程间通信】四、mmap共享存储映射

1. 什么是存储映射IO

存储映射I/O (Memory-mapped I/O) 使一个磁盘文件与存储空间(内存)中的一个缓冲区相映射。这样的话,当从缓冲区中取数据,就相当于读文件中的相应的字节,而将数据存入缓冲区,则相应的字节就自动写入文件。这样,就可在不使用read和write函数的情况下,使用地址(指针)完成I/O操作,当然也可以使用内存操作函数strcpy,memcpy等。

使用这种方法,首先应通知内核,将一个指定文件映射到存储区域中。这个映射工作可以通过mmap函数来实现。

2. mmap函数介绍

2.1 mmap函数创建映射区

  • 包含头文件
#include <sys/mman.h>
  • 函数原型
void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);
void *mmap64(void *addr, size_t length, int prot, int flags, int fd, off64_t offset);
int munmap(void *addr, size_t length);
  • 函数功能
    mmap() creates a new mapping in the virtual address space of the calling process. The starting address for the new mapping is specified in addr. The length argument specifies the length of the mapping.
  • 函数参数
  • addr:建立映射区的首地址,由Linux内核指定,使用时直接传递NULL 。
  • length:想要创建的映射区的大小 。
  • prot:映射区权限
  • PROT_READ:可读
  • PROT_WRITE:可写(PROT_READ | PROT_WRITE 读写)
  • PROT_EXEC:可执行
  • PROT_NONE:不可用
  • flags:标志位参数,常用于设定更新物理区域、设置共享、创建匿名映射区
  • MAP_SHARED:共享的,会将映射区所做的操作反映到物理设备(磁盘)上。也就是说,对内存的修改会影响到源文件。
  • MAP_PRIVATE:私有的,映射区所做的修改不会反映到物理设备(磁盘)。
  • fd:用来建立映射区的文件描述符,映射区是从文件映射来的,所以创建映射区肯定要打开一个文件。
  • offset:偏移量,映射文件的偏移(4k的整数倍)。
  • 函数返回值
  • On success, mmap() returns a pointer to the mapped area. 成功返回创建的映射区首地址。
  • On error, the value MAP_FAILED (that is, (void *) -1) is returned, and errno is set appropriately. 失败返回一个宏MAP_FAILED,并设置errno。

2.2 munmap函数释放映射区

  • 包含头文件
#include <sys/mman.h>
  • 函数原型
int munmap(void *addr, size_t length);
  • 函数功能
    释放mmap创建的映射区(可以联想malloc/free, new/delete等内存管理函数对)。
  • 函数参数
  • addr:mmap的返回值(由mmap返回的映射区首地址)
  • length:mmap创建的映射区的长度
  • 函数返回值
  • On success, munmap() returns 0.
  • On failure returns -1, and errno is set (probably to EINVAL).

3. mmap函数用法示例及注意事项

/************************************************************
  >File Name  : mmap_test.c
  >Author     : Mindtechnist
  >Company    : Mindtechnist
  >Create Time: 2022年05月22日 星期日 15时52分45秒
************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/mman.h>
int main(int argc, char* argv[])
{
    int fd = open("mem.txt", O_RDWR);
    /*int fd = open("mem.txt", O_RDWR | O_CREAT | O_TRUNC, 0644); 创建文件并截断
    ftruncate(fd, 8); 新创建文件必须进行扩展,大小不能为0*/
    char* mem = mmap(NULL, 8, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
    /*char* mem = mmap(NULL, 8, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0);*/
    if(mem == MAP_FAILED)
    {
        perror("mmap err");
        return -1;
    }
    /*我们操作的是内存,但是这种操作会被同步到文件*/
    strcpy(mem, "hello"); /*写入字符串,所以mmap()返回char*类型*/
    int mret = munmap(mem, 8);
    if(mret < 0)
    {
        perror("munmap err");
    }
    close(fd);
    return 0;
}

mmap函数使用时的注意事项:

  • 使用mmap()函数创建缓冲区返回的地址mem是多少,munmap()释放的时候传入的参数mem就是多少,不能更改,也就是说,要把mmap()返回的地址原模原样传进munmap()才能正常释放。(munmap传入的地址一定是mmap的返回地址,不能对地址指针进行++等位移操作。)
  • mem.txt文件的大小对映射区操作也是有影响的,如果我们设置的映射缓冲区为len,而实际上的mem.txt文件大小大于len,假如说我们写入映射区的内容大于len,只要不超过文件大小,也是可以写进去的。如果写入的内容大于文件的长度,那么超出文件长度的部分就被截断。
  • 文件偏移量必须为4K的整数倍。mmap()函数中,最后一个参数偏移量必须是4K的整数倍,这是操作系统的限制,文件最小门槛就是8个512,也就是4096。如果一个文件本身大小没有达到4K,那么它占用的空间大小也是4096。比如下面mem.txt实际上只有10字节大小,但是占据了8个512,这就证明文件最小占据4K空间。

  • 映射区的释放与文件关闭无关,只要映射建立成功,文件可以立即关闭。这是因为,只要使用mmap()函数映射完缓冲区,那么这个映射区和文件之间的通道已经建立,即使提前关闭文件描述符,也不影响对映射区和文件的操作。
  • 如果文件mem.txt的为0,也就是空文件,那么会报错“总线错误(核心已转储)”,所以映射时使用的文件大小不能是0(当映射文件大小为0时,不能创建映射区,用于映射的文件必须要有实际大小。mmap使用时常常会出现总线错误,通常是由于共享文件存储空间大小引起的)。

  • 创建映射区的过程中,隐含着一次对映射文件的读操作,所以open打开文件时,必须要有读权限。
  • 当MAP_SHARED时,要求:映射区的权限应小于等于文件打开的权限(出于对映射区的保护,因为映射区的操作会影响文件)。而MAP_PRIVATE则无所谓,因为mmap中的权限是对内存的限制。
  • mmap创建映射区时出错概率非常高,一定要检查返回值,确保映射区建立成功再进行后续操作。


相关文章
|
16天前
|
消息中间件 存储 Linux
|
1月前
|
消息中间件 Linux API
Linux c/c++之IPC进程间通信
这篇文章详细介绍了Linux下C/C++进程间通信(IPC)的三种主要技术:共享内存、消息队列和信号量,包括它们的编程模型、API函数原型、优势与缺点,并通过示例代码展示了它们的创建、使用和管理方法。
30 0
Linux c/c++之IPC进程间通信
|
1月前
|
Linux C++
Linux c/c++进程间通信(1)
这篇文章介绍了Linux下C/C++进程间通信的几种方式,包括普通文件、文件映射虚拟内存、管道通信(FIFO),并提供了示例代码和标准输入输出设备的应用。
26 0
Linux c/c++进程间通信(1)
|
3月前
|
消息中间件 Linux
Linux进程间通信
Linux进程间通信
43 1
|
3月前
|
开发者 API Windows
从怀旧到革新:看WinForms如何在保持向后兼容性的前提下,借助.NET新平台的力量实现自我进化与应用现代化,让经典桌面应用焕发第二春——我们的WinForms应用转型之路深度剖析
【8月更文挑战第31天】在Windows桌面应用开发中,Windows Forms(WinForms)依然是许多开发者的首选。尽管.NET Framework已演进至.NET 5 及更高版本,WinForms 仍作为核心组件保留,支持现有代码库的同时引入新特性。开发者可将项目迁移至.NET Core,享受性能提升和跨平台能力。迁移时需注意API变更,确保应用平稳过渡。通过自定义样式或第三方控件库,还可增强视觉效果。结合.NET新功能,WinForms 应用不仅能延续既有投资,还能焕发新生。 示例代码展示了如何在.NET Core中创建包含按钮和标签的基本窗口,实现简单的用户交互。
67 0
|
5天前
|
Linux
在 Linux 系统中,“cd”命令用于切换当前工作目录
在 Linux 系统中,“cd”命令用于切换当前工作目录。本文详细介绍了“cd”命令的基本用法和常见技巧,包括使用“.”、“..”、“~”、绝对路径和相对路径,以及快速切换到上一次工作目录等。此外,还探讨了高级技巧,如使用通配符、结合其他命令、在脚本中使用,以及实际应用案例,帮助读者提高工作效率。
22 3
|
5天前
|
监控 安全 Linux
在 Linux 系统中,网络管理是重要任务。本文介绍了常用的网络命令及其适用场景
在 Linux 系统中,网络管理是重要任务。本文介绍了常用的网络命令及其适用场景,包括 ping(测试连通性)、traceroute(跟踪路由路径)、netstat(显示网络连接信息)、nmap(网络扫描)、ifconfig 和 ip(网络接口配置)。掌握这些命令有助于高效诊断和解决网络问题,保障网络稳定运行。
17 2
|
13天前
|
缓存 监控 Linux
|
16天前
|
Linux Shell 数据安全/隐私保护
|
17天前
|
域名解析 网络协议 安全