文件内存映射mmap解决大文件快速读写问题

2023-11-19

转自:http://blog.csdn.net/gulaizi/article/details/6325726

mmap函数主要用途有三个:

1、将一个普通文件映射到内存中,通常在需要对文件进行频繁读写时使用,这样用内存读写取代I/O读写,以获得较高的性能;

2、将特殊文件进行匿名内存映射,可以为关联进程提供共享内存空间;

3、为无关联的进程提供共享内存空间,一般也是将一个普通文件映射到内存中。


Linux提供了内存映射函数mmap, 它把文件内容映射到一段内存上(准确说是虚拟内存上), 通过对这段内存的读取和修改, 实现对文件的读取和修改。

 

一、使用步骤

用open系统调用打开文件, 并返回描述符fd.

用mmap建立内存映射, 并返回映射首地址指针start.

对映射(文件)进行各种操作, 显示(printf), 修改(sprintf).

用munmap(void *start, size_t lenght)关闭内存映射.

用close系统调用关闭文件fd.

 

二、mmap函数用法

头文件:

          #include <unistd.h>

          #include <sys/mman.h>

函数:void *mmap(void *start,size_t length,int prot,int flags,int fd,off_t offsize);

参数start:指向欲映射的内存起始地址,通常设为 NULL,代表让系统自动选定地址,映射成功后返回该地址。

参数length:代表将文件中多大的部分映射到内存。

参数prot:映射区域的保护方式。可以为以下几种方式的组合:

PROT_EXEC 映射区域可被执行

PROT_READ 映射区域可被读取

PROT_WRITE 映射区域可被写入

PROT_NONE 映射区域不能存取

参数flags:影响映射区域的各种特性。在调用mmap()时必须要指定MAP_SHARED 或MAP_PRIVATE。

MAP_FIXED 如果参数start所指的地址无法成功建立映射时,则放弃映射,不对地址做修正。通常不鼓励用此旗标。

MAP_SHARED对映射区域的写入数据会复制回文件内,而且允许其他映射该文件的进程共享。

MAP_PRIVATE 对映射区域的写入操作会产生一个映射文件的复制,即私人的“写入时复制”(copy on write)对此区域作的任何修改都不会写回原来的文件内容。

MAP_ANONYMOUS建立匿名映射。此时会忽略参数fd,不涉及文件,而且映射区域无法和其他进程共享。

MAP_DENYWRITE只允许对映射区域的写入操作,其他对文件直接写入的操作将会被拒绝。

MAP_LOCKED 将映射区域锁定住,这表示该区域不会被置换(swap)。

参数fd:要映射到内存中的文件描述符。如果使用匿名内存映射时,即flags中设置了MAP_ANONYMOUS,fd设为-1。有些系统不支持匿名内存映射,则可以使用fopen打开/dev/zero文件,然后对该文件进行映射,可以同样达到匿名内存映射的效果。

参数offset:文件映射的偏移量,通常设置为0,代表从文件最前方开始对应,offset必须是分页大小的整数倍。

返回值:

若映射成功则返回映射区的内存起始地址否则返回MAP_FAILED(-1),错误原因存于errno 中。

错误代码:

EBADF 参数fd 不是有效的文件描述词

EACCES 存取权限有误。如果是MAP_PRIVATE 情况下文件必须可读,使用MAP_SHARED则要有PROT_WRITE以及该文件要能写入。

EINVAL 参数start、length 或offset有一个不合法。

EAGAIN 文件被锁住,或是有太多内存被锁住。

ENOMEM 内存不足。

系统调用mmap()用于共享内存的两种方式:

(1)使用普通文件提供的内存映射:

适用于任何进程之间。此时,需要打开或创建一个文件,然后再调用mmap()

典型调用代码如下:

fd=open(name, flag, mode); if(fd<0) ...

ptr=mmap(NULL, len , PROT_READ|PROT_WRITE, MAP_SHARED , fd , 0);

通过mmap()实现共享内存的通信方式有许多特点和要注意的地方,可以参看UNIX网络编程第二卷。

(2)使用特殊文件提供匿名内存映射:

