Linux mmap系统调用

文章目录

  • 前言
  • 一、mmap()函数简介
  • 二、代码演示
    • 2.1 mmap使用场景
    • 2.2 私有匿名映射
    • 2.3 私有文件映射
    • 2.4 共享匿名映射
    • 2.5 共享文件映射
  • 参考

前言

NAMEmmap, munmap - map or unmap files or devices into memorySYNOPSIS#include <sys/mman.h>void *mmap(void *addr, size_t length, int prot, int flags,int fd, off_t offset);int munmap(void *addr, size_t length);

mmap函数用于将文件或设备映射到内存中。
mmap函数是一种内存映射文件的方法,它可以将一个文件或设备映射到进程的地址空间中,使得进程可以像访问内存一样访问文件或设备。
在这里插入图片描述

一、mmap()函数简介

mmap()函数在调用进程的虚拟地址空间中创建一个新的映射:

void *mmap(void *addr, size_t length, int prot, int flags,int fd, off_t offset);
RETURN VALUEOn  success,  mmap() returns a pointer to the mapped area.

通过这种方式,文件内容可以通过指针直接访问addr,就像访问普通的内存数组一样,这极大地提高了文件操作的效率和直观性。

以下是关于其行为的一些关键点:
(1)参数addr指定了新映射的起始地址。如果addr为NULL,内核会选择一个(页对齐的)地址来创建映射;这是创建新映射的最便携方法。如果addr不为NULL,内核将其作为放置映射的提示;对于Linux系统,内核会选择一个靠近的页边界(但始终大于或等于/proc/sys/vm/mmap_min_addr指定的值)并尝试在那里创建映射。如果该地址已经存在其他映射,内核会选择一个新的地址,可能与提示相关或不相关。新映射的地址将作为调用的结果返回。
(2)参数length指定映射的长度,必须大于0。
(3)对于文件映射(与匿名映射相对应,参见MAP_ANONYMOUS),映射的内容使用文件描述符fd引用的文件(或其他对象)中的从偏移量offset开始的length字节进行初始化。offset必须是sysconf(_SC_PAGE_SIZE)返回的页面大小的倍数。

总结一下,mmap()函数在调用进程的虚拟地址空间中创建一个新的映射,内核根据提供的地址或提示选择一个合适的地址来确定映射的起始位置。对于文件映射,映射的内容从文件中的指定偏移量处开始进行初始化。

在mmap()调用返回后,文件描述符fd可以立即关闭而不会使映射失效。

参数prot描述了映射的期望内存保护方式(不能与文件的打开模式冲突)。它可以是PROT_NONE,或者是以下标志位的按位或:

PROT_EXEC:页面可执行。
PROT_READ:页面可读取。
PROT_WRITE:页面可写入。
PROT_NONE:页面不可访问。

参数flags确定对映射的更新是否对其他映射同一区域的进程可见,以及是否将更新传递到底层文件。这个行为是通过在flags中包含以下值中的一个来确定的:
(1)MAP_SHARED:共享映射。对映射的更新对其他映射同一区域的进程可见,并且(对于基于文件的映射而言)会传递到底层文件。(要精确控制何时将更新传递到底层文件,需要使用msync(2)函数。)

使用MAP_SHARED标志可以实现共享内存,让多个进程可以共享同一区域的映射,并且对映射的更新可以相互可见。对于基于文件的映射,更新也会被传递到底层文件。需要注意的是,要精确控制更新何时传递到底层文件,可以使用msync(2)函数。

NAMEmsync - synchronize a file with a memory map

(2)MAP_PRIVATE:用于创建私有的写时复制(copy-on-write)映射。对映射的更新对于其他映射同一文件的进程不可见,并且不会传递到底层文件。在mmap()调用后对文件进行的更改是否在映射的区域中可见是未指定的。

使用MAP_PRIVATE标志可以创建一个独立的映射副本,对该映射的写入操作会在需要时进行写时复制,即只有在修改映射的页面时才会复制相应的页面内容,以确保每个进程都有自己的私有副本。这样,对映射的更新不会影响其他进程的映射,并且不会对底层文件进行实际的修改。

(3)MAP_ANONYMOUS:用于创建一个不由任何文件支持的映射,其内容被初始化为零。fd参数会被忽略;但是,一些实现要求如果指定了MAP_ANONYMOUS(或MAP_ANON),则fd必须为-1,因此可移植的应用程序应确保这一点。offset参数应为零。只有在Linux内核2.4及更高版本上,才支持将MAP_ANONYMOUS与MAP_SHARED结合使用。

