代码之家  ›  专栏  ›  技术社区  ›  arsenm

如何使用只能手动同步到磁盘的mmap创建检查点文件

  •  5
  • arsenm  · 技术社区  · 15 年前

    我需要最快的方式定期同步文件与内存。

    除非在我手动指定的时间,否则无法修改文件。关键是要有一个检查点文件,它在内存中保存状态的快照。我想尽量避免复制,因为这将需要调用相当频繁,速度是重要的。

    5 回复  |  直到 15 年前
        1
  •  2
  •   R.. GitHub STOP HELPING ICE    15 年前

    mmap 不能用于此目的。无法阻止数据写入磁盘。在实践中,使用 mlock() 可以 有一个副作用,防止它被写入磁盘,除非你要求它被写入,但没有保证。当然,如果另一个进程打开文件,它将看到缓存在内存中的副本(带有最新更改),而不是物理磁盘上的副本。在许多方面,您应该做什么取决于您是尝试与其他进程同步,还是仅仅为了在崩溃或电源故障时的安全。

    如果您的数据量很小,您可以尝试其他一些原子同步到磁盘的方法。一种方法是将整个数据集存储在一个文件名中,用该文件名创建一个空文件,然后删除旧文件。如果启动时存在两个文件(由于极不可能出现崩溃时间),请删除旧文件并从新文件恢复。 write() 可以 如果您的数据大小小于文件系统块、页面大小或磁盘块,那么也应该是原子的,但我不知道是否有任何保证可以立即达到这一效果。你得做些调查。

    另一种非常标准的方法,只要您的数据不是太大以至于磁盘上放不下两个拷贝,就可以工作:只要用临时名称创建第二个拷贝,然后 rename() 它在旧的上面。 总是原子的。这可能是最好的方法,除非你有理由不这样做。

        2
  •  4
  •   caf    15 年前

    任何你在一个小时内写入内存的东西 MAP_SHARED 一个文件的映射被认为是在那个时候被写到文件中的,就像您使用 write() . msync() fsync() -它只会确保你所做的改变 已经做了 对文件的访问实际上被推送到永久存储中。你不能改变这个-这是怎么回事 mmap() 定义为工作。

    一般来说,安全的方法是将数据的完整一致性副本写入临时文件,同步临时文件,然后在先前的检查点文件上对其进行原子重命名。这是确保检查点之间的崩溃不会导致文件不一致的唯一方法。任何复制较少的解决方案都需要更复杂的事务日志样式的文件格式,并且对应用程序的其余部分更具侵入性(需要在内存状态更改的每个位置调用特定的钩子)。

        3
  •  2
  •   Alexis Wilke    5 年前

    你可以的 mmap() 将文件作为“写入时复制”,以便您在内存中执行的任何更新都不会写回该文件,然后当您要同步时,可以:

    或者

    mmap() mmap()

    同步完成后,你的副本就可以写了 mmap() 与磁盘文件相同,但内核仍将需要同步的页面标记为非共享(与磁盘)。这样就可以关闭并重新创建 (仍然是写时复制)这样内核就可以在有内存压力的情况下在必要时丢弃页面(而不是将它们分页以交换空间)。

    syscall() ?)

    --编辑--

    Can the dirtiness of pages of a mmap be found from userspace? 了解如何查看哪些页面是脏的。

        4
  •  2
  •   Alexis Wilke    5 年前

    正如其他受访者所建议的那样,我不认为有一种便携的方式可以不复制就做你想做的事情。如果您希望在一个可以控制操作系统等的专用环境中执行此操作,那么您可以在Linux下使用btrfs文件系统执行此操作。

    btrfs支持新的 reflink() 操作,本质上是一个写时拷贝文件系统拷贝。你可以的 你的文件在启动时被临时保存, mmap() msync() 重新链接()

        5
  •  -1
  •   nategoose    15 年前

    我高度怀疑这可能不会被任何操作系统利用,但操作系统可能会注意到以下优化:

    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”时会导致大量磁盘写入。如果它被利用了,那就太酷了。

    推荐文章