适用于具有亲缘关系的进程之间。由于父子进程特殊的亲缘关系,在父进程中先调用mmap(),然后调用 fork()。那么在调用fork()之后,子进程继承父进程匿名映射后的地址空间,同样也继承mmap()返回的地址,这样,父子进程就可以通过映射区域进行通信了。注意,这里不是一般的继承关系。一般来说,子进程单独维护从父进程继承下来的一些变量。而mmap()返回的地址,却由父子进程共同维护。对于具有亲缘关系的进程实现共享内存最好的方式应该是采用匿名内存映射的方式。此时,不必指定具体的文件,只要设置相应的标志即可。 


转自:http://www.cnblogs.com/lknlfy/archive/2012/04/27/2473804.html


Linux内存映射(mmap)

一. 概述

          内存映射,简而言之就是将用户空间的一段内存区域映射到内核空间,映射成功后,用户对这段内存区域的修改可以直接反映到内核空间,相反,内核空间对这段区域的修改也直接反映用户空间。那么对于内核空间<---->用户空间两者之间需要大量数据传输等操作的话效率是非常高的。

        首先,驱动程序先分配好一段内存,接着用户进程通过库函数mmap()来告诉内核要将多大的内存映射到内核空间,内核经过一系列函数调用后调用对应的驱动程序的file_operation中的mmap函数,在该函数中调用remap_pfn_range()来建立映射关系。直白一点就是:驱动程序在mmap()中利用remap_pfn_range()函数将内核空间的一段内存与用户空间的一段内存建立映射关系。

用户空间mmap()函数:

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

start:用户进程中要映射的某段内存区域的起始地址,通常为NULL(由内核来指定)

length:要映射的内存区域的大小

prot:期望的内存保护标志

flags:指定映射对象的类型

fd:文件描述符(由open函数返回)

offset:要映射的用户空间的内存区域在内核空间中已经分配好的的内存区域中的偏移。大小为PAGE_SIZE的整数倍

 

二. 实现

         首先在驱动程序分配一页大小的内存,然后用户进程通过mmap()将用户空间中大小也为一页的内存映射到内核空间这页内存上。映射完成后,驱动程序往这段内存写10个字节数据,用户进程将这些数据显示出来。

驱动程序:

 1 #include <linux/miscdevice.h>
 2 #include <linux/delay.h>
 3 #include <linux/kernel.h>
 4 #include <linux/module.h>
 5 #include <linux/init.h>
 6 #include <linux/mm.h>
 7 #include <linux/fs.h>
 8 #include <linux/types.h>
 9 #include <linux/delay.h>
10 #include <linux/moduleparam.h>
11 #include <linux/slab.h>
12 #include <linux/errno.h>
13 #include <linux/ioctl.h>
14 #include <linux/cdev.h>
15 #include <linux/string.h>
16 #include <linux/list.h>
17 #include <linux/pci.h>
18 #include <linux/gpio.h>
19 
20 
21 #define DEVICE_NAME "mymap"
22 
23 
24 static unsigned char array[10]={0,1,2,3,4,5,6,7,8,9};
25 static unsigned char *buffer;
26 
27 
28 static int my_open(struct inode *inode, struct file *file)
29 {
30     return 0;
31 }
32 
33 
34 static int my_map(struct file *filp, struct vm_area_struct *vma)
35 {    
36     unsigned long page;
37     unsigned char i;
38     unsigned long start = (unsigned long)vma->vm_start;
39     //unsigned long end =  (unsigned long)vma->vm_end;
40     unsigned long size = (unsigned long)(vma->vm_end - vma->vm_start);
41 
42     //得到物理地址
43     page = virt_to_phys(buffer);    
44     //将用户空间的一个vma虚拟内存区映射到以page开始的一段连续物理页面上
45     if(remap_pfn_range(vma,start,page>>PAGE_SHIFT,size,PAGE_SHARED))//第三个参数是页帧号,由物理地址右移PAGE_SHIFT得到
46         return -1;
47 
48     //往该内存写10字节数据
49     for(i=0;i<10;i++)
50         buffer[i] = array[i];
51     
52     return 0;
53 }
54 
55 
56 static struct file_operations dev_fops = {
57     .owner    = THIS_MODULE,
58     .open    = my_open,
59     .mmap   = my_map,
60 };
61 
62 static struct miscdevice misc = {
63     .minor = MISC_DYNAMIC_MINOR,
64     .name = DEVICE_NAME,
65     .fops = &dev_fops,
66 };
67 
68 
69 static int __init dev_init(void)
70 {
71     int ret;    
72 
73     //注册混杂设备
74     ret = misc_register(&misc);
75     //内存分配
76     buffer = (unsigned char *)kmalloc(PAGE_SIZE,GFP_KERNEL);
77     //将该段内存设置为保留
78     SetPageReserved(virt_to_page(buffer));
79 
80     return ret;
81 }
82 
83 
84 static void __exit dev_exit(void)
85 {
86     //注销设备
87     misc_deregister(&misc);
88     //清除保留
89     ClearPageReserved(virt_to_page(buffer));
90     //释放内存
91     kfree(buffer);
92 }
93 
94 
95 module_init(dev_init);
96 module_exit(dev_exit);
97 MODULE_LICENSE("GPL");
98 MODULE_AUTHOR("LKN@SCUT");