使用MAP_ANONYMOUS标志创建的映射不与任何文件相关联,其内容被初始化为零。这种映射通常用于实现匿名内存,用于共享数据或作为临时存储。由于没有与文件的关联,对映射的更改不会影响任何文件,并且不需要指定文件描述符(fd参数被忽略)。

如下图所示:
在这里插入图片描述
Memory mmaping segment 就属于内存映射区。

二、代码演示

2.1 mmap使用场景

物理内存页主要分为两种:一种是匿名页,另一种是文件页。
根据物理内存页的类型分类,内存映射自然也分为两种:一种是虚拟内存对匿名物理内存页的映射,另一种是虚拟内存对文件页的映射。

(1)匿名页(Anonymous Pages):匿名页是一种没有与之关联的文件的内存页。它们通常用于存储进程的堆栈、堆分配的内存以及共享内存等。匿名页的内容在映射时可以初始化为零或未初始化状态,不会与任何文件进行关联。

(2)文件页(File Pages):文件页是与文件关联的内存页。它们用于将文件的内容映射到进程的地址空间,允许进程通过内存访问文件的内容,而无需直接进行读取和写入操作。文件页可以用于读取文件的内容,也可以用于将修改的数据写回文件。

(1)私有匿名映射:malloc分配大内存在glibc中对应的mmap()实现,以及BSS 段,堆,栈。
(2)私有文件映射:映射动态库,文件的text、data段。
(3)共享匿名映射:用于进程间(父子进程)共享内存。
(4)共享文件映射:用于进程间(不同的进程)共享内存,通信。
(5)其他,比如大页内存。

2.2 私有匿名映射

私有匿名映射使用一下标志位:

MAP_PRIVATE | MAP_ANONYMOUS

其中fd = -1,与文件没有关联。

#include <sys/mman.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>#define SIZE 4096int main() {// 创建一个私有匿名映射void* addr = mmap(NULL, SIZE, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);if (addr == MAP_FAILED) {perror("mmap failed");exit(1);}// 在映射的内存中进行读写操作int* data = (int*)addr;*data = 42;printf("Value at mapped memory: %d\n", *data);// 取消映射if (munmap(addr, SIZE) == -1) {perror("munmap failed");exit(1);}return 0;
}
# ./a.out
Value at mapped memory: 42

私有匿名映射(mmap/brk/malloc)申请的内存是一段虚拟地址空间,当没有在这段虚拟地址空间写入的时候,没有对应的物理内存,只有在这段虚拟地址空间写入的时候,就会发生缺页异常,然后分配对应的物理地址,建立虚拟地址空间和物理地址的影映射关系。
在这里插入图片描述
从上图我们可以看到进程虚拟内存空间中的 BSS 段,堆,栈这些虚拟内存区域都是私有匿名映射区域,glibc 中的 malloc函数当申请比较大的内存时,也使用私有匿名映射区域。

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>int main() {size_t size = 256 * 1024; // 128K内存的大小getpid();// 使用malloc分配内存char* buffer = (char*)malloc(size);if (buffer == NULL) {perror("malloc");exit(1);}getpid();// 内存分配成功,可以使用buffer指针访问分配的内存// 这里可以进行读取、写入或处理数据的操作// 释放内存free(buffer);return 0;
}
# strace ./a.out
......
getpid()                                = 101479
brk(NULL)                               = 0x55a399f12000
brk(0x55a399f33000)                     = 0x55a399f33000
mmap(NULL, 266240, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fec8b5ea000
getpid()                                = 101479
munmap(0x7fec8b5ea000, 266240)          = 0
mmap(NULL, 266240, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fec8b5ea000

2.3 私有文件映射

私有文件映射标志位:

MAP_PRIVATE

其中fd与文件有关联。

#include <sys/mman.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>#define FILE_PATH "example.txt"
#define SIZE 4096int main() {// 打开文件int fd = open(FILE_PATH, O_RDWR);if (fd == -1) {perror("open failed");exit(1);}// 获取文件大小struct stat st;if (fstat(fd, &st) == -1) {perror("fstat failed");exit(1);}off_t file_size = st.st_size;// 创建私有文件映射void* addr = mmap(NULL, file_size, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0);if (addr == MAP_FAILED) {perror("mmap failed");exit(1);}// 在映射的内存中进行读取操作char* data = (char*)addr;printf("Content of the file:%s\n", data);// 在映射的内存中进行写入操作sprintf(data, "Hello, World!");printf("Content of the file:%s\n", data);// 取消映射if (munmap(addr, file_size) == -1) {perror("munmap failed");exit(1);}// 关闭文件if (close(fd) == -1) {perror("close failed");exit(1);}return 0;
}

example.txt 文件的内容是 111。
读取其内容,然后写入:

# cat example.txt
111
# ./a.out
Content of the file:111Content of the file:Hello, World!
# cat example.txt
111

可以看到对私有文件映射区域的修改不会修改实际的文件。

私有文件映射允许多个进程将文件的内容映射到各自的虚拟内存空间中,但对映射的修改只反映到各自的文件页上,而不会影响其他进程的文件页。这种方式可以用于加载二进制可执行文件的代码段和数据段到进程的虚拟内存空间中以及加载动态库。
在这里插入图片描述
从上图我们可以看到进程虚拟内存空间中的 text 段,data 段和.so动态库这些虚拟内存区域都是私有文件映射区域。

2.4 共享匿名映射

私有匿名映射使用一下标志位:

MAP_SHARED | MAP_ANONYMOUS

其中fd = -1,与文件没有关联。

#include <sys/mman.h>
#include <unistd.h>
#include <sys/wait.h>
#include <stdio.h>
#include <stdlib.h>#define SIZE 4096int main() {// 创建共享匿名映射void* addr = mmap(NULL, SIZE, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0);if (addr == MAP_FAILED) {perror("mmap failed");exit(1);}// 创建子进程pid_t pid = fork();if (pid == -1) {perror("fork failed");exit(1);} else if (pid == 0) {// 子进程写入数据到共享内存char* data = (char*)addr;sprintf(data, "Hello from the child process!");// 子进程结束exit(0);} else {// 等待子进程结束wait(NULL);// 父进程读取共享内存中的数据char* data = (char*)addr;printf("Content of shared memory: %s\n", data);// 解除映射if (munmap(addr, SIZE) == -1) {perror("munmap failed");exit(1);}}return 0;
}
# ./a.out
Content of shared memory: Hello from the child process!

