OS - MMAP初探
文章目录
- 生猛干货
- What's mmap
- mmap 函数
- mmap() 的底层原理
- 虚拟内存空间与物理内存空间
- vm_area_struct 结构
- 对文件的读写
- 搞定计算机基础内功
生猛干货
计算机专业学生、非科班程序员必备! 90%程序员忽略的大厂招聘考点、技能进阶秘诀尽在这里!
What’s mmap
https://man7.org/linux/man-pages/man2/mmap.2.html
简单来说: mmap() 系统调用能够将文件映射到内存空间,然后可以通过读写内存来读写文件
mmap 函数
#include <sys/mman.h>void *mmap(void *addr, size_t length, int prot, int flags,int fd, off_t offset);
- start:指定要映射的内存地址,一般设置为 NULL 让操作系统自动选择合适的内存地址。
- length:映射地址空间的字节数,它从被映射文件开头 offset 个字节开始算起。
- prot:指定共享内存的访问权限。可取如下几个值的可选:PROT_READ(可读), PROT_WRITE(可写), PROT_EXEC(可执行), PROT_NONE(不可访问)。
- flags:由以下几个常值指定:MAP_SHARED(共享的) MAP_PRIVATE(私有的), - MAP_FIXED(表示必须使用 start 参数作为开始地址,如果失败不进行修正),其中,MAP_SHARED , MAP_PRIVATE必选其一,而 MAP_FIXED 则不推荐使用。
- fd:表示要映射的文件句柄。
- offset:表示映射文件的偏移量,一般设置为 0 表示从文件头部开始映射。
函数的返回值为最后文件映射到进程空间的地址,进程可直接操作起始地址为该值的有效地址。
举个例子
int main() {...fd = open(name, flag, mode);if (fd < 0) {// error process...exit(1);}addr = mmap(NULL, len, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_SHARED, fd, 0);if (addr < 0) {// error process...exit(1);}memset(addr, 0, len);...exit(0);
}
首先通过 open() 系统调用打开一个文件,然后通过调用 mmap() 把文件映射到内存空间,映射成功后就可以通过操作函数返回的内存地址来对文件进行读写操作。
mmap() 的底层原理
虚拟内存空间与物理内存空间
我们先来看下 操作系统的虚拟内存空间与物理内存空间的概念 。
在 32位的 Linux 内核中,每个进程都独有 4GB 的虚拟内存空间,但所有进程却共用相同的物理内存空间。
物理内存空间就是安装在电脑上的内存条,如果内存条只有 1GB,那么物理内存空间就只有 1GB。但虚拟内存空间是逻辑上的内存空间,虚拟内存空间必须映射到物理内存空间才能使用。
虚拟内存空间与物理内存空间映射关系如下:
映射是按内存页进行的,一个内存页为 4KB 大小。
在上图中,P1 是进程1,P2 是进程2。
- 进程1的虚拟内存页A映射到物理内存页A
- 进程2的虚拟内存页A映射到物理内存页B
- 进程1的虚拟内存页B和进程2的虚拟内存页B同时映射到物理内存页C,也就是说进程1和进程2共享了物理内存页C
vm_area_struct 结构
在Linux内核中,虚拟内存是用过结构体 vm_area_struct 来管理的,通过 vm_area_struct 结构体可以把虚拟内存划分为多个用途不相同的内存区,比如可以划分为数据段区、代码段区等等.
vm_area_struct
struct vm_area_struct {struct mm_struct * vm_mm; /* The address space we belong to. */
unsigned long vm_start; /* Our start address within vm_mm. */
unsigned long vm_end; /* The first byte after our end addresswithin vm_mm. *//* linked list of VM areas per task, sorted by address */
struct vm_area_struct *vm_next;pgprot_t vm_page_prot; /* Access permissions of this VMA. */
unsigned long vm_flags; /* Flags, listed below. */rb_node_t vm_rb;struct vm_area_struct *vm_next_share;
struct vm_area_struct **vm_pprev_share;/* Function pointers to deal with this struct. */
struct vm_operations_struct * vm_ops;
...
struct file * vm_file; /* File we map to (can be NULL). */
...
};
vm_area_struct 结构各个字段作用:
- vm_mm:指向进程内存空间管理对象。
- vm_start:内存区的开始地址。
- vm_end:内存区的结束地址。
- vm_next:用于连接进程的所有内存区。
- vm_page_prot:指定内存区的访问权限。
- vm_flags:内存区的一些标志。
- vm_file:指向映射的文件对象。
- vm_ops:内存区的一些操作函数。
vm_area_struct 结构与虚拟内存地址的关系如下图:
每个进程都由 task_struct 结构进行管理,task_struct 结构中的 mm 成员指向了每个进程的内存管理结构 mm_struct,而 mm_struct 结构的 mmap 成员记录了进程虚拟内存空间各个内存区的 vm_area_struct 结构链表。
当调用 mmap() 时,内核会创建一个 vm_area_struct 结构,并且把 vm_start 和 vm_end 指向虚拟内存空间的某个内存区,并且把 vm_file 字段指向映射的文件对象。然后调用文件对象的 mmap 接口来对 vm_area_struct 结构的 vm_ops 成员进行初始化,如 ext2 文件系统的文件对象会调用 generic_file_mmap() 函数进行初始化,代码如下:
static struct vm_operations_struct generic_file_vm_ops = {nopage: filemap_nopage,
};int generic_file_mmap(struct file * file, struct vm_area_struct * vma)
{struct address_space *mapping = file->f_dentry->d_inode->i_mapping;
struct inode *inode = mapping->host;if ((vma->vm_flags & VM_SHARED) && (vma->vm_flags & VM_MAYWRITE)) {if (!mapping->a_ops->writepage)
return -EINVAL;
}
if (!mapping->a_ops->readpage)
return -ENOEXEC;
UPDATE_ATIME(inode);
vma->vm_ops = &generic_file_vm_ops;
return 0;
}
vm_operations_struct 结构的 nopage 接口会在访问内存发生异常时被调用,上面指向的是 filemap_nopage() 函数,filemap_nopage() 函数的主要工作是:
- 把映射到虚拟内存区的文件内容读入到物理内存页中。
- 对访问发生异常的虚拟内存地址与物理内存地址进行映射。
处理过程如下
如上图所示,虚拟内存页m 映射到 物理内存页x,并且把映射的文件的内容读入到物理内存中,这样就把内存与文件的映射关系建立起来,对映射的内存区进行读写操作实际上就是对文件的读写操作。
一般来说,对映射的内存空间进行读写并不会实时写入到文件中,所以要对内存与文件进行同步时需要调用 msync() 函数来实现。
对文件的读写
像 read()/write() 这些系统调用,首先需要进行内核空间,然后把文件内容读入到缓存中,然后再对缓存进行读写操作,最后由内核定时同步到文件中。过程如下图:
而调用 mmap() 系统调用对文件进行映射后,用户对映射后的内存进行读写实际上是对文件缓存的读写,所以减少了一次系统调用,从而加速了对文件读写的效率。如下图:
搞定计算机基础内功
OS - MMAP初探相关推荐
- It's all about buffers: zero-copy, mmap and Java NIO
2019独角兽企业重金招聘Python工程师标准>>> Sep 10, 2016 in OS There are use cases where data need to be re ...
- MongoDB的使用
mongod: mongo 是启动MongoDB shell客户端的命令 mongod 是启动MongoDB数据库服务的命令,主要提供了四种启动方式: 1. 命令行方式直接启动 MongoDB默认的存 ...
- 【转载】shell实例手册
原文地址:shell实例手册 作者:没头脑的土豆 shell实例手册0说明{手册制作: 雪松更新日期: 2013-12-06欢迎系统运维加入Q群: 198173206请使用"notepad ...
- mongoDB配制及学习
mongoDB配制及学习 http://blog.csdn.net/zj7758/article/details/7102996 ----------------------------------- ...
- 如何在Python中便宜地获取大文件的行数?
我需要在python中获取一个大文件(数十万行)的行数. 内存和时间最有效的方法是什么? 目前,我这样做: def file_len(fname):with open(fname) as f:for ...
- shell实例手册 2021-4-10
shell实例手册0说明{手册制作: 雪松更新日期: 2013-12-06欢迎系统运维加入Q群: 198173206请使用"notepad++"打开此文档,"alt+0& ...
- 转——shell实用手册
shell实例手册 0说明{ 手册制作: 雪松 更新日期: 2013-12-06 欢迎系统运维加入Q群: 198173206 请使用"notepad++"打开此文档,"a ...
- 干货!一篇文章集合所有Linux基础命令,适合所有菜鸟学习和老手回顾!
阿里云 干货!一篇文章集合所有Linux基础命令,适合所有菜鸟学习和老手回顾! 1 文件{ ls -rtl # 按时间倒叙列出所有目录和文件 ll -rt touch file # 创建空白文件 ...
- 干货!一篇文章集合所有Linux基础命令
1 文件{ ls -rtl # 按时间倒叙列出所有目录和文件 ll -rt touch file # 创建空白文件 rm -rf 目录名 # 不提示删除非空目录(-r:递归删除 -f强制) dos2u ...
最新文章
- 三家运营商的手机终端补贴政策分析(二)
- wireshark网络分析就这么简单 pdf_才知道,PDF、Excel、Word互转这么简单?涨知识了...
- 客户信贷应收总额相关
- python识别验证码ocr_Python 验证码识别-- tesserocr
- 告别加班:一个让你不再重复工作的技能
- erlang安装_消息中间件(八)- RabbitMQ - 安装
- vue和小程序哪个好学一点_litemall,Spring Boot后端,微信小程序用户前端 + Vue用户移动端...
- 3-8《Ruby元编程》第二章对象模型
- 30多岁的程序员仍然奋斗在一线
- windows10桌面_32位windows10系统正式版推荐下载
- Acrel-6000/B电气火灾监控系统在千山大厦的应用
- 进栈顺序为abcd则出栈顺序为_进栈顺序为ABCDEFG 有可能的出站顺序是什么
- IPA安装的几种方法
- 集成 React Native 到现有Android项目
- 阿里云服务器价格是多少,阿里云服务器价格查询的三种方式
- 数学与计算机的微妙关系
- Android 页面布局xd,页面布局(XD):小尺寸设备上的页面布局《 从设计到代码:布局设计 》...
- 广东开放大学学习指引(A)(本专,2022春)
- 王权富贵:使用pytorch复现Fater-RCNN
- 什么时候用C而不用C++?
热门文章
- postgre 没有主键自增ma_PostgreSQL 主键自增解决方案
- 六行代码安装 GPU版本的TensorFlow
- Leetcode 104.二叉树的最大深度 (每日一题 20210625)
- 认识哈希函数(散列函数)
- 【缺迪杰斯特拉和SPFA] 文巾解题 787. K 站中转内最便宜的航班
- R语言实战应用精讲50篇(三)-多重线性回归系列之模型评估与诊断应用案例
- 又到了年终总结的时候了,数据分析的年度工作计划,你知道该怎么写吗?
- 增强学习(一) ----- 基本概念
- 用终端访问路由器设置端口开发_Serial for Mac(全功能串行终端管理软件)
- 浅谈“知识蒸馏”技术在机器学习领域的应用