应用程序:

 1 #include <unistd.h>
 2 #include <stdio.h>
 3 #include <stdlib.h>
 4 #include <string.h>
 5 #include <fcntl.h>
 6 #include <linux/fb.h>
 7 #include <sys/mman.h>
 8 #include <sys/ioctl.h> 
 9 
10 #define PAGE_SIZE 4096
11 
12 
13 int main(int argc , char *argv[])
14 {
15     int fd;
16     int i;
17     unsigned char *p_map;
18     
19     //打开设备
20     fd = open("/dev/mymap",O_RDWR);
21     if(fd < 0)
22     {
23         printf("open fail\n");
24         exit(1);
25     }
26 
27     //内存映射
28     p_map = (unsigned char *)mmap(0, PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED,fd, 0);
29     if(p_map == MAP_FAILED)
30     {
31         printf("mmap fail\n");
32         goto here;
33     }
34 
35     //打印映射后的内存中的前10个字节内容
36     for(i=0;i<10;i++)
37         printf("%d\n",p_map[i]);
38     
39 
40 here:
41     munmap(p_map, PAGE_SIZE);
42     return 0;
43 }

先加载驱动后执行应用程序,用户空间打印如下:

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

文件内存映射mmap解决大文件快速读写问题 的相关文章

  • React函数式组件渲染、useEffect顺序总结

    参考资料 深入React的生命周期 上 出生阶段 Mount 深入React的生命周期 下 更新 Update 精读 useEffect 完全指南 React组件重新渲染理解 优化大全React渲染顺序及useEffect执行顺序探究 含并
  • mysql创建定时任务

    一 前言 自 MySQL5 1 6起 增加了一个非常有特色的功能 事件调度器 Event Scheduler 可以用做定时执行某些特定任务 例如 删除记录 对数据进行汇总等等 来取代原先只能由操作系统的计划任务来执行的工作 更值得 一提的是
  • pytorch小问题

    pytorch张量的保存和恢复 tensor本身就是对象 如果想要保存和恢复对应的对象 使用 torch save 和 torch load即可 就像picke的load 和dump一样 t2 torch randn 1024 1024 t
  • Java Spring @Scheduled 定时任务crontab表达式设置

    Java Spring Scheduled 定时任务crontab表达式设置 1 Cron详解 2 例子 参考 1 Cron详解 Cron表达式是一个字符串 字符串以5或6个空格隔开 分为6或7个域 每一个域代表一个含义 Cron有如下两种
  • React(一):React的设计哲学 - 简单之美

    React 一 React的设计哲学 简单之美 React 二 React开发神器Webpack React 三 理解JSX和组件 React 四 虚拟DOM Diff算法解析 React 五 使用Flux搭建React应用程序架构 Rea
  • Java程序员开发软件(工具)清单

    亲 你准备好了吗 001 项目管理 禅道 JIRA 002 办公协同 钉钉 003 工作邮件 Foxmail OutLook 004 项目文档 MS Office Word Excel PowerPoint WPS 005 MD 笔记 Ty
  • openGL之API学习(六十九)水平同步 垂直同步

    垂直和水平是CRT中两个基本的同步信号 水平同步信号决定了CRT画出一条横越屏幕线的时间 垂直同步信号决定了CRT从屏幕顶部画到底部 再返回原始位置的时间 而恰恰是垂直同步代表着CRT显示器的刷新率水平 垂直同步打开 那么在游戏中 或许强劲
  • 用 LangChain 构建基于资料库的问答机器人(二):从资料源中提取文本信息

    大家好 我是学生大使 Jambo 这个系列的目标是做出一个根据资料回答问题的机器人 那么从资料源中提取文本信息就是一件必要的事 但我们的资料源格式是多样的 比如 PDF Word HTML PPT 等等 甚至有的资料源来自于网络 这些格式都
  • Android studio 怎样连接手机运行,Android studio连接手机调试

    今天百度了一下如何在在Android studio如何在真机上运行 比较复杂 看了很多 现在给大家总结一下 1 首先要将手机连接到电脑上 直接在电脑上安装一个应用宝 然后在手机上也安装一个应用宝 通过应用宝将手机连接到电脑上 2 打开手机的
  • Nacos下载安装与配置(windows)

    一 Nacos下载 外网不好下载以下提供了两个版本 官网地址 https nacos io zh cn 蓝奏云地址 nacos server 1 4 1 zip 蓝奏云 1 4 1 版本 windows nacos server 2 0 0
  • 像智能手机一样造车,可能吗?

    造车这件事有多火 从小鹏 理想等昔日 造车新势力 在互联网军团的入局浪潮中 都变成了 前浪 就可见一斑 春节前后 我们见证了一波波互联网企业在汽车领域的布局 百度与吉利组建合资公司 苹果传出与韩国现代合作 阿里与上汽的智己汽车注册不到20天
  • 2023年VSCode插件最新推荐(54款)

    本文介绍前端开发领域常用的一些VSCode插件 插件是VSCode最重要的组成部分之一 本文列出了我自己在以往工作经验中积累的54款插件 个人觉得这些插件是有用或有趣的 根据它们的作用 我粗略的把它们分成了代码管理 文本和图片处理 前端框架