共享匿名映射在父子进程之间共享内存和实现进程间通信时非常有用。它是一种特殊的共享文件映射,不需要依赖具体的文件,而是将映射的内存区域与进程间共享。
父子进程通信:父进程可以创建一个共享匿名映射,并将其传递给子进程。子进程可以访问并修改映射的内存区域,从而与父进程进行通信。这种方法常用于进程间共享数据或传递消息。

父进程和子进程其页表项是相同的。只要父子进程中的一个发生了缺页中断,就给分配物理内存,建立其虚拟内存和物理内存之间的映射,由于父子进程的页表项是相同的,且共享内存,那么另一个发生缺页中断时,对应页表项已经建立了到物理地址的映射关系。

2.5 共享文件映射

共享文件映射标志位:

MAP_SHARED

其中fd与文件有关联。

#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/wait.h>
#include <stdio.h>
#include <stdlib.h>#define SIZE 4096
#define FILE_NAME "shared_memory"int main() {// 创建共享文件int fd = open(FILE_NAME, O_CREAT | O_RDWR, S_IRUSR | S_IWUSR);if (fd == -1) {perror("open failed");exit(1);}// 设置共享文件大小if (ftruncate(fd, SIZE) == -1) {perror("ftruncate failed");exit(1);}// 创建共享文件映射void* addr = mmap(NULL, SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);if (addr == MAP_FAILED) {perror("mmap failed");exit(1);}// 创建子进程pid_t pid = fork();if (pid == -1) {perror("fork failed");exit(1);} else if (pid == 0) {// 子进程写入数据到共享内存char* data = (char*)addr;sprintf(data, "Hello from the child process!");// 将修改刷新到文件if (msync(addr, SIZE, MS_SYNC) == -1) {perror("msync failed");exit(1);}// 子进程结束exit(0);} else {// 等待子进程结束wait(NULL);// 父进程读取共享内存中的数据char* data = (char*)addr;printf("Content of shared memory: %s\n", data);// 解除映射if (munmap(addr, SIZE) == -1) {perror("munmap failed");exit(1);}// 关闭文件if (close(fd) == -1) {perror("close failed");exit(1);}// 删除共享文件if (unlink(FILE_NAME) == -1) {perror("unlink failed");exit(1);}}return 0;
}
# ./a.out
Content of shared memory: Hello from the child process!

共享文件映射在多进程之间共享内存、实现进程间通信,并且避免写时复制的场景中非常常见。

