我需要最快的方法定期同步文件和内存。
我认为我想要的是使用 mmap 的文件,只有在手动同步到磁盘时才会同步。我不确定如何防止自动同步发生。
文件只能在我手动指定的时间进行修改。重点是要有一个检查点文件,保存内存中状态的快照。我希望尽可能减少复制次数,因为这将会是相当频繁调用的,速度很重要。
我需要最快的方法定期同步文件和内存。
我认为我想要的是使用 mmap 的文件,只有在手动同步到磁盘时才会同步。我不确定如何防止自动同步发生。
文件只能在我手动指定的时间进行修改。重点是要有一个检查点文件,保存内存中状态的快照。我希望尽可能减少复制次数,因为这将会是相当频繁调用的,速度很重要。
mmap()
,这样在内存中进行的任何更新都不会写回文件,然后当您想要同步时,您可以:mmap()
很大并且没有空间将另一个巨大的文件mmap()
,则此方法具有不使用地址空间的好处。mmap()
与磁盘文件相同,但内核仍然将您需要同步的页面标记为非共享(与磁盘不共享)。因此,您可以关闭并重新创建mmap()
(仍为写时复制),这样内核就可以在需要时丢弃您的页面(而不是将它们换出到交换空间),如果存在内存压力。syscall()
吗?)MAP_SHARED
映射文件时,写入内存的任何内容都被视为在此时写入文件,就像你使用了write()
一样。从这个意义上讲,msync()
与fsync()
完全相似-它只是确保你已经对文件进行的更改实际上被推送到永久存储器中。你不能改变这一点-这是mmap()
定义的工作方式。mmap
不能用于此目的。无法防止数据被写入磁盘。在实践中,使用mlock()
使内存不可交换可能会有一个副作用,即除非您要求将其写入,否则它不会被写入磁盘,但没有保证。当然,如果另一个进程打开文件,则它将看到缓存在内存中的副本(带有您最新的更改),而不是物理磁盘上的副本。在很多方面,你应该根据你是否想要与其他进程同步或仅为了在崩溃或断电时安全而做出决定。
如果您的数据量较小,您可以尝试许多其他原子同步到磁盘的方法。一种方法是将整个数据集存储在文件名中,并通过该名称创建一个空文件,然后删除旧文件。如果在启动时存在2个文件(由于极少见的崩溃时间),请删除旧文件并从新文件恢复。如果您的数据大小小于文件系统块、页面大小或磁盘块,则write()
可能也是原子性的,但我不知道任何保证效果的方法。您需要进行一些研究。
另一种非常标准的方法,只要您的数据不太大,2个副本就不适合放在磁盘上:只需使用临时名称创建第二个副本,然后将其rename()
覆盖旧的副本。 rename()
始终是原子性的。除非您有理由不以这种方式进行,否则这可能是最佳方法。
我非常怀疑任何操作系统都可能不会利用这一点,但是对于操作系统来说,注意到以下优化是有可能的:
int fd = open("file", O_RDWR | O_SYNC | O_DIRECT);
size_t length = get_lenght(fd);
uint8_t * map_addr = mmap(NULL, length, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0);
...
// This represents all of the changes that could possibly happen before you
// want to update the on disk file.
change_various_data(map_addr);
if (is_time_to_update()) {
write(fd, map_addr, length);
lseek(fd, 0, SEEK_SET);
// you could have just used pwrite here and not seeked
}
操作系统可能会利用这种方式的原因是,在你写入特定页面之前(也没有其他人写入),操作系统可能会将实际文件在该位置的页面作为该页面的交换空间。
然后,当你写入某些页面时,操作系统会对你的进程进行“复制写入”,但仍然保留未写入的页面备份。
然后,在调用write
时,操作系统可以注意到写入的内容在内存和磁盘上都是块对齐的,然后它可以注意到一些源内存页面已经与它们被写入的确切文件系统页面同步,并且只写出已更改的页面。
尽管如此,如果任何操作系统都没有执行此优化,这种类型的代码最终会变得非常缓慢,并在调用“write”时导致大量磁盘写入。如果能够利用这种方式就太棒了。