好的,哇,我关于空文件的帖子产生了很多反馈,主要是底部的一次性行,即空文件是 /bin/true 可能的最小版本。
首先,Rob Pike 写了一个指向他的推文的链接。谢谢,罗伯!这正是让我大吃一惊的反馈:当绝对知道的人出现并提供权威答案时。
一群其他人写信要么提及推文,链接到推文,要么将其固定在律师身上(在一种情况下,还有 GNU)。一切都是正确的,所以每个人都赢了。
Garrett 写了一个链接,该链接指向另一个来自 CP/M 世界的零字节文件,名为The Infinitely Profitable Program 。 (如果您不耐烦,请查找有关“go.com”的部分。)
我还从这篇文章的作者那里听说了 POSIX true(1) ,他指出了一些参考文献,包括 John Chambers 的一篇名为The /bin/true Command and Copyright的参考文献。因此,似乎人们已经投入了一些实际工作来弄清楚这一切是如何发生的。
…
一位人士提到,Linux 领域在文件系统操作、原子性和到处丢弃零字节文件方面存在分歧。我肯定在有问题的工作中看到了其中的一些。我已经把这个故事搁置了一段时间,现在似乎是分享它的绝佳机会,所以我们开始吧。
除了零字节文件之外,我们看到的另一个问题是机器在写入操作期间重新启动时会*混淆文件内容*。这通常表现为 RPM 垃圾被写入没有包含 RPM 信息的业务的文件。
我从另一个角度了解了它。我支持的一个团队拥有这个在所有机器上运行的“代理”(实际上是一个缓存,但名称不变)。你会要求它为你获取配置项,它会连接到配置后端存储并在它们上设置“监视”。然后,每当它发生变化时,它都会为您提供更新。
在某个时候,有人决定让“代理”记住盒子上明确要求的内容,因此他们让它在*纯文本文件*中转储出所要求的项目列表,每行一个。这工作正常,但随后它与文件系统损坏怪物交叉路径。他们的文件收集了各种与配置存储中的任何内容无关的二进制垃圾。
下次机器启动时,它启动了他们的“代理”,并尝试执行“预热”模式。它通读该文件,每次在二进制废话中看到换行符时,都会将其视为路径。然后它创建了一个线程去从后端加载它。现在,由于这绝对是垃圾,所以 fetch 永远不会成功。他们假设只有有效的路径会出现在这里,所以他们让这些事情重试,直到它成功。而且,当然,一堆乱码是不可能成功的,所以线程并没有消失。
现在进行最后的摩擦:这些线程中的每一个都使过程变得更大。这是您在 ps 等工具中看到的 VSZ – 不是物理内存,而是它的整体*虚拟*大小。它可能比进程的实际物理分配 (RSS) 大很多倍。
在这种情况下,出于某种原因,一些好心的人试图限制程序的规模。他们使用 ulimit(又名 setrlimit)来做到这一点。问题是 RSS *不是*您可以使用 u/rlimits 控制的事情之一。有一个设置看起来很像适用于此类事情,但它仅限制 *VSZ*。
所以,当所有这一切最终都融合在一起时,你就会有一个“代理”,它会出现,尝试启动大量从未消失的线程,使其 VSZ 膨胀,然后限制将启动,它会开始失败内存分配。也就是说,C++ 会为诸如“new”之类的东西抛出一个“bad alloc”异常,并且程序会死掉,因为没有人期望 *that* 会失败。
我从未发现该公司机器上的文件之间发生了多少异花授粉。据我们所知,这可能导致猫图片#1 的内容被写入猫图片#2 的文件,反之亦然。那将是一场史诗般的隐私失败,不是吗?
据我所知,内核在关闭时尝试将其缓冲区刷新到磁盘是一件很疯狂的事情。不知何故,缓冲区 A 不一定会刷新到文件 A,依此类推。毫无疑问,有些人记得这一点并且确切地知道发生了什么,但我不是其中之一。
修复所有这些缺陷涉及使内核不会混淆其缓冲区,并实际删除有人放在启动脚本中的善意但最终无用的“ulimit -v”。至于纯文本文件,好吧,我试着让他们把它变成用框架或其他方式检测损坏的强(而不是“字符串”)类型的东西,但开发人员并没有这样做。叹。