在这种情况下,多个进程可以通过将同一个文件映射到它们的地址空间来实现共享内存。这意味着它们可以直接读取和写入映射的内存区域,而无需进行复制操作。

共享文件映射的优势在于,多个进程可以通过将同一个文件映射到它们的地址空间来共享数据,而无需进行复制。这对于需要频繁读写共享数据的场景非常有用,因为它避免了写时复制带来的性能开销。

需要注意的是,共享文件映射使用文件作为底层存储介质,因此对于共享内存的读写操作会反映到文件中。这也意味着共享文件映射在进程终止后依然存在,并且可以被其他进程访问。因此,需要小心处理共享文件映射的生命周期和访问权限,以确保数据的一致性和安全性。

参考

https://mp.weixin.qq.com/s/AUsgFOaePwVsPozC3F6Wjw

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

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

相关文章

Linux服务器安装jdk

背景: 安装JDK是我们java程序在服务器运行的必要条件,下面描述几个简单的命令就可再服务器上成功安装jdk 命令总览: yum update -y yum list | grep jdk yum -y install java-1.8.0-openjdk java -version 1.查看可安装版本 yum list | grep jdk 2.如果查不到可先进行 yum upd…

could not publish server configuration for tomcat at localhost

1&#xff0c;报错信息如图&#xff1a; 2&#xff0c;找到servers双击&#xff0c;选择Modules&#xff0c;如果有两个webModules ,remove一个&#xff0c; 3&#xff0c;如果重启还是报错&#xff0c;干脆两个都remove&#xff0c;双击tomcat服务add And Remove重新添加

游戏引擎渲染流程

一、渲染概述 我们首先看到渲染技术的发展 游戏渲染面临的挑战&#xff1a; 一个容器中同一时刻有大量的游戏对象需要进行渲染&#xff0c;并且不同对象渲染的形式、算法还有所差异&#xff0c;这些使得游戏的绘制系统变得非常复杂&#xff1b;其次&#xff0c;游戏引擎的渲染…

iOS增量报告生成方案

一&#xff0c;iOS覆盖率报告生成逻辑 iOS覆盖率报告生成与Android有很大的不同&#xff0c;主要的生成逻辑如下&#xff1a; 1&#xff0c;将profraw文件&#xff0c;通过命令xcrun llvm-profdata merge -sparse转换成profdata; 2&#xff0c;再将profdata文件&#xff0c;通…

Django会话

一、Cookie介绍 1.1、背景介绍 HTTP协议有一个特性就是无状态的,是指协议对于交互性场景没有记忆能力 随着动态交互的web应用的出现,HTTP的无状态特性严重阻碍了动态交互应用程序的发展,例如一些购物网站在进行购物时候都会进行了页面跳转/刷新,按照HTTP的无状态协议岂不…

云计算 3月8号 (wordpress的搭建)

项目wordpress 实验目的&#xff1a; 熟悉yum和编译安装操作 锻炼关联性思维&#xff0c;便于以后做项目 nginx 编译安装 1、安装源码包 [rootlinux-server ~]# yum -y install gcc make zlib-devel pcre pcre-devel openssl-devel [rootlinux-server ~]# wget http://nginx.…

两天学会微服务网关Gateway-Gateway路由规则

锋哥原创的微服务网关Gateway视频教程&#xff1a; Gateway微服务网关视频教程&#xff08;无废话版&#xff09;_哔哩哔哩_bilibiliGateway微服务网关视频教程&#xff08;无废话版&#xff09;共计17条视频&#xff0c;包括&#xff1a;1_Gateway简介、2_Gateway工作原理、3…

flink重温笔记(十):Flink 高级 API 开发——flink 四大基石之 State(涉及Checkpoint)

Flink学习笔记 前言&#xff1a;今天是学习 flink 的第 10 天啦&#xff01;学习了 flink 四大基石之 State &#xff08;状态&#xff09;&#xff0c;主要是解决大数据领域增量计算的效果&#xff0c;能够保存已经计算过的结果数据状态&#xff01;重点学习了 state 的类型划…

CleanMyMac X4.15.0专为macOS设计的清理和优化工具

CleanMyMac X 是一款专为 macOS 设计的清理和优化工具。其基本功能和特点主要包括&#xff1a; 系统清理&#xff1a;CleanMyMac X 可以扫描并清除 macOS 系统中的垃圾文件&#xff0c;如缓存、日志、无用的语言文件等&#xff0c;从而释放硬盘空间并提高系统性能。应用程序管…

