1.mmap����Դ��
2.Linux黑科技|mmap实现详解(一文看懂~)
3.mmapç使ç¨
4.linux内存管理——mmap函数详解
5.Android终极笔记—mmap原理与解析
6.内存映射(memory map)
mmap����Դ��
1、函数函数mmap基础概念
mmap是源码内存映射的简称,它是详解操作系统提供的一种技术,允许程序直接操作文件或设备的函数函数物理内存地址,而不是源码通过传统的内存分配和文件读写操作。mmap具有如下特点:
2、详解文章 cms系统源码mmap内存映射原理
内存映射的函数函数实现过程分为三个阶段:
2.1进程启动映射过程,并在虚拟地址空间中为映射创建虚拟映射区域;
2.2调用内核空间的源码系统调用函数mmap,实现文件物理地址和进程虚拟地址的详解一一映射关系;
2.3进程发起对这片映射空间的访问,引发缺页异常,函数函数实现文件内容到物理内存(主存)的源码拷贝。
3、详解mmap函数实例分析
3.1mmap函数的函数函数原型
参数addr:指定映射的起始地址,通常设为NULL,源码由内核来分配。详解
参数length:代表将文件中映射到内存的部分的长度。
参数prot:映射区域的保护方式,可以为以下几种方式的组合。
参数flags:映射区的特性标志位,常用的两个选项是。
参数fd:要映射到内存中的文件描述符,有open函数打开文件时返回的值。
参数offset:文件映射的偏移量,通常设置为0,代表从文件最前方开始对应,offset必须是分页大小的整数倍。
函数返回值:实际分配的内存的起始地址。
3.2munmap函数
munmap与mmap成对使用,用于解除映射。
3.3实例
通过mmap将测试文件打开并映射到内存,实现文件读写操作,并在终端中查看数据。程序结束时,文件内容会反映写入的结果。
Linux黑科技|mmap实现详解(一文看懂~)
mmap的全称是memory map,中文意思是内存映射。其用途是将文件映射到内存中,然后可以通过对映射区的内存进行读写操作,其效果等同于对文件进行读写操作。
mmap的原理就是将虚拟内存空间映射到文件的页缓存,可知,对文件进行读写时需要经过页缓存进行中转的。所以当虚拟内存地址映射到文件的页缓存后,就可以直接通过读写映射区内存来对文件进行读写操作。
在分析mmap的实现前,最好先了解其使用方式。
1. 文件映射
当我们使用mmap()系统调用对文件进行映射时,将会触发调用do_mmap_pgoff()内核函数来完成工作,经过精简后的ai本地跑源码do_mmap_pgoff()函数主要完成以下两个工作:在位的操作系统中,每个进程都有4GB的虚拟内存空间,应用程序在使用内存前,需要先向操作系统发起申请内存的操作。操作系统会从进程的虚拟内存空间中查找未被使用的内存地址,并且返回给应用程序。操作系统会记录进程正在使用中的虚拟内存地址,如果内存地址没被登记,说明此内存地址是空闲的(未被使用)。
2. 缺页异常
虚拟内存必须映射到物理内存才能使用。如果访问没有映射到物理内存的虚拟内存地址,CPU将会触发缺页异常。也就是说,虚拟内存并不能直接映射到磁盘中的文件。
那么mmap是如何将文件映射到虚拟内存中呢?读写文件时并不是直接对磁盘上的文件进行操作的,而是通过页缓存作为中转的,而页缓存就是物理内存中的内存页。所以,mmap可以通过将文件的页缓存映射到虚拟内存空间来实现对文件的映射。
但我们在mmap系统调用的实现中,并没有看到将文件页缓存映射到虚拟内存空间。那么映射过程是在什么时候发生的呢?答案就是:缺页异常。
由于mmap系统调用并没有直接将文件的页缓存映射到虚拟内存中,所以当访问到没有映射的虚拟内存地址时,将会触发缺页异常。当CPU触发缺页异常时,将会调用do_page_fault()函数来修复触发异常的虚拟内存地址。
最后,我们以一幅图来描述一下虚拟内存是如何与文件进行映射的:从上图可以看出,mmap是通过将虚拟内存地址映射到文件的页缓存来实现的。当对映射后的虚拟内存进行读写操作时,其效果等价于直接对文件的页缓存进行读写操作。对文件的页缓存进行读写操作,也等价于对文件进行读写操作。
mmapç使ç¨
mmap å³ memory mapï¼ä¹å°±æ¯å åæ å°ãmmapæä½æä¾äºä¸ç§æºå¶ï¼è®©ç¨æ·ç¨åºç´æ¥è®¿é®è®¾å¤å åï¼è¿ç§æºå¶ï¼ç¸æ¯è¾å¨ç¨æ·ç©ºé´åå æ ¸ç©ºé´äºç¸æ·è´æ°æ®ï¼æçæ´é«ãå¨è¦æ±é«æ§è½çåºç¨ä¸æ¯è¾å¸¸ç¨ãmmapæ å°å åå¿ é¡»æ¯é¡µé¢å¤§å°çæ´æ°åï¼é¢åæµç设å¤ä¸è½è¿è¡mmapï¼mmapçå®ç°å硬件æå ³ã
æ å°æ¡ä»¶ï¼
mmap()å¿ é¡»ä»¥PAGE_SIZE为åä½è¿è¡æ å°ï¼èå åä¹åªè½ä»¥é¡µä¸ºåä½è¿è¡æ å°ï¼è¥è¦æ å°éPAGE_SIZEæ´æ°åçå°åèå´ï¼è¦å è¿è¡å å对é½ï¼å¼ºè¡ä»¥PAGE_SIZEçåæ°å¤§å°è¿è¡æ å°ã
头æ件ï¼
<sys/mman.h>
å½æ°ååï¼
void* mmap(void* start,size_t length,int prot,int flags,int fd,off_t offset);
int munmap(void* start,size_t length);
åæ°è¯´æï¼
startï¼æ å°åºçå¼å§å°åï¼è®¾ç½®ä¸º0æ¶è¡¨ç¤ºç±ç³»ç»å³å®æ å°åºçèµ·å§å°åã
lengthï¼æ å°åºçé¿åº¦ã//é¿åº¦åä½æ¯ 以åè为åä½ï¼ä¸è¶³ä¸å å页æä¸å å页å¤ç
protï¼ææçå åä¿æ¤æ å¿ï¼ä¸è½ä¸æ件çæå¼æ¨¡å¼å²çªãæ¯ä»¥ä¸çæ个å¼ï¼å¯ä»¥éè¿orè¿ç®åçå°ç»åå¨ä¸èµ·
PROT_EXEC //页å 容å¯ä»¥è¢«æ§è¡
PROT_READ //页å 容å¯ä»¥è¢«è¯»å
PROT_WRITE //页å¯ä»¥è¢«åå ¥
PROT_NONE //页ä¸å¯è®¿é®
flagsï¼æå®æ å°å¯¹è±¡çç±»åï¼æ å°é项åæ å°é¡µæ¯å¦å¯ä»¥å ±äº«ãå®çå¼å¯ä»¥æ¯ä¸ä¸ªæè å¤ä¸ªä»¥ä¸ä½çç»åä½
MAP_FIXED //使ç¨æå®çæ å°èµ·å§å°åï¼å¦æç±startålenåæ°æå®çå ååºéå äºç°åçæ å°ç©ºé´ï¼éå é¨åå°ä¼è¢«ä¸¢å¼ãå¦ææå®çèµ·å§å°åä¸å¯ç¨ï¼æä½å°ä¼å¤±è´¥ã并ä¸èµ·å§å°åå¿ é¡»è½å¨é¡µçè¾¹çä¸ã
MAP_SHARED //ä¸å ¶å®æææ å°è¿ä¸ªå¯¹è±¡çè¿ç¨å ±äº«æ å°ç©ºé´ãå¯¹å ±äº«åºçåå ¥ï¼ç¸å½äºè¾åºå°æ件ãç´å°msync()æè munmap()被è°ç¨ï¼æ件å®é ä¸ä¸ä¼è¢«æ´æ°ã
MAP_PRIVATE //建ç«ä¸ä¸ªåå ¥æ¶æ·è´çç§ææ å°ãå ååºåçåå ¥ä¸ä¼å½±åå°åæ件ãè¿ä¸ªæ å¿å以ä¸æ å¿æ¯äºæ¥çï¼åªè½ä½¿ç¨å ¶ä¸ä¸ä¸ªã
MAP_DENYWRITE //è¿ä¸ªæ å¿è¢«å¿½ç¥ã
MAP_EXECUTABLE //åä¸
MAP_NORESERVE //ä¸è¦ä¸ºè¿ä¸ªæ å°ä¿ç交æ¢ç©ºé´ãå½äº¤æ¢ç©ºé´è¢«ä¿çï¼å¯¹æ å°åºä¿®æ¹çå¯è½ä¼å¾å°ä¿è¯ãå½äº¤æ¢ç©ºé´ä¸è¢«ä¿çï¼åæ¶å åä¸è¶³ï¼å¯¹æ å°åºçä¿®æ¹ä¼å¼èµ·æ®µè¿ä¾ä¿¡å·ã
MAP_LOCKED //éå®æ å°åºç页é¢ï¼ä»èé²æ¢é¡µé¢è¢«äº¤æ¢åºå åã
MAP_GROWSDOWN //ç¨äºå æ ï¼åè¯å æ ¸VMç³»ç»ï¼æ å°åºå¯ä»¥åä¸æ©å±ã
MAP_ANONYMOUS //å¿åæ å°ï¼æ å°åºä¸ä¸ä»»ä½æä»¶å ³èã
MAP_ANON //MAP_ANONYMOUSçå«ç§°ï¼ä¸å被使ç¨ã
MAP_FILE //å ¼å®¹æ å¿ï¼è¢«å¿½ç¥ã
MAP_BIT //å°æ å°åºæ¾å¨è¿ç¨å°å空é´çä½2GBï¼MAP_FIXEDæå®æ¶ä¼è¢«å¿½ç¥ãå½åè¿ä¸ªæ å¿åªå¨x-å¹³å°ä¸å¾å°æ¯æã
MAP_POPULATE //为æ件æ å°éè¿é¢è¯»çæ¹å¼åå¤å¥½é¡µè¡¨ãéå对æ å°åºç访é®ä¸ä¼è¢«é¡µè¿ä¾é»å¡ã
MAP_NONBLOCK //ä» åMAP_POPULATEä¸èµ·ä½¿ç¨æ¶æææä¹ãä¸æ§è¡é¢è¯»ï¼åªä¸ºå·²åå¨äºå åä¸ç页é¢å»ºç«é¡µè¡¨å ¥å£ã
fdï¼ææçæ件æè¿°è¯ãä¸è¬æ¯ç±open()å½æ°è¿åï¼å ¶å¼ä¹å¯ä»¥è®¾ç½®ä¸º-1ï¼æ¤æ¶éè¦æå®flagsåæ°ä¸çMAP_ANON,表æè¿è¡çæ¯å¿åæ å°ã
offsetï¼è¢«æ å°å¯¹è±¡å 容çèµ·ç¹ã
è¿åå¼
æåæ§è¡æ¶ï¼mmap()è¿å被æ å°åºçæéï¼munmap()è¿å0ã失败æ¶ï¼mmap()è¿åMAP_FAILED[å ¶å¼ä¸º(void *)-1]ï¼munmapè¿å-1ãerrno被设为以ä¸çæ个å¼
EACCESï¼è®¿é®åºé
EAGAINï¼æ件已被éå®ï¼æè 太å¤çå å已被éå®
EBADFï¼fdä¸æ¯ææçæ件æè¿°è¯
EINVALï¼ä¸ä¸ªæè å¤ä¸ªåæ°æ æ
ENFILEï¼å·²è¾¾å°ç³»ç»å¯¹æå¼æ件çéå¶
ENODEVï¼æå®æ件æå¨çæ件系ç»ä¸æ¯æå åæ å°
ENOMEMï¼å åä¸è¶³ï¼æè è¿ç¨å·²è¶ åºæ大å åæ å°æ°é
EPERMï¼æè½ä¸è¶³ï¼æä½ä¸å 许
ETXTBSYï¼å·²åçæ¹å¼æå¼æ件ï¼åæ¶æå®MAP_DENYWRITEæ å¿
SIGSEGVï¼è¯çååªè¯»åºåå ¥
SIGBUSï¼è¯ç访é®ä¸å±äºè¿ç¨çå ååº
ç¹ç¹ï¼
使ç¨è¯´æï¼
éç¨åºæ¯ï¼
mmap çéç¨åºæ¯å®é ä¸é常åéï¼å¨å¦ä¸åºåä¸å¯ä»¥éæ©ä½¿ç¨ mmap æºå¶ï¼
å ¶ä»æ³¨æ项ï¼
linux内存管理——mmap函数详解
Linux内存管理中的mmap函数是一种重要的系统调用,它在客户-服务程序中通过共享内存显著优化了文件复制操作。原本的四次数据复制被简化为两次,提高了性能。mmap函数的主要用途包括:
1. 将文件映射到内存,常用于频繁读写的场景,通过内存操作代替IO,提升性能。
2. 匿名内存映射,为进程提供共享内存空间,便于进程间的协作。
3. 无关联进程间的Posix共享内存,如SystemV的shmget/shmat功能的实现。
函数的参数详解如下:
- addr:映射的内存起始地址,可设为NULL让系统自动选择。c开发.net源码
- length:映射到内存的文件部分大小。
- prot:映射区域的访问权限,可组合PROT_EXEC、PROT_READ、PROT_WRITE或PROT_NONE。
- flags:控制映射特性,如MAP_SHARED(共享)或MAP_PRIVATE(私有,写时复制)等。
- fd:映射文件描述符,匿名映射时设为-1。
- offset:文件映射的偏移量,通常为0。
示例代码展示了共享映射和父子进程通信的应用,如修改共享内存文件内容,以及利用mmap进行进程间的通信。同时,需要注意的是,Linux的页式管理机制决定了进程对映射内存的访问限制,避免内存访问溢出。
mmap函数在内存管理和进程间通信中发挥着关键作用,通过合理使用,可以提高程序性能并增强进程间的协作。更多详细信息请参考《UNIX网络编程卷2》。
Android终极笔记—mmap原理与解析
首先,“映射”这个词,就和数学课上说的“一一映射”是一个意思,就是建立一种一一对应关系,在这里主要是只硬盘上文件的位置与进程逻辑地址空间中一块大小相同的区域之间的一一对应,如图1中过程1所示。这种对应关系纯属是逻辑上的概念,物理上是不存在的,原因是进程的逻辑地址空间本身就是不存在的。在内存映射的过程中,并没有实际的数据拷贝,文件没有被载入内存,只是逻辑上被放入了内存,具体到代码,就是建立并初始化了相关的数据结构(struct address_space),这个过程有系统调用mmap()实现,所以建立内存映射的效率很高。
既然建立内存映射没有进行实际的数据拷贝,那么进程又怎么能最终直接通过内存操作访问到硬盘上的文件呢?那就要看内存映射之后的几个相关的过程了。
mmap()会返回一个指针ptr,它指向进程逻辑地址空间中的一个地址,这样以后,进程无需再调用read或write对文件进行读写,而只需要通过ptr就能够操作文件。但是php源码好多木马ptr所指向的是一个逻辑地址,要操作其中的数据,必须通过MMU将逻辑地址转换成物理地址,如图1中过程2所示。这个过程与内存映射无关。
前面讲过,建立内存映射并没有实际拷贝数据,这时,MMU在地址映射表中是无法找到与ptr相对应的物理地址的,也就是MMU失败,将产生一个缺页中断,缺页中断的中断响应函数会在swap中寻找相对应的页面,如果找不到(也就是该文件从来没有被读入内存的情况),则会通过mmap()建立的映射关系,从硬盘上将文件读取到物理内存中,如图1中过程3所示。这个过程与内存映射无关。
如果在拷贝数据时,发现物理内存不够用,则会通过虚拟内存机制(swap)将暂时不用的物理页面交换到硬盘上,如图1中过程4所示。这个过程也与内存映射无关。
mmap的强大之处在于,它可以根据参数配置,用于创建共享内存,从而提高文件映射区域的IO效率,实现IO零拷贝,后面讲下零拷贝的技术,对比下,决定这些功能的主要就是三个参数,下面一一解释
prot
四种情况如下:
flags
比较有代表性的如下:
fd
当参数fd不等于0时,内存映射将与文件进行关联,如果等于0,就会变成匿名映射,此时flags必为MAP_ANONYMOUS
一个mmap竟有如此丰富的功能,从申请分配内存到加载动态库,再到进程间通信,真的是无所不能,强大到让人五体投地。下面就着四种情况,拿一个我最关心的父子进程通信来举例看下,实现一个简单的父子进程通信逻辑,毕竟我们学习的目的就是为了应用,光有理论怎么能称之为合格的博客呢?
父子进程共享内存
运行后打印如下
用mmap创建了一块匿名共享内存区域,fd传入-1和MAP_ANONYMOUS配置实现匿名映射,使用MAP_SHARED创建共享区域,使用fork函数创建子进程,这样来实现子进程通信,通过sprintf将格式化后的单日反转公式源码数据写入到共享内存中。
通过简单的几行代码就实现了跨进程通信,如此简单,这么强大的东西,背后有什么支撑么?带着问题我们接着一探究竟。
MMAP背后的保护神
说到MMAP的保护神,首页了解下内存页:在页式虚拟存储器中,会在虚拟存储空间和物理主存空间都分割为一个个固定大小的页,为线程分配内存也是以页为单位。比如:页的大小为4K,那么4GB存储空间就需要4GB/4KB=1M条记录,即有多万个4KB的页,内存页中,当用户发生文件读写时,内核会申请一个内存页与文件进行读写操作,如图:
这时如果内存页中没有数据,就会发生一种中断机制,它就叫缺页中断,此中断就是MMAP的保护神,为什么这么说呢?我们知道mmap函数调用后,在分配时只是建立了进程虚拟地址空间,并没有分配虚拟内存对应的物理内存,当访问这些没有建立映射关系的虚拟内存时,CPU加载指令发现代码段是缺失的,就触发了缺页中断,中断后,内核通过检查虚拟地址的所在区域,发现存在内存映射,就可以通过虚拟内存地址计算文件偏移,定位到内存所缺的页对应的文件的页,由内核启动磁盘IO,将对应的页从磁盘加载到内存中。最终保护mmap能顺利进行,无私奉献。了解完缺页中断,我们再来细聊下mmap四种场景下的内存分配原理
四种场景分配原理
上面是一个简单的原理总结,并没有详细的展开,感兴趣可以自己查查资料哈。
以上就是Android开发中技术,非常重要的mmap原理解析,更多Android高级进阶技术;可以参考《Android核心技术手册》里面内容包含几个模块。
本次分享,主要介绍了mmap的四种应用场景,通过一个实例验证了父子进程间的通信,并深入mmap找到它的保护神,且深入了解到mmap在四种场景下,操作系统是如何组织分配,通过对这些的了解,在你之后的mmap实战应用有了更好的理论基础,可以根据不同的需求,不同的性能要求等,选择最合适的实现。
内存映射(memory map)
内存映射(Memory Mapping),在Java中,主要通过FileChannel的map方法实现。它允许程序直接操作内存,无需通过磁盘文件进行数据读写,极大提高了性能。
内存映射文件机制,通过将磁盘文件与内存缓冲区建立映射关系,从而使得程序可以直接对内存进行读写操作,仿佛在操作文件一样。这样可以省去磁盘I/O操作,提高程序执行效率。
实现内存映射,通常使用Linux下的mmap函数。该函数需要指定映射区的开始地址、长度、保护模式、映射类型、文件描述符和偏移量等参数。通过这些参数,系统将文件内容映射到内存中,创建一个文件与内存之间的连接。
以mmap函数为例,其参数解释如下:start(映射区的起始地址)、length(映射区的长度)、prot(内存保护标志,如只读或可读写)、flags(映射对象类型,如共享或私有)、fd(文件描述符,用于指定要映射的文件)、offset(映射文件内容的起点)。
使用mmap函数实现内存映射,其过程如图所示。函数会在内存中找到一段空闲空间,并将其与指定的文件内容建立映射关系。之后,程序对内存的所有操作都会直接影响文件内容。
下面通过一个简单的例子来说明如何使用mmap实现内存映射。首先,在Linux环境下创建一个名为mmap.c的C文件,编写如下代码:
接着,使用gcc编译该C文件。然后,在mmap.c所在的目录下创建一个文本文件hello.txt,编写如下内容:
运行程序后,会生成一个名为world.txt的文件。
mmap的优点在于通过内存操作文件,省去了磁盘文件拷贝到用户缓冲区的步骤,因此非常高效。这种技术在多进程共享数据、高速缓存管理等场景中非常有用。
至此,关于内存映射的基本功能介绍完毕。接下来,我们将探讨Java中的FileChannel如何实现内存映射,为Java开发人员提供更深入的理解。上一节课我们学习了FileChannel的基本操作,下一节课将详细讲解FileChannel的map方法。更多内容,敬请期待课程目录。
Linux系统调用-- mmap/munmap函数 (详细讲解~)
Linux系统提供了mmap和munmap两个关键函数,用于在内存中映射文件或其他对象。mmap函数将文件内容映射到进程地址空间,允许进程像操作内存一样访问文件,而munmap则负责删除这些映射。使用时,需指定映射的起始地址(start),长度(length),期望的内存保护标志(prot),以及映射选项(flags)。其中,prot可以包括PROT_READ, PROT_WRITE, PROT_EXEC等,flags可选MAP_SHARED或MAP_PRIVATE,用于决定是否与其他进程共享映射空间。
映射文件时,映射区的st_atime可能在操作过程中更新,除非已更新,否则首次访问映射区时会更新。对共享映射区写入后,st_ctime和st_mtime会在msync()调用特定标志前更新。mmap支持匿名映射(MAP_ANONYMOUS)和基于文件的映射,后者可以用来实现进程间共享内存。
munmap用于移除指定地址区域的映射,返回0表示成功,失败时返回-1。mmap和munmap操作失败时,errno可能设置为EACCES、EAGAIN、EBADF等错误代码。在实际使用中,msync函数可以同步映射区的内容到磁盘,确保数据一致性。
总的来说,mmap和munmap是Linux系统中实现内存映射和共享内存的关键工具,它们允许进程高效地处理文件数据,同时保持数据的一致性。
嵌入式中经常使用mmap这个函数,可以介绍一下作用吗
在嵌入式系统编程中,mmap函数扮演着关键角色。它提供了三种核心功能:一是将文件映射到内存,适用于频繁读写的场景,以提高性能;二是创建匿名内存映射,为关联或非关联进程提供共享内存空间;三是实现无文件关联的进程间共享内存,通常也是通过文件映射到内存。mmap函数的原型如下:`void *mmap(void *start, size_t length, int prot, int flags, int fd, off_t offset);`
参数解释如下:start指定了映射的起始地址,通常设为NULL,由系统自动选择;length表示映射的文件部分大小;prot定义映射区域的访问权限,如可执行、读取、写入或禁止访问;flags控制映射特性,如共享或私有、固定地址或匿名映射等;fd是文件描述符,匿名映射时设为-1;offset是文件映射的偏移量。
调用mmap的常见方式有两种:一是使用普通文件映射,适用于所有进程间的共享,如`fd = open(name, flag, mode); ptr = mmap(NULL, len, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0)`。二是利用特殊文件进行匿名内存映射,适用于具有亲缘关系的进程间通信,通过fork()创建子进程共享父进程的内存空间。
总的来说,mmap函数提供了高效、灵活的内存映射机制,有助于优化性能和进程间通信。务必注意权限设置和错误处理,以确保其正确使用。本文内容摘自CSDN博客,如需引用,请注明出处:/scorpio/archive////.aspx。
mmap的系统调用
1. 创建内存映射
mmap:进程创建匿名的内存映射,把内存的物理页映射到进程的虚拟地址空间。进程把文件映射到进程的虚拟地址空间,可以像访问内存一样访问文件,不需要调用系统调用read()/write()访问文件,从而避免用户模式和内核模式之间的切换,提高读写文件速度。两个进程针对同一个文件创建共享的内存映射,实现共享内存。
mumap:该调用在进程地址空间中解除一个映射关系,addr是调用mmap()时返回的地址,len是映射区的大小。当映射关系解除后,对原来映射地址的访问将导致段错误发生。
3. 设置虚拟内存区域的访问权限
mprotect:把自start开始的、长度为len的内存区的保护属性修改为prot指定的值。 prot可以取以下几个值,并且可以用“|”将几个属性合起来使用: 1)PROT_READ:表示内存段内的内容可写; 2)PROT_WRITE:表示内存段内的内容可读; 3)PROT_EXEC:表示内存段中的内容可执行; 4)PROT_NONE:表示内存段中的内容根本没法访问。 需要指出的是,指定的内存区间必须包含整个内存页(4K)。区间开始的地址start必须是一个内存页的起始地址,并且区间长度len必须是页大小的整数倍。
0. 查找mmap在内核中的系统调用函数 我现在用的内核版是4..,首先在应用层参考上面解析编写一个mmap使用代码,然后编译成程序,在使用strace工具跟踪其函数调用,可以发现mmap也是调用底层的mmap系统调用,然后我们寻找一下底层的带6个参数的mmap系统调用有哪些:
1.mmap的系统调用 x的位于arch/x/kernel/sys_x_.c文件,如下所示:
arm的位于arch/arm/kernel/sys.c文件,如下所示:
然后都是进入ksys_mmap_pgoff:
然后进入vm_mmap_pgoff:
我们讲解最重要的do_mmap_pgoff函数:
然后进入do_mmap:
do_mmap_pgoff这个函数主要做了两件事,get_unmapped_area获取未映射地址,mmap_region映射。 先看下get_unmapped_area ,他是先找到mm_struct的get_unmapped_area成员,再去执行他:
再看mmap_region的实现:
现在,我们看看匿名映射的函数shmem_zero_setup到底做了什么,其实匿名页实际也映射了文件,只是映射到了/dev/zero上,这样有个好处是,不需要对所有页面进行提前置0,只有当访问到某具体页面的时候才会申请一个0页。
其实说白了,mmap就是在进程mm中创建或者扩展一个vma映射到某个文件,而共享、私有、文件、匿名这些mmap所具有的属性是在哪里体现的呢?上面的源码在不断的设置一些标记位,这些标记位就决定了进程在访问这些内存时内核的行为,mmap仅负责创建一个映射而已。
面试都问mmap,你知道mmap是什么吗?
mmap是什么?
mmap是Memory-mapped Files的缩写,它是一种在内存中创建映射文件的机制。当处理大文件时,传统的文件I/O操作可能变得非常缓慢,mmap可以有效避免这种问题。通过使用mmap,操作系统会在内存中创建一个虚拟地址,将文件映射到这个虚拟地址上。这样,我们就可以像访问内存一样访问文件内容,无需频繁的文件I/O操作。
使用mmap的主要优点包括:
mmap的优势在于它可以提高文件读写的效率,减少内存使用,支持多进程访问和文件共享,以及支持随机访问等。对于大文件的处理,mmap尤其有用。
在Java中,mmap通过Java NIO(New I/O)的byteBuffer实现。Java通过JNI(Java Native Interface)调用操作系统提供的mmap函数,将文件映射到虚拟地址空间中。Java中主要使用了FileChannel类,提供了一种将文件映射到内存的方法,即MappedByteBuffer。MappedByteBuffer是ByteBuffer的子类,可以将文件映射到内存中。
一个简单的mmap示例如下:
上述代码中,我们通过RandomAccessFile打开文件,并将其映射到内存中。通过getChannel方法获取文件通道,再调用map方法将文件映射到内存中。操作完毕后,通常需要将其刷回磁盘以确保数据完整性。
在使用mmap技术时,需要注意内存管理和数据完整性。由于mmap直接映射文件到内存,需要谨慎操作,以免超出系统限制或内存使用过多。通常,操作前应将文件全部加载到内存,操作完成后再刷回磁盘。
mmap技术还可以用于多个进程之间的数据共享。一个进程修改文件后,其他进程能即时看到这些修改,相较于传统进程间通信方式,这种方法效率更高。
mmap是高效文件操作的利器,尤其在处理大文件时。Java开发人员掌握mmap技术,能显著优化程序性能和IO操作效率。