深入探究Linux mmap64的实现及应用 (linux mmap64)

Linux作为一款自由开源的操作系统,其性能和稳定性备受赞誉,被广泛应用于服务器、嵌入式系统等领域。其中,mmap()系统调用是Linux有名的一大特色,它允许用户进程通过建立虚拟内存映射来访问文件或设备,实现了一个衔接内存的方法,将磁盘上的文件映射到进程地址空间,提高了文件的访问效率。而mmap64的出现,更是解决了32位平台无法支持大内存访问的问题。本文将。

一、Linux mmap64的概念和原理

1.1 概念

mmap64是由32位机器到64位机器转换时,操作系统为保持兼容,新增的一个系统调用。与mmap()函数相比,主要区别在于支持对大内存的访问。mmap()函数在32位机器上只能映射2GB左右大小的内存,而mmap64则能够在64位机器上支持对更大內存的访问。因此,它在处理大型文件时非常有用。

1.2 原理

mmap64调用原理跟mmap调用基本一样,但其在创建映射时会将offset参数左移32位,以支持64位地址指针。

mmap64的调用格式为:

“`

void *mmap64(void *addr, size_t length, int prot, int flags, int fd, off64_t offset);

“`

其中,

– addr:映射区的开始地址

– length:映射区的长度

– prot:映射区域的保护方式

– flags:映射区域的类型和其他属性

– fd:打开文件的文件描述符 fd。

– offset:被映射内存的起始偏移量,单位为字节。

二、Linux mmap64的应用

2.1 mmap64用于内存映射文件

mmap64支持通过共享内存与IPC通信,但更常见的应用是内存映射文件。mmap64可以将一个文件(或设备)映射到用户空间的一段内存中,所有对这片内存的读写操作都将直接对文件进行,而不需要使用read()/write()等系统调用。

2.2 mmap64用于大文件读写操作

mmap64还可以用于大文件的读写操作。相比一次读取大文件到内存中,使用mmap64将文件映射到内存中,可以避免内存不足的情况发生。同时,尤其适用于通过多进程方式访问同一个文件的情景。由于多进程共享一个文件映射,不需要复制整个文件,而是共享同一个内存区域,避免了复制的时间和性能开销。这种方法在多进程之间较为常见。

2.3 mmap64用于动态内存管理

mmap64还可以用在动态内存管理中。mmap64系统调用也可以用于确定内存的起始地址。mmap64返回的地址使用后可以通过munmap移除,从而使做大过程变得更加灵活。此外,虽然mmap()无法动态地调整映射的长度,但通过使用mremap(),我们可以以原址为中心,调整区域大小。

三、Linux mmap64实际使用中需要注意的事项

3.1 地址保护权限

mmap64将文件映射到内存中,需设置访问该内存的权限,如果内存可写,那么文件也可写;如果是只读的,文件只能读取。

3.2 对文件的访问方式

mmap64常常被用于大型磁盘文件的高效读写。然而,需要注意的是,使用mmap时,对于频繁更改的文件,如日志文件、数据库文件等,如果不适当地使用mmap,将会对磁盘空间产生很大的压力。所以,对于频繁修改文件的应用,应尽量避免使用mmap。

3.3 应防止野指针

使用mmap64函数映射一个文件时,注意内存块是否要求清空,否则可能会引发安全性问题。这时需要注意让系统保证这段内存避免产生野指针。

四、Linux mmap64

可以出,Linux mmap64作为一项重要的内存映射技术,是在Linux系统下开发软件的不可或缺的一项技能。mmap64可以用于内存和磁盘之间进行高效率的交换数据;通过mmap64可以更好的集成磁盘访问和内存访问;在操作大文件时提供了更好的支持等等。Linux mmap64不仅可以用于文件的内存映射,还能够通过设置内存的保护以实现内存管理,对多进程共享内存等使用都非常方便。合理地使用mmap64将显著改善软件的计算性能。在Linux平台下,若能熟练地掌握mmap64的使用方法,并且结合各类应用场景,将会使代码的效率更加卓越。

相关问题拓展阅读:

探讨一下 Linux 共享内存的 N 种方式

关于 Linux 共享内存,写得更好的应该是宋宝华的 《世上更好的共享内存》 一文。

本文可以说是对这篇文章的学习笔记,顺手练习了一下 rust libc —— shichaoyuan/learn_rust/linux-shmipc-demo

按照宋宝华的总结,当前有四种主流的共享内存方式:

前两种方式比较符合传统的用法,共享内侍碧存做为进程间通信的媒介。

第三种方式更像是通过传递内存“句柄”进行数据传输。

第四种方式是为设备间传递数据设计,避免内存拷贝,直接传递内存“句柄”。

这里尝试了一下第二种和第三种方式。

这套 API 应该是最档缺普遍的 —— shm_open + mmap,本质上来说 Aeron 也是用的这种方式(关于 Aeron 可以参考 我之前的文章 )。

看一下 glibc 中 shm_open 函数的实现就一清二楚了:

shm_open 函数就是在 /dev/shm 目录下建文件,该目录挂载为 tmpfs,至于 tmpfs 可以简单理解为存储介质是内存的一种文件系统,更准确的理解可以参考官方文档 tmpfs.txt 。

然后通过 mmap 函数将 tmpfs 文件映射到用户空间就可以随意操作了。

优点:

这种方式更大的优势在于共享的内存是有“实体”(也就是 tmpfs 中的文件)的,所以多个进程可以很容易通过文件名这个信息构建共享内存结构,特别适合把共享内存做为通信媒介的场景(例如 Aeron )。

缺点:

如果非要找一个缺点的话,可能是,文件本身独立于进程的生命周期,在使用完毕后需要注意删除文件(仅仅行谈辩 close 是不行的),否则会一直占用内存资源。

memfd_create 函数的作用是创建一个匿名的文件,返回对应的 fd,这个文件当然不普通,它存活在内存中。更准确的理解可以参考官方文档 memfd_create(2) 。

直观理解,memfd_create 与 shm_open 的作用是一样的,都是创建共享内存实体,只是 memfd_create 创建的实体是匿名的,这就带了一个问题:如何让其它进程获取到匿名的实体?shm_open 方式有具体的文件名,所以可以通过打开文件的方式获取,那么对于匿名的文件怎么处理呢?

答案是:通过 Unix Domain Socket 传递 fd。

rust 的 UDS 实现:

rust 在 std 中已经提供了 UDS 的实现,但是关于传递 fd 的 send_vectored_with_ancillary 函数还属于 nightly-only experimental API 阶段。所以这里使用了一个三方 crate —— sendfd ,坦白说可以自己实现一下,使用 libc 构建好 SCM_RIGHTS 数据,sendmsg 出去即可,不过细节还是挺多,我这里就放弃了。

这套 API 设计更灵活,直接拓展了我的思路,本来还是受限于 Aeron 的用法,如果在这套 API 的加持下,是否可以通过传递数据包内存块(fd)真正实现零拷贝呢?

优点:

灵活。

缺点:

linux mmap64的介绍就聊到这里吧,感谢你花时间阅读本站内容,更多关于linux mmap64,深入探究Linux mmap64的实现及应用,探讨一下 Linux 共享内存的 N 种方式的信息别忘了在本站进行查找喔。


数据运维技术 » 深入探究Linux mmap64的实现及应用 (linux mmap64)