mmap函数小实验

mmap函数小实验

  • 文章目的
  • 参数 length 不是页大小的整数倍会怎样?
    • 研究过程
    • length结论
  • 参数 offset 取不同的值时会怎样?
    • 研究过程
    • offset 结论
  • 参考链接

文章目的

本文是为了深入理解mmap的参数length与offset对mmap函数行为的影响,从而更好地理解内存映射。
man手册中看到的mmap的参数如下:
在这里插入图片描述
函数输入参数介绍:

  • 参数1:addr:指定映射的起始地址,通常设为NULL,由内核来分配
  • 参数2:length:代表将文件中映射到内存的部分的长度,单位为字节数,一般要求为页大小整数倍
  • 参数3:prot:映射区域的保护方式。可以为以下几种方式的组合:
    PROT_EXEC 映射区域可被执行
    PROT_READ 映射区域可被读取
    PROT_WRITE 映射区域可被写入
    PROT_NONE 映射区域不能存取
  • 参数4:flags:影响映射区域的各种特性。在调用mmap()时必须要指定MAP_SHARED 或MAP_PRIVATE。
    MAP_FIXED 如果参数start所指的地址无法成功建立映射时,则放弃映射,不对地址做修正。通常不鼓励用此。
    MAP_SHARED对映射区域的写入数据会复制回文件内,而且允许其他映射该文件的进程共享。
    MAP_PRIVATE 对映射区域的写入操作会产生一个映射文件的复制,即私人的“写入时复制”(copy on write)对此区域作的任何修改都不会写回原来的文件内容。
    等等,在man手册中查看其详细介绍。
  • 参数5:fd:要映射到内存中的文件描述符,即调用mmap前调用open函数的返回值。(如果使用匿名内存映射时,即flags中设置了MAP_ANONYMOUS,fd设为-1。)
  • 参数6:offset:文件映射的偏移量,通常设置为0,代表从文件最前方开始对应,offset必须是分页大小的整数倍

必要知识点:

  • mmap并不分配空间, 只是将文件映射到调用进程的地址空间里(但是会占用掉你的 virutal memory)
  • 普通文件被映射到进程地址空间后,进程可以像访问普通内存一样对文件进行访问,不必再调用read(),write()等操作,然后你就可以用memcpy等操作写文件, 而不用write()了。
  • 内存中的内容并不会立即更新到文件中,而是有一段时间的延迟,你可以调用msync()来显式同步一下。
  • 取消内存映射,需调用munmap()
  • mmap映射完之后,相当于把文件中的内容在用户空间做了映射,内核空间对内容的修改也会反映到用户空间,用户空间对内核的修改也会反映到内核空间。(待确认)

参数 length 不是页大小的整数倍会怎样?

参数length不是页大小整数倍有两种情况,一是小于一个页的大小,二是大于页大小,第二种情况中,超出部分也不足一个页,其实都属于第一种情况。

于是可以用length小于一个页大小时,来实验分析mmap的行为:

  1. mmap实际占用的虚拟内存区域的大小是length的大小还是其他?
  2. 内存映射对象为普通文件时,修改该虚拟内存中大于length的部分,是否可共享?
  3. 内存映射对象为普通文件时,修改该虚拟内存中大于length的部分,是否被同步到普通文件中?

研究过程

使用mmap函数创建一个可读写、共享的虚拟内存区域,内存映射对象为Linux中普通文件a.txt,然后写一些数据到该虚拟内存区域中大于length的部分,最后,调用msync函数显式地同步这些数据到 a.txt 文件中。
mmap_test.c源码:

#include <stdlib.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <stdint.h>
#include <sys/mman.h>/* Usage: ./mmap_test <file> <length> <offset>*/
int main(int argc, char *argv[])
{int fd;uint8_t *pbase = NULL;if (argc < 4) {printf("Usage: ./mmap_test <file> <length> <offset> \n");return 0;}fd = open(argv[1], O_RDWR);if (fd == -1)printf("error: open filed!\n");const size_t length = atoi(argv[2]); const size_t offset = atoi(argv[3]); pbase = mmap(NULL, length, PROT_READ | PROT_WRITE, MAP_SHARED, fd, offset);if (MAP_FAILED == pbase) {perror("mmap failed, reason:");return EXIT_FAILURE;}close(fd);printf("page size is: %ld bytes \n", sysconf(_SC_PAGE_SIZE));const size_t end = length + 13;for (int i = length; i < end; i++) {pbase[i] = '6';}printf("\n");msync(pbase, end, MS_SYNC);return 0;
}

编译:

gcc mmap_test.c -o mmap_test

查看 a.txt 文件的初始内容(ASCII 码,共 110 字节):
在这里插入图片描述执行

./mmap_test a.txt 10 0

然后再查看a.txt内容:
在这里插入图片描述
结果发现,超出了mmap指定length的数据也照样被修改了,那如果mmap中length正好指定了4096Byte还会有同样的结果吗?

