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. 不断循环改变相邻两个节点的指…

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;其他路由器均有一个环…

HTML 入门

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

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

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

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;通过…

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

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

基于ADB的Scrcpy实现电脑控制手机

Scrcpy是一个开源的&#xff0c;基于ADB&#xff08;Android 调试桥&#xff09;的手机到电脑上的投屏操控的实现&#xff0c;本文将介绍如何搭建开发环境&#xff0c;使得在Windows系统中去控制投屏的安卓手机。 1. 安装投屏软件 下载Scrcpy软件到电脑上&#xff0c;该软件中…

YoloV9实战:从Labelme到训练、验证、测试、模块解析

模型实战 训练COCO数据集 本次使用2017版本的COCO数据集作为例子&#xff0c;演示如何使用YoloV8训练和预测。 下载数据集 Images: 2017 Train images [118K/18GB] &#xff1a;http://images.cocodataset.org/zips/train2017.zip2017 Val images [5K/1GB]&#xff1a;htt…

【电控笔记2.2】电流回路+延迟效应

延迟效应的来源以及影响 数字控制系统的delay: 5.4节有介绍T0=0.5TS 低通滤波器的时间常数? 滤波器的传递函数与性能参数

【多线程】阻塞队列 | put()方法 | take()方法 | 生产者-消费者模式 |实现阻塞队列

文章目录 阻塞队列1.生产者-消费者模式生产者消费者模型的意义&#xff1a;1.解耦合2.削峰填谷&#xff1a; 2.阻塞队列的使用BlockingQueue 3.实现阻塞队列唤醒&#xff1a;使用阻塞队列实现生产者消费者模型 阻塞队列 阻塞队列是一种特殊的队列&#xff1a; 1.是线程安全的。…

Vue 阶段练习:记事本

将 Vue快速入门 和 Vue 指令的学习成果应用到实际场景中&#xff08;如该练习 记事本&#xff09;&#xff0c;我们能够解决实际问题并提升对 Vue 的技能掌握。 目录 功能展示 需求分析 我的代码 案例代码 知识点总结 功能展示 需求分析 列表渲染删除功能添加功能底部统计…

网络—DAY4

思维导图 多进程并发服务器 #include<myhead.h> #define SER_IP "192.168.122.56" #define SER_PORT 8888 void handler(int signo) {if(signoSIGCHLD){while(waitpid(-1,NULL,WNOHANG)>0);} } int main(int argc, char *argv[]) {//将SIGCHLD信号与处理…

项目中,如何写 readme.md 文件 | 写项目总结

tips&#xff1a;注意写 1. readme文件&#xff1a;①项目文档&#xff08;项目需求和设计文档、项目系统架构和技术文档、接口文档&#xff09;、②项目结构、③启动项目。具体结构见下文。 2. 项目总结&#xff1a;技术栈、描述、主要工作&#xff01;&#xff01;需求及功…

递归、搜索与回溯算法——递归

T04BF &#x1f44b;专栏: 算法|JAVA|MySQL|C语言 &#x1faf5; 小比特 大梦想 此篇文章与大家分享递归,搜索与回溯算法关于递归的专题 如果有不足的或者错误的请您指出! 目录 1.什么时候使用递归2.汉诺塔2.1解析2.2题解 3.合并两个有序链表3.1解析3.2题解 4.翻转链表4.1解析4…