问题1:标准文件 I/O 操作也是这样吗?如果应用程序尝试读取尚未缓存的文件部分,将导致系统调用,从而使内核加载相关页/块。此外,页面需要复制回用户空间缓冲区。
你可以将 read 内容读入缓冲区,并且 I/O 设备会将其复制到该位置。还有异步读取或 AIO,其中数据将由内核在后台传输,因为设备提供了它。您可以使用线程和 read 进行相同的操作。对于 mmap 用例,您无法控制或不知道页面是否已映射。与 read 相关的情况更为明确。
ssize_t read(int fd, void *buf, size_t count);
你需要指定一个
buf
和
count
。你可以明确地确定数据在程序中的位置。作为程序员,你可能知道该数据不会再被使用。因此,后续对
read
的调用可以重用上次调用所使用的同一
buf
。这样做有多个好处;最容易看到的是减少了内存使用(或至少是地址空间和MMU表)。
mmap
不知道一个页面是否仍将来被访问。
mmap
不知道页面中只有部分数据是有意义的。因此,
read
更加明确。
假设你在磁盘上有4096个大小为4095字节的记录。你需要读/查看其中两个随机记录并对它们进行操作。对于
read
,你可以使用
malloc()
分配两个大小为4095的缓冲区,也可以使用
static char buffer[2][4095]
数据。对于
mmap()
,每个记录平均必须映射8192字节以填充两页或总共16k。访问每个
mmap
记录时,记录横跨两页。这导致每个记录访问产生两个
page faults。此外,内核必须分配四个TLB / MMU页面来保存数据。
另外,如果使用顺序缓冲区进行
read
,只需要两个页面,并且只需要两个系统调用(
read
)。此外,如果记录上的计算很复杂,则缓冲区的局部性将使得它比
mmap
数据更快(CPU缓存命中)。
而且,还要将页面复制回用户空间缓冲区。
这个复制可能没有你想象的那么糟糕。CPU将缓存数据,以便下次访问时无需从主内存重新加载,因为后者可能比L1 CPU缓存慢100倍。
在上述情况下,
mmap
可能需要两倍以上的时间才能完成
read
所需的时间。
这里是否存在的问题是,page faults比一般的系统调用更昂贵 - 这是我对Linus Torvalds在这里说的理解?是因为page faults是阻塞的 => 线程没有被调度到CPU上 => 我们浪费了宝贵的时间吗?或者我漏掉了什么?
我认为主要问题是使用
mmap
时你无法控制。你将文件映射到内存中,无法确定任何部分是否在内存中。如果你随机访问文件,则它会不停地从磁盘读取,根据访问模式而定,可能会出现抖动,而你并不知情。如果访问纯粹是顺序的,则乍一看似乎并没有更好。但是,通过向同一用户缓冲区重新读取新的
块,CPU 的 L1/L2 缓存和 TLB 将更好地利用;对于您的进程和系统中的其他进程都是如此。如果你将所有块都读取到唯一的缓冲区并进行顺序处理,那么它们将大致相同(见下面的注释)。
问题 #2:支持内存映射文件的异步 I/O 存在架构限制,还是只是没有人去做它呢?
mmap
已经类似于 AIO,但其固定大小为 4k。也就是说,完整的
mmap
文件不需要全部在内存中才能开始操作。从功能上讲,它们是以不同的方式实现相似效果的不同机制。它们在体系结构上是不同的。
问题 #3:有点相关,但我对本文的理解是内核可以提前读取标准 I/O(即使没有使用 fadvise()),但不会为内存映射文件提前读取(除非用 madvice() 进行指示)。这是准确的吗?如果这个说法确实正确,那么标准 I/O 的系统调用可能更快,而内存映射文件几乎总是会导致页错误,是否是因为这个原因?
read
的差编程可能与
mmap
一样糟糕。
mmap
可以使用
madvise
。它更多地涉及到必须发生的所有 Linux MM 事务,以使
mmap
工作。这完全取决于你的用例;具体情况取决于访问模式哪个更好。我认为 Linus 只是在说两者都不是万能的解决方法。
例如,如果你使用
read
读取的缓冲区比系统中的内存还要大,并且你使用了和
mmap
相同的交换机制,则情况会更糟。如果你的系统没有交换空间,那么
mmap
对于随机读取访问将很好,并允许你管理比实际内存更大的文件。使用
read
进行此操作需要更多的代码,这通常意味着会有更多的 Bug,或者如果你太天真,你将只会得到一个 OOM 杀死消息。
注 但是,如果访问是顺序的,
read
的代码不需要那么多,它可能比
mmap
更快。
read
的其他优势
对于一些情况,read
提供了使用套接字和管道的功能。此外,像ttyS0这样的字符设备只能使用 read
。如果您编写一个从命令行获取文件名的命令行程序,则这将非常有益。如果使用 mmap
进行结构化,可能难以支持这些文件。