实验过程是这样的,我先将一个文件内填充超过一个扇区(4096字节)的数据,然后修改上面的代码,分以下几种情况:

  1. mmap的length参数为4096:试图修改超过第4096字节的数据,发现无法修改。
  2. mmap的length参数为4090:试图修改第4090到第4190字节的数据,发现只有第4091到第4096这6个字节的数据被修改了。

length结论

mmap实际创建的虚拟内存大小不小于length的页大小的最小整数倍。

注:期间遇到了个问题:我先打开了个空文件,然后想通过mmap往空文件中写东西,发现怎么都写不进去,因为这违反了mmap不分配空间的规定。

参数 offset 取不同的值时会怎样?

为了探索offset取不同的值时,mmap究竟表现如何,继续使用上面的源代码,共测试以下几种情况(先设定文件大小为超过4096字节但并不是4096的整数倍,length大小为文件对应最小整数被页的大小):

  • offset取负值,mmap的行为
  • offset取值为5时(即不是页面大小的整数倍),mmap的行为
  • offset取值为4096(即是页面大小的整数倍),mmap的行为
  • offset取值为内存映射文件的大小时,mmap的行为
  • offset取值为大于内存映射文件的大小但正好时页面大小的整数倍时,mmap的行为

研究过程

  1. offset取负值:报"mmap failed, reason:: Invalid argument"
  2. offset取值为5:报"mmap failed, reason:: Invalid argument"
  3. offset取值为4096:此时mmap得到的虚拟地址对应文件从起始位置偏移4096字节后的位置。
  4. offset取值为内存映射文件的大小时(但不是页面大小整数倍):报"mmap failed, reason:: Invalid argument"
  5. offset取值是页面大小的整数倍(但大于内存映射文件的大小):报Bus error

offset 结论

offset的取值必须是页面大小(4096字节)的整数倍,隐含该值必须大于等于零,一般取0,否则会报无效参数报错,且offset不能超过文件大小,否则有bus error。
如果你非要设置offset,按下方法设置:

//需要包含头文件 #include <unistd.h>
const auto true_offset = offset & ~(sysconf(_SC_PAGE_SIZE) - 1);

参考链接

计算机系统篇之虚拟内存(4):再探 mmap

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

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

相关文章

反转链表【java】

给定一个链表的头节点head反转链表 方法一&#xff1a;循环 1.定义三个指针&#xff1a; pre指针&#xff1a;刚开始指向空 prenull cur指针&#xff1a;刚开始指向head节点 curhead temp指针&#xff1a;保存cur指针指向节点的下一个节点 2. 不断循环改变相邻两个节点的指…

Spring MVC 中的统一异常处理

文章目录 Spring MVC 中的统一异常处理Spring MVC 中跳转自定义 404 页面的两种常见方式在 web.xml 中指定自定义的 404 页面提供一个匹配 * 的 Controller 请求处理方法 使用 ExceptionHandler 注解使用 ControllerAdvice ExceptionHandler 注解使用示例控制生效的 Controlle…

JavaWeb前端/后端开发规范——接口文档概述及YApi平台的使用

前言&#xff1a; 整理下笔记&#xff0c;打好基础&#xff0c;daydayup!!! 接口文档 什么是接口文档&#xff1f; 目前主流的开发模式为前后端分离式开发&#xff0c;为了方便前后端的对接&#xff0c;就需要使用接口文件进行统一规范。 接口文档记载什么信息&#xff1f; 1&…

第19天:信息打点-小程序应用解包反编译动态调试抓包静态分析源码架构

第十九天 本课意义 1.如何获取到目标小程序信息 2.如何从小程序中提取资产信息 一、Web&备案信息&单位名称中发现小程序 1.国内主流小程序平台 微信 百度 支付宝 抖音头条 2.小程序结构 1.主体结构 小程序包含一个描述整体程序的app和多个描述各自页面的page …

goland2024安装包(亲测可用)

目录 一、软件简介 二、软件下载 一、软件简介 Goland 是一款由 JetBrains 公司开发的集成开发环境&#xff08;IDE&#xff09;&#xff0c;专门用于 Go 语言的开发。它提供了丰富的功能和工具&#xff0c;帮助开发者更高效地编写、调试和管理 Go 语言项目。 功能特点&#x…

Milvus 老友汇|RAG 场景、电商平台、AI 平台……如何用向量数据库构建业务方案?

近日&#xff0c;Milvus 老友汇Arch Meetup 在深圳圆满落幕。本次 Meetup 由 Milvus 社区携手 Shopee 共同举办&#xff0c;同时还邀请到来自 AWS、点石科技的技术专家&#xff0c;分享电商行业、RAG 场景、AI 平台等如何基于向量数据库构建业务方案。 以下是本次 Meetup 的重点…

OSPF---综合实验

1、R4为ISP&#xff0c;其上只配置IP地址&#xff1b;R4与其他所直连设备间均使用公有IP&#xff1b; 2、R3-R5、R6、R7为MGRE环境&#xff0c;R3为中心站点&#xff1b; 3、整个OSPF环境IP基于172.16.0.0/16划分&#xff1b;除了R12有两个环回&#xff0c;其他路由器均有一个环…

Qt 实战(1)Qt 概述

