文件系统错误处理我们将从大约十年前撰写的有关文件系统健壮性的论文中复制一些结果:Prabhakaran等。 SOSP 05论文,它在文件系统和Gunawi等人的论文下面注入了错误。 FAST 08,它检查文件系统未能检查可返回错误的函数的返回码的频率。
Prabhakaran等。在块设备级别(恰好在文件系统下面)注入了错误,发现ext3,resierfs,ntfs和jfs主要处理读错误,而ext3,ntfs和jfs大多数忽略写错误。尽管这篇文章很有趣,但是与Prahbhakaran等人测试过的任何最新文件系统相比,今天在系统上安装Linux的人更可能使用ext4。我们将尝试重现本文中有关ext4和btrfs等更现代的文件系统,exfat,ext3和jfs等一些旧文件系统以及overlayfs的一些基本结果。
Gunawi等。发现大多数时间未检查错误。在研究了现代文件系统上的错误注入之后,我们将看看有多少(或很少)文件系统改进了它们的错误处理代码。
读取的文件的卡通视图可能是:pread syscall-> OS通用文件系统代码->文件系统特定的代码->阻止设备代码->设备驱动程序->设备控制器->磁盘。磁盘获得请求后,它将发送数据备份:disk->设备控制器->设备驱动程序->阻止设备代码->文件系统特定的代码-> OS通用文件系统代码->前置我们将在文件系统正下方的块设备级别研究错误注入。
让我们看一下我们在2017年注入错误时发生的情况以及Prabhakaran等人的情况。发现于2005年。
每行显示一个文件系统的结果。读取和写入分别表示读取和写入数据,其中块设备返回指示操作失败的错误。 Silent表示读取失败(数据不正确),其中块设备未指示错误。如果磁盘损坏,瞬时读取失败或瞬时写入失败默默地导致不良数据被写入,则可能会发生这种情况。文件表示对打开为打开的文件进行了操作,而mmap表示对映射为mmap的文件进行了测试。 ignore(红色)表示已忽略该错误,prop(黄色)表示该错误已传播,并且pread或pwrite syscall返回了错误代码,而fix(绿色)表示已纠正该错误。没有错误得到纠正。灰色条目表示未经测试的配置。
从表中可以看到,在2005年,即使块设备指示写入失败并且情况有所改善,并且可能使用的任何文件系统都会正确告诉您,ext3和jfs忽略了写入错误。写失败。 jfs尚未改进,但是jfs现在很少在旧式安装之外使用。
除btrfs之外,没有经过测试的文件系统可以正确处理静默故障。其他文件系统未测试重复数据或校验和数据,因此它们无法检测到静默故障。 zfs可能还可以正确处理静默故障,但未经测试。尽管对btrfs和zfs进行了约会,但apfs做出了明确的决定,即不校验和数据,并且在出现静默块设备错误时静默失败。我们将在以后再讨论。
在传播错误的所有测试情况下,文件读取和写入分别从pread或pwrite返回EIO; mmap的读取和写入导致进程接收到SIGBUS信号。
上面的2017测试使用了8k文件,其中第一个包含文件数据的块在块设备级别返回了错误或已损坏,具体取决于测试。下表测试了相同的内容,但是使用445字节文件而不是8k文件。 445的选择是任意的。
在小型文件测试表中,除btrfs以外,所有结果均相同,而btrfs在每种情况下均返回正确的数据。这里发生的是文件系统是在旋转磁盘上创建的,并且默认情况下,btrfs在旋转磁盘上复制文件系统元数据(可以配置为在SSD上这样做,但这不是默认设置)。由于文件很小,因此btrfs将文件打包到元数据中,并且该文件与元数据一起复制,从而使文件系统可以在一个块返回错误数据或报告故障时修复错误。
Overlayfs允许将一个文件系统“覆盖”在另一个文件系统上。如最初提交中所述,一种用例可能是将(上部)读写目录树放在(下部)只读目录树的顶部,所有修改都移至上部可写层。
尽管未在表中列出,但我们也测试了除fat以外的每个文件系统,将它们作为具有覆盖fs的较低文件系统(ext4是所有测试的较高文件系统)。当用作覆盖层的底层时,测试的每个文件系统都显示与单独使用时相同的结果。尚未测试fat,因为安装fat会导致文件系统不支持的错误。
默认情况下,btrfs不会在SSD上复制元数据,因为开发人员认为冗余不会为防止SSD上的错误提供保护(这与apfs没有冗余的原因相同)。 SSD执行某种写入合并,这很可能导致连续发生的写入落入同一块中。如果该块完全失败,则冗余副本将全部丢失,因此冗余无法提供与旋转驱动器一样多的故障保护。
我不确定这是否意味着冗余将无济于事-各个闪存单元会随着运行而降级,并随着它们的老化而失去电荷。 SSD具有内置的损耗均衡和纠错功能,旨在降低块返回不良数据的可能性,但是随着时间的流逝,某些块会产生大量错误,因此纠错将无法修复错误。并且该块将返回错误数据。在这种情况下,读操作应返回一些不良位以及大多数不良位。 AFAICT的关于SSD错误率的公开数据似乎与此观点一致。
相关地,似乎apfs不会校验和数据,因为“ [apfs]工程师认为Apple设备基本上不会返回假数据”。关于SSD可靠性的公开研究尚未发现,有一种模型有时不会返回不良数据。通常的观念是,与旋转磁盘相比,固态硬盘返回坏数据的可能性较小,但是当Google在其驱动器上进行研究时,他们发现:
以前,硬盘驱动器的年更换率为2-9%[19,20],与我们在四年内看到的闪存驱动器的4-10%相比,这个数字很高。但是,就错误率而言,闪存驱动器吸引力不大。在四年的时间内,超过20%的闪存驱动器会产生无法纠正的错误,30-80%的驱动器会产生坏块,而其中2-7%的驱动器会产生坏芯片。相比之下,以前有关HDD的工作[1]报告说,在大量硬盘中,只有3.5%的磁盘在32个月内出现了坏扇区–考虑到硬盘上的扇区数量是几个数量级,这个数字很小。大于固态驱动器上的块或芯片的数量,并且扇区小于块,因此故障的严重性就较小。
从某种意义上说,SSD比旋转磁盘更可靠,但从某种意义上说,它们似乎不那么可靠。 Apple在驱动器上使用某种自定义固件来进行纠错比在公共磁盘上投入更多的比特并不是不可能,但是即使是这种情况,您也可以将非Apple驱动器插入Apple计算机并想要某种防止数据损坏的保护措施。
现在,我们已经复制了Prabhakaran等人的一些测试,我们将继续研究Gunawi等人。由于本文涉及的内容非常丰富,因此我们只看一下本文的一小部分,即在该部分中,他们检查了三个函数调用,分别是filemap_fdatawait,filemap_fdatawrite和sync_blockdev,以查看不检查这些函数的错误的频率。
如第3.1节所述,一个函数可以同时返回多个错误代码,并且仅检查其中一个就足够了。但是,如果我们知道某个函数只返回一个错误代码,而调用者却没有正确保存返回值,那么我们就会知道这样的调用确实是一个缺陷。为了发现文件系统代码中的真正缺陷,我们检查了三个我们仅知道会返回单个错误代码的重要函数:sync_blockdev,filemap_fdatawrite和filemap_fdatawait。如果文件系统不检查从这些函数返回的错误代码,则显然会使上层未注意到故障。
忽略这些功能中的错误似乎会带来相当严重的后果。 filemap_fdatawait的文档说:
filemap_fdatawait —等待所有欠写回页面完成...步行给定地址空间的欠写回页面列表,然后等待所有它们。检查地址空间的错误状态并返回它。由于此功能清除了地址空间的错误状态,因此调用者负责检查返回值以及处理和/或报告错误。
写出并等待通过块映射与块设备关联的所有脏数据。不采用超级块锁定。
在这两种情况下,忽略错误代码似乎都意味着数据将无法写入磁盘而没有通知写入者该数据不是实际写入的?
让我们看看对这些函数的调用没有完全忽略错误代码的频率:
该表适用于Linux下fs下的所有代码。每行显示一个函数的调用数据。对于每年,最左边的单元格显示的是在返回的总次数中使用返回值执行某项操作的呼叫数。右边的单元格显示使用返回值执行操作的呼叫百分比。 “做某事”在这里非常宽松-分支返回值,然后在任何一个分支中均无法处理错误,返回返回值并使调用者无法处理返回值,以及保存返回值和然后忽略此表,则认为所有操作都在做。
尽管cifs_sign_smb返回了错误代码,但从未被检查过,然后被smb_send覆盖,即使未处理该错误,该代码仍被视为用于我们的目的。
总体而言,该表似乎表明,现在处理的错误比2008年Gunawi等提出的错误要多得多。进行了分析,但是很难看清原始数字的含义,因为可以解决一些错误并且以不同的概率执行不同的代码行是可以的。
文件系统错误处理似乎有所改善。如果块设备报告错误,则在pwrite上报告错误,这可能是健壮的文件系统应该执行的最基本的错误传播;很少有文件系统在2005年正确地报告了该错误。今天,如果没有复杂的因素,那么当最简单的错误情况(不涉及整个驱动器死机)发生时,大多数文件系统将正确地报告错误。
大多数文件系统都没有数据校验和,而错误检测和纠正则留给用户空间软件进行。当我与大公司的服务器端开发人员交谈时,他们的回答通常是“谁在乎?我们所有的文件访问都经过一个库,该库无论如何都会对所有事物进行校验和,并且跨机器和数据中心的冗余可以解决故障,因此我们只需要错误检测而无需纠正。”尽管某些大公司的开发人员确实如此,但其中有很多软件编写得不够好,只是假设文件系统和磁盘没有错误。
这是与Wesley Aptekar-Cassels的联合项目;该项目的绝大部分工作是在RC进行结对编程时完成的。凯特·墨菲(Kate Murphy)也提供了很多帮助。卫斯理([email protected])和凯特([email protected])都在找工作。他们很棒,如果您正在招聘,我强烈建议与他们交谈!
为了获得正确的错误处理,已经进行了大量的工作。但是,即使您付出了相当大的努力,甚至使用了额外的工具,使用C都很容易出错。代码中的一个示例是Submit_one_bio函数。如果看一下定义,您会发现它用__must_check进行了注释,当忽略结果时,它将引起编译器警告。但是,如果您查看submit_one_bio的调用,您会发现其调用者没有注释,可以忽略错误。如果深入研究,您会发现一条错误传播路径,如下所示:
从commit_one_bio中删除了9个级别,我们看到了我们的老朋友`filemap_fdatawrite,我们知道通常不会检查错误。
关于如何防止此类意外事件的争论非常古老。我称之为鲍勃叔叔(UB)学派的一所思想流派认为,我们无法用工具或流程解决这类问题,仅需要成为更好的程序员来避免错误。您会经常听到UB学校的人说类似的事情,“您无法使用更好的工具(或过程)摆脱所有错误”。 Rich Hickey在他著名且备受好评的演讲中说:
它写了。是。关于它的更有趣的事实是什么?它通过了类型检查器。
它通过了所有测试。好的。那么,现在您该怎么办?对?我想我们在这个世界上想打电话给护栏编程。对?真的很伤心。我们就像:我可以进行更改,因为我有测试。是谁啊谁开着车撞在护栏上说:哇!我很高兴获得了这些护栏,因为我从来没有准时参加演出。
如果您观看谈话,Rich会使用" simplicity" Bob叔叔使用"纪律"的方式。他们使用这些陈述的方式,大致相当于Ken Thompson所说的"错误就是错误。您编写带有错误的代码是因为这样做。 UB学校在总线下扔了工具和流程,说仅仅依靠工具或流程是不安全的。
里奇的修辞手法非常出色-自从谈论反对测试,工具或类型的讨论以来,我听说那行引用了数十次。但是,就像护栏一样,大多数工具和流程并不是要消除所有错误,而是要降低错误的严重性或可能性。如果我们看一下这个特定的函数调用,我们可以看到静态分析工具未能找到该错误。这是否意味着我们应该放弃静态分析工具?静态分析工具可以查找所有commit_one_bio调用,并向您显示将错误传播到N个级别而仅将其删除的情况。 Gunawi等。确实做到了,发现了很多错误。没有工具,一个人基本上不可能做同样的事情。他们可以尝试,但如果人们在手动进行此类操作时获得95%的准确度,他们将很幸运。庞大的代码量保证了人工操作会出错。
甚至比静态分析工具更好的语言是一种语言,它使人们更容易意外忘记检查错误。这里的问题之一是删除错误有时是有效的。在很多地方,没有接口可以使错误从文件系统中传播出去,从而可以正确地删除错误,对接口进行模更改。在当前情况下,作为局外人阅读代码,如果您查看一堆掉落错误的调用,那么对于所有错误来说,很难说出哪个是错误,哪个是错误。正确。如果默认情况是我们有一种说“必须检查此错误”的护栏,那么人们仍然可以错误地忽略错误,但是您至少会得到一个注释,表示故意遗漏了。例如,如果您被迫专门编写表明您忽略错误的代码,并且在文件系统代码等本来要健壮的代码中,故意丢弃错误的代码相对有可能附有解释错误原因的注释。
毕竟,如果我们知道现代文件系统是否可以正确完成基本任务,那就太好了。文件系统开发人员可能知道这些知识,但是由于我不遵循LKML,因此在进行实验之前,我不知道自2005年以来情况是否有所改善。
我们在这里查看的论文来自Andrea和Remzi Arpaci-Dusseau的研究实验室。 Remzi在一次演讲中提到,研究生不希望复制和更新旧作品。考虑到他们面临的激励,这是完全合理的。我不是要在这里选择学术界-这项工作来自学术界,而不是行业。如果没有学术奖励制度,这种工作可能根本不会发生。
通常,为正确性工作提供资金似乎非常困难。关于发现错误的新方法的论文很多,但是将现有技术应用于现有代码的工作相对较少。在学术界,在开源世界中很难找到一个好的出版物,这对人们来说似乎比编写新代码有趣。那也是完全合理的-人们应该按照自己想要的去工作,即使他们喜欢正确性,总体上这也不是一个伟大的职业决定。那天晚上我参加了RC招聘会,徽章上写着我对测试感兴趣。与我聊天的第一个人以"开始从事质量检查工作吗?"。回到我在硬件上工作时,这并不是一个危险的信号,但是在软件中,&Q34&QA"是低技能,乏味且薪水低的工作的代码。许多行业认为测试和质量检查是事后的想法。结果,公司所依赖的开放源代码项目常常资金不足。 Google资助了一些出色的工作(例如afl-fuzz),但这是一个例外,而不是常规,即使在Google内部也是如此,而且大多数公司不资助任何开源工作。这篇文章中的工作是由一些有意暂时失业的人完成的,这实际上不是可扩展的模型。
有时,您会看到有人在提高正确性上花费了很多心血,但这通常是大量的自由劳动。凯尔·金斯伯里(Kyle Kingsbury)可能是一个典型的例子-我的理解是,他在晚上和周末从事Jepsen分布式系统测试工具的工作了多年,然后才将其转变为咨询业务。他做到了,真是太好了-他表明几乎每个开源分布式系统都存在严重的数据丢失或损坏错误。我认为这很不错,但是关于英雄式努力的故事总是让我感到担忧,因为英雄主义无法扩展。如果凯尔(Kyle)没有出现,他和他的工具发现的大多数错误是否还会困扰当今的开源分布式系统?那是一个可怕的想法。
如果我知道如何为正确性上的更多工作提供资金,我会试图说服您我们应该改用这种新模型,但是我不知道可行的资金模型。我已经建立了一个patreon(捐赠帐户),但是如果这足以实际资助大量工作,那将是非常不寻常的。如果你看看程序员从捐赠中赚了多少钱,如果我减少了两个数量级,
......