随机推荐

  • 端到端学习在车辆测距中的探索与实践

    yolo车距1 订阅车距专栏获得源码 http t csdn cn sU3U6 随着深度学习技术的快速发展 端到端学习在计算机视觉领域取得了显著的成果 端到端学习是一种直接从输入数据到输出结果的模型训练方法 无需进行复杂的特征工程 在车辆测
  • 【Arduino学习】05.驱动4个数码管

    数码管介绍 如图 本次使用的数码管为共阴极 四个数码管有 12 个引脚 可以分为位选脚和段选脚 段选脚 8个引脚a b c d e f g 位选脚 4 个引脚 D1 D2 D3 D4 哪个数码管显示由片选脚决定 片选脚为高电平 则该数码管点
  • 九宫格人车识别

    一 原理 通过霍夫检测圆的个数来识别小人位置 二 过程 1 二值图像 2 去掉宫格内容 便于分割 3 对二值图填补 减少纹理 4 分割九宫格 依次检测每个宫格中圆个数 5 最终显示小人所在宫格图片 在img6 jpg中 详细程序运行结果 三
  • xshell连接提示Linux服务器发送了一个意外的数据包

    服务器发送了一个意外的数据包received 3 expected 20 打开需要连接的Linux主机 编辑vim etc ssh sshd config 在最后一行添加 KexAlgorithms curve25519 sha256 li
  • Java通过自定义类加载器模拟冰蝎免杀功能

    一 Java类加载器 类加载器属于JVM的一个重要知识点 也是Java安全里命令执行 webshell管理器编写的常用技术 类加载器简介 我们知道java源文件在运行前会被编译为class类文件 存放着编译后JVM虚拟机指令的二进制字节流
  • 字符编码悉知

    1 查看windows系统代码页 代码页是字符集编码的别名 也有人称 内码表 早期 代码页是IBM称呼电脑BIOS本身支持的字符集编码的名称 当时通用的操作系统都是命令行界面系统 这些操作系统直接使用BIOS供应的VGA功能来显示字符 操作
  • BrokenPipeError: [Errno 32] Broken pipe

    运行Pytorch tutorial代码报错 BrokenPipeError Errno 32 Broken pipe 源代码地址 Training a classifier CIFAR10 该问题的产生是由于windows下多线程的问题
  • QMap遍历(修改)

    方法一 STL风格的遍历器 个人较常用 直观易读 方便修改值 QMap
  • 解决 MDK keil 注册中出现 TOOLS.INI TOOLCHAIN NOT INSTALLED 办法

    MDK Keil 兼容 C51 Keil 我们的电脑有32位系统和64位系统 当keil出现这个错误时 可以按以下办法解决 有两种解决办法 第一种 同时安装MDK Keil 和 C51 Keil 有可能解决此问题 安装必须路径相同 第二种
  • leetcode-合并两个有序链表(详解)

    合并两个有序链表 前言 一 题链接 题意 题思路 题思路图解 题代码 总结 前言 路漫漫及修远兮 一 题链接 题意 将两个升序链表合并为一个新的 升序 链表并返回 新链表是通过拼接给定的两个链表的所有节点组成的 输入 l1 1 2 4 l2
  • 【车道线】TwinLiteNet 复现过程全纪录

    码字不易 喜欢的请点赞收藏 论文全文翻译 freespace TwinLiteNet An Efficient and Lightweight Model for Driveable Area and Lane Segmentation 莫
  • 虹软人脸识别 - ArcFace SDK介绍及使用注意事项

    很多朋友在开发人脸识别系统的时候 会遇到各种各样的问题 现在我们以安卓平台使用虹软的免费离线人脸识别SDK开发为例 给大家介绍一下如何开发一个带有图片的人脸检测 视频画面的人脸属性检测 人脸注册识别等功能的人脸识别系统 一 获取SDK 1
  • [rk3588]多种wifi模组兼容

    硬件部分 M 2接口 使用的wifi模组是PCIE接口的RTL8852BE和SDIO接口的AP6256 软件部分 M 2接口介绍 M 2接口是一种用于连接各种扩展设备的接口标准 它最初设计用于连接固态硬盘 SSD 但也广泛用于连接其他设备
  • spark hadoop环境及运行

    hadoop配置 在Ubuntu20 04里安装Hadoop详细步骤 图文 亲测成功 ubuntu20 04安装hadoop 菜鸡的学习之路的博客 CSDN博客 启动hadoop root ubuntu usr local hadoop s
  • 服务器处理发生异常:java.text.ParseException: Unparseable date

    测试上传报文的时候遇见报错 服务器处理发生异常 java text ParseException Unparseable date 2023 03 03 错误报文 实际需要的报文 错误原因 上传时间字段 与Date字段数据位数不匹配 Jav
  • python贝叶斯网络预测模型_高效灵活的概率建模方法基于Python

    前言 在今天给大家介绍一个研究工具 pomegranate 它比其他软件包更加灵活 更快 直观易用 并且可以在多线程中并行完成 The API 主要模型介绍一般混合模型 隐马尔可夫模型 贝叶斯网络 贝叶斯分类器 所有模型使用做多的方法 mo
  • 迪杰斯特拉(Dijkstra)算法求最短路径

    文章目录 一 最短路径 二 基本思想 三 步骤图解 步骤 S中存放的是已经求得的最短路径的终点的集合 v s集合包含其他点 i代表第i条最短路径 及可能路径走法 邻接矩阵表示弧 一 最短路径 从某顶点 源点 出发到另一顶点 目的地 的路径中
  • 【数据挖掘】数据挖掘比赛项目-kaggle泰坦尼克号

    数据挖掘实战项目 kaggle泰坦尼克号生还者预测 ing kaggle泰坦尼克号生还者预测 泰坦尼克号 从灾难中学习机器 kaggle网站连接 链接 https www kaggle com c titanic 一 实战项目描述 1 项目
  • uniapp判断h5运行环境(微信、pc、移动端)

    isOpenMode 平台 设备和操作系统 var system win false mac false xll false ipad false 检测平台 var p navigator platform system win p ind
  • 文件内存映射mmap解决大文件快速读写问题

    转自 http blog csdn net gulaizi article details 6325726 mmap函数主要用途有三个 1 将一个普通文件映射到内存中 通常在需要对文件进行频繁读写时使用 这样用内存读写取代I O读写 以获得