一、Qt概述 1、什么是Qt&#xff1f; Qt&#xff08;官方发音 [kju:t]&#xff0c;音同 cute&#xff09;是一个跨平台的 C 开发库&#xff0c;主要用来开发图形用户界面&#xff08;Graphical User Interface&#xff0c;GUI&#xff09;程序&#xff0c;也可以开发不带界面的…

HTML 入门

HTML 简介 1. 什么是 HTML&#xff1f; 全称&#xff1a;HyperText Markup Language&#xff08;超文本标记语言&#xff09;。 超文本&#xff1a;暂且简单理解为 “超级的文本”&#xff0c;和普通文本比&#xff0c;内容更丰富。 标 记&#xff1a;文本要变成超文本&…

java面试之线程八锁

所谓的“线程八锁”&#xff0c;其实就是考察 synchronized 锁住的是哪个对象 情况1&#xff1a;12 或 21情况2&#xff1a;1s后12&#xff0c;或 2 1s后 1情况3&#xff1a;3 1s 12 或 23 1s 1 或 32 1s 1情况4&#xff1a;2 1s 后 1情况5&#xff1a;2 1s 后 1情况6&#xff…

PLC扩展更自由,钡铼IOy系列模块实现DI/DO/AI/AO任意组合

随着工业自动化的不断发展&#xff0c;PLC&#xff08;可编程逻辑控制器&#xff09;作为工业控制领域的核心设备&#xff0c;扮演着至关重要的角色。而钡铼IOy系列模块作为PLC的重要扩展设备&#xff0c;不仅实现了DI&#xff08;数字输入&#xff09;、DO&#xff08;数字输出…

nodejs读取并计算指定文件的MD5哈希值

读取并计算指定文件的MD5哈希值 /*** 读取并计算指定文件的MD5哈希值* param {string} file - 文件路径* returns {void} 不返回任何值&#xff0c;但会打印出文件的MD5哈希值*/ const fs require(fs); // 引入文件系统模块 const crypto require(crypto); // 引入加密模块 …

Scala之List

列表 不可变列表(List) 在Scala中&#xff0c;通过List来定义不可变列表&#xff0c;需要注意的是&#xff0c;List本身是一个抽象类&#xff0c;所以并不能直接使用List来构建对象&#xff0c;需要使用它的伴生对象来构建 package com.fesco.listimport scala.::object ListD…

代码随想录训练营第三十五期|第天16|二叉树part03|104.二叉树的最大深度 ● 111.二叉树的最小深度● 222.完全二叉树的节点个数

104. 二叉树的最大深度 - 力扣&#xff08;LeetCode&#xff09; 递归&#xff0c;可以前序遍历&#xff0c;也可以后序遍历 前序遍历是backtracking 下面是后序遍历的代码&#xff1a; /*** Definition for a binary tree node.* public class TreeNode {* int val;* …

TOP100 二分法

写在前面&#xff1a;二分法用在有序序列上 1.35. 搜索插入位置 给定一个排序数组和一个目标值&#xff0c;在数组中找到目标值&#xff0c;并返回其索引。如果目标值不存在于数组中&#xff0c;返回它将会被按顺序插入的位置。 请必须使用时间复杂度为 O(log n) 的算法。 示…

Ubuntu20从0开始选择合适版本手动安装cuda,torch-geometric,jax

一个全新的ubuntu20台式机&#xff0c;在Additional Drivers安装nvidia-470-server&#xff08;一开始安装450&#xff0c;cunda版本只能到11.0&#xff0c;torch有些库用不了&#xff0c;可以直接切换点击Apply Changes重启就行&#xff09; nvidia-smi查看CUDA Version可到…

全球最新国内外18个热门风景视频素材网站推荐

寻找最新的高清风景视频素材&#xff1f;这里有国内外共18个热门网站&#xff0c;精心整理供您选择。 国内资源&#xff1a; 蛙学网&#xff1a;免费提供多种无版权视频素材&#xff0c;资源丰富。新GG网&#xff1a;需QQ登录&#xff0c;提供丰富的视频模板&#xff0c;通过…

【数据库】MVCC

MVCC是一种用来解决读写冲突的无锁并发控制&#xff0c;也就是为事务分配单项增长的时间戳&#xff0c;为每个修改保存一个版本&#xff0c;版本与事务时间戳关联&#xff0c;读操作只读该事务开始前的数据库的快照 MVCC&#xff0c;全称Multi-Version Concurrency Control&am…

vue快速入门(十七)v-model数据双向绑定修饰符

注释很详细&#xff0c;直接上代码 上一篇 新增内容 v-model.trim 自动去除首尾空格v-model.number 自动转换成数字类型 源码 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta name"viewport" con…

kubelet的作用

1、节点管理 定时向api server汇报本节点的信息状态&#xff0c;资源使用状态&#xff0c; 2、pod管理 负责维护节点pod的生命周期&#xff0c;创建、更新、删除容器 3、容器健康检查 kubelet会定期调用容器中的探针来检测容器是否存活、就绪&#xff0c;来进行对应的操作…