Linux文件API的持久化保障

更多互联网新鲜资讯、工作奇淫技巧关注原创【飞鱼在浪屿】(日更新)
Linux文件API的持久化保障文章插图
在研究云系统提供的持久性时 , 想确保自己了解基本知识 。 首先阅读NVMe规范 , 以了解disks提供的保证() 。 简单来说 , 你应该假设 , 在发出写入到刷新或强制数据单元访问写入完成之间 , 数据已损坏 。
大多数程序使用系统调用来写入数据 。 本文着眼于Linux文件API提供的保证 。 看起来这应该很简单:程序调用write()并且完成后 , 数据是持久的 。 然而 , write()仅将数据从应用程序复制到内存中的内核缓存中 。 为了强制数据持久 , 您需要使用一些其他机制 。
本文知识的凌乱集 。 (简单来说使用fdatasync或使用O_DSYNC打开) 。 更好更清晰的概述 , 请参见LWN的文章() 。
write()的语义在IEEE POSIX标准中 , 将write系统调用定义为尝试将数据写入文件描述符 。 成功返回后 , 即使是由其他进程或线程读取或写入的 , 也需要进行读取以返回已写入的字 。
常规文件操作的线程交互:“如果两个线程各自调用这些函数之一 , 则每个调用应看到另一个调用的所有指定效果 , 或者都不看到 。 ” 这表明所有文件I / O必须有效地持有一个锁 。
这是否意味着写是原子的?从技术上讲 , 是的:将来的读取必须返回写入的全部内容 , 或者不返回任何内容 。 但是 , 写入并不一定要完成 , 只允许传输部分数据 。 例如 , 有两个线程 , 每个线程将1024个字节附加到一个文件描述符中 。 两次写入到每次只写入一个字节是可以接受的 。 这仍然是“原子的” , 但也会导致不希望的交错输出 。 有一个很棒的StackOverflow答案 , 有更多细节 。 #42442926
fsync / fdatasync在磁盘上获取数据的最直接方法是调用fsync() 。 它要求操作系统将缓存中所有修改的块以及所有文件元数据(例如访问时间 , 修改时间等)传输到磁盘 。 元数据很少有用 , 因此除非你知道需要元数据 , 否则应使用fdatasync 。 该fdatasync是flush尽可能多的元数据作为必要“的后续数据读取要正确处理” , 这才是多数应用关心的事 ,。
一个问题是不能保证可以再次找到该文件 。 特别是 , 第一次创建文件时 , 需要在包含该文件的目录上调用fsync , 否则在失败后该文件可能不存在 。 原因基本上是在UNIX中 , 由于硬链接 , 一个文件可以存在于多个目录中 , 因此 , 当你在文件上调用fsync时 , 无法确定应该写出哪个目录() 。 ext4实际上可能会自动同步目录 , 但是对于其他文件系统可能并非如此 。
实施方式会因文件系统而异 。 使用blktrace来检查ext4和xfs使用了哪些磁盘操作 。 他们都对文件数据和文件系统日志发出正常的磁盘写操作 , 使用高速缓存刷新 , 然后对日志进行FUA(Force Unit Access)写操作 , 这可能表示操作已提交 。 在不支持FUA的磁盘上 , 这涉及两次缓存刷新 。 实验表明 , fdatasync比fsync快一点 , 而blktrace显示fdatasync倾向于写入更少的数据(ext4:fsync为20 kiB , fdatasync为16 kiB) 。 实验还表明 , xfs的速度比ext4稍快 , 并且blktrace再次表明它倾向于清除较少的数据(xfs:fdatasync为4 kiB) 。
用O_SYNC / O_DSYNC打开系统要求耐久性 。 另一种选择是在open()系统调用中使用O_SYNC或O_DSYNC选项 。 这将导致每个写入的语义与写入后分别带有fsync / fdatasync的语义相同 。 POSIX规范将此称为“同步I / O文件完整性完成”和“数据完整性完成” 。 这种方法的主要优点是 , 你只需要单个系统调用 , 而不是先写入后跟fdatasync 。 最大的缺点是使用该文件描述符的所有写入都将被同步 , 这可能会限制应用程序代码的结构 。


推荐阅读