我正在开发一个严格与页面大小对齐的高性能消息传递异步缓冲区。为了确保在崩溃或意外终止时数据持久存在(不触发 sigaction),我使用 mmap 将此缓冲区映射到文件。这样,如果进程崩溃或被杀死,我希望在进程重新启动时从映射文件中恢复任何未处理的数据(我不需要100%成功,只需要一定的成功概率)。
到目前为止,这种方法在大多数系统上运行良好,没有任何明显的性能开销。但是,我在 Android NDK 上遇到了问题。
在 Android 设备上,我注意到一旦使用 mmap,有时内存数据就会损坏。当我禁用 mmap 时,数据的行为符合预期并且不会损坏。我的怀疑是,虽然我从未明确调用 msync 并依赖系统定期将数据同步到文件(并且可能在退出时),但 Android 系统(或可能其他操作系统)可能偶尔会在某些条件下执行自动同步操作满足 - 例如,当系统内存不足并执行交换时。
我怀疑系统的自动同步过程(例如,在内存压力或交换的情况下)导致数据损坏。具体来说,我认为系统可能会将数据从内存同步到文件,然后从文件同步回内存,从而破坏了线程之间的预期同步。这是因为负责这些操作的操作系统线程不遵守我的应用程序线程强制执行的内存排序约束。
我需要一个解决方案,一旦在开始时创建 mmap,内存仅从内存同步到文件,而不会从文件同步回内存。仅内存到文件同步,在进程的生命周期内没有文件到内存同步。
那么,有什么方法可以阻止操作系统将数据同步回内存(例如禁用 MS_INVALIDATE 或类似功能)?
或者,是否有任何替代方案或最佳实践可以在多线程环境中实现这种持久缓冲区?
您对
mmap
的假设与实际实施不符。请记住,C++ 和 Linux 都抽象出了许多硬件细节。具体来说,您从 mmap
获得的指针不是指向物理 RAM 的指针。
相反,你得到的指针是一个抽象值。在进程的生命周期内,写入的数据可以被读回。从这个意义上说,它非常像
malloc
。事实上,两者都不能保证数据实际上存储在 RAM 中。在这两种情况下,数据都可能被分页到磁盘。
具体来说,在您的情况下,该磁盘存储将是
mmap
中指定的后备文件。
像
malloc
一样,mmap
不会神奇地解决你的并发问题。线程的并发访问需要同步。与 malloc
一样,数据是否分页到磁盘或物理 RAM 中并不重要。与malloc
一样,操作系统不会导致损坏。
这里唯一相关的区别是您需要进程间同步。
malloc
分配只能由一个程序的线程访问的内存。
假设您遵循同步规则,Linux 永远不会用磁盘中的字节覆盖 RAM。
Linux不会自动同步线程。这是你的责任。由于 CPU 核心数量有限,Linux 通常被迫不能同时运行所有线程,但这并不是同步。即使两个线程物理上顺序运行,仍然需要同步才能可靠运行。