一文帮助快速入门Django

文章目录 创建django项目应用app配置pycharm虚拟环境打包依赖 路由传统路由include路由分发namenamespace 视图中间件orm关系对象映射操作表数据库配置model常见字段及参数orm基本操作 cookie和sessiondemo类视图 创建django项目 指定版本安装django&#xff1a;pip install dj…

Android应用界面

概述&#xff1a;由于学校原因&#xff0c;估计会考&#xff0c;曹某人就浅学一下。 目录 View概念 创建和使用布局文件 相对布局 线性布局 水平线性布局 垂直线性布局 表格布局 帧布局 扁平化布局 Android控件详解 AdapterView及其子类 View概念 安卓中的View是所…

Linux系统Docker部署DbGate并结合内网穿透实现公网管理本地数据库

&#x1f308;个人主页: Aileen_0v0 &#x1f525;热门专栏: 华为鸿蒙系统学习|计算机网络|数据结构与算法|MySQL| ​&#x1f4ab;个人格言:“没有罗马,那就自己创造罗马~” #mermaid-svg-66GkyG9g7oNq7tl8 {font-family:"trebuchet ms",verdana,arial,sans-serif;f…

音视频学习笔记——c++多线程(一)

✊✊✊&#x1f308;大家好&#xff01;本篇文章主要整理了部分多线程相关的内容重点&#x1f607;。首先讲解了多进程和多线程并发的区别以及各自优缺点&#xff0c;之后讲解了Thead线程库的基本使用。 本专栏知识点是通过<零声教育>的音视频流媒体高级开发课程进行系统…

图论入门题题解

✨欢迎来到脑子不好的小菜鸟的文章✨ &#x1f388;创作不易&#xff0c;麻烦点点赞哦&#x1f388; 所属专栏&#xff1a;刷题_脑子不好的小菜鸟的博客-CSDN博客 我的主页&#xff1a;脑子不好的小菜鸟 文章特点&#xff1a;关键点和步骤讲解放在 代码相应位置 拓扑排序 / 家谱…

【开源】JAVA+Vue.js实现高校宿舍调配管理系统

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能需求2.1 学生端2.2 宿管2.3 老师端 三、系统展示四、核心代码4.1 查询单条个人习惯4.2 查询我的室友4.3 查询宿舍4.4 查询指定性别全部宿舍4.5 初次分配宿舍 五、免责说明 一、摘要 1.1 项目介绍 基于JAVAVueSpringBootMySQL的…

win11中微软商店如何使用微信支付?microsoft store支付教程

Microsoft Store是由微软公司提供的一个数字分发平台&#xff0c;用于购买和下载Windows操作系统及其相关应用、游戏、音乐、电影、电视节目和其他数字内容。该平台最初是作为Windows 8的一部分引入的&#xff0c;后来也适用于Windows 10和其他Microsoft平台。 以下是Microsof…

讲解linux下的Qt如何编译oracle的驱动库libqsqloci.so

1.需求 最近linux下的Qt项目中要连接oracle数据库&#xff0c;用户需要我们访问他们的oracle数据库&#xff0c;查询数据 2.遇到的问题 qt连接oracle数据库需要oracle的驱动库libqsqloci.so插件&#xff0c;需要编译下&#xff0c;之前没有编译过&#xff0c;看了网上的…

消息队列-kafka-消息发送流程(源码跟踪) 与消息可靠性

官方网址 源码&#xff1a;https://kafka.apache.org/downloads 快速开始&#xff1a;https://kafka.apache.org/documentation/#gettingStarted springcloud整合 发送消息流程 主线程&#xff1a;主线程只负责组织消息&#xff0c;如果是同步发送会阻塞&#xff0c;如果是异…

HTML—常用标签

常用标签&#xff1a; 标题标签&#xff1a;<h1></h1>......<h6></h6>段落标签&#xff1a;<p></p>换行标签&#xff1a;<br/>列表&#xff1a;无序列表<ul><li></li></ul> 有序列表<ol>&…

人工智能|机器学习——k-近邻算法(KNN分类算法)

1.简介 k-最近邻算法&#xff0c;也称为 kNN 或 k-NN&#xff0c;是一种非参数、有监督的学习分类器&#xff0c;它使用邻近度对单个数据点的分组进行分类或预测。虽然它可以用于回归问题&#xff0c;但它通常用作分类算法&#xff0c;假设可以在彼此附近找到相似点。 对于分类…