C 程序在 Mac OS X Snow Leopard 上执行磁盘 I/O 时卡在不间断等待

Posted

技术标签:

【中文标题】C 程序在 Mac OS X Snow Leopard 上执行磁盘 I/O 时卡在不间断等待【英文标题】:C program stuck on uninterruptible wait while performing disk I/O on Mac OS X Snow Leopard 【发布时间】:2011-01-02 07:15:51 【问题描述】:

一线背景:我是Redis, a NoSQL database的开发者。我正在实现的新功能之一是虚拟内存,因为 Redis 将所有数据都存储在内存中。多亏了 VM Redis 能够将很少使用的对象从内存传输到磁盘,这比让操作系统为我们交换工作要好得多的原因有很多(redis 对象是由许多不连续分配的小对象构建的位置,当被 Redis 序列化到磁盘时,它们占用的空间比它们所在的内存页面少 10 倍,依此类推)。

现在我有一个在 Linux 上完美运行的 alpha 实现,但在 Mac OS X Snow Leopard 上却不是很好。有时,当 Redis 尝试将页面从内存移动到磁盘时,redis 进程会进入几分钟的不间断等待状态。我无法对此进行调试,但这发生在对fseeko()fwrite() 的调用中。几分钟后,调用终于返回,redis 继续毫无问题地工作:没有崩溃。

传输的数据量非常很小,大约 256 字节。所以这不应该是执行大量 I/O 的问题。

但是关于作为写操作目标的交换文件有一个有趣的细节。这是一个大文件(26 GB),使用fopen() 打开文件,然后使用ftruncate() 放大。最后这个文件是unlink()ed,所以 Redis 会继续引用它,但我们确信当 Redis 进程退出时,操作系统会真正释放交换文件。

好的,这就是全部,但我在这里了解更多详细信息。顺便说一句,您甚至可以在 Redis git 中找到实际代码,但鉴于这是一个相当复杂的系统,在五分钟内理解它并非易事。

非常感谢您的帮助。

【问题讨论】:

更多信息:现在尝试使用较小的交换文件 (256 MB),即使数据完全写入相同的位置和相同的页数,该错误也消失了。鉴于答案中的这个和其他猜测,看起来很像发生的事情是几次写入后的操作系统似乎试图在文件系统中物理分配大文件,考虑到大小,这需要几分钟。我可以在启动时“修复”这个写入几个随机字节,以便尽快强制物理分配,至少作为一个选项。非常感谢。将在此处推送更新。 【参考方案1】:

据我了解,HFS+ 对稀疏文件的支持很差。因此,您的写入可能会触发文件扩展,该文件扩展正在初始化/实现大部分文件。

例如,我知道映射一个新的大型空文件,然后在几个随机位置写入会在 HFS+ 的磁盘上生成一个非常大的文件。这很烦人,因为 mmap 和稀疏文件是一种非常方便的数据处理方式,几乎所有其他平台/文件系统都可以优雅地处理这个问题。

交换文件是线性写入的吗?这意味着我们要么替换现有块,要么在最后写入一个新块并增加一个可用空间指针?如果是这样,也许更频繁地进行较小的 ftruncate 调用来扩展文件会导致更短的暂停。

顺便说一句,我很好奇为什么 redis VM 不使用 mmap,然后只是移动块以试图将热块集中到热页中。

【讨论】:

你好杰森。是的,这也是我的想法:出于某种原因,在 ftruncate() 之后和几次写入之后,HFS+ 实现在某些时候认为是时候实现文件的很大一部分了。页面是增量分配的。我使用类似于 Linux Kernel 的算法。我尝试逐步分配给定数量的页面,而不是不时返回文件的开头搜索空闲的连续块。所以增量 ftruncates() 是一个好主意 AFAIK。我想过,但避免在启动时告诉“空间不足”需要全盘。 我想知道,即使在支持稀疏文件的系统上,ftruncate() 是否实际上也会保留文件空间?另外:我听说 Apple 已经开始开发一个新的文件系统,而不是从 HFS 派生的。除非他们这样做,否则 OSX 将永远无法用于服务器,并且对于部署到 linux/solaris/etc 的开发人员来说会很烦人。 尝试使用较小的文件后,该错误消失了。所以我认为你的答案是正确的,在 ftruncate 之后,第一次写入可能正在实现文件。鉴于每个人都在 Linux 上运行 Redis 进行生产,这不是一个大问题,但最好知道 :) 谢谢【参考方案2】:

antirez,由于我的 Apple 经验仅限于 Apple ][,因此我不确定我是否会提供太多帮助,但我会试一试。

首先是一个问题。我原以为,对于虚拟内存,运行速度将是比磁盘空间更重要的衡量标准(尤其是对于速度是重点的 NoSQL DB,否则你会使用 SQL,不是吗?)。但是,如果您的交换文件是 26G,则可能不是 :-)

尝试一些事情(如果可能的话)。

    尝试将问题实际隔离到查找或写入。我很难相信寻找可能需要那么长时间,因为在最坏的情况下,它应该是缓冲区指针的变化。不过,我没有编写 OSX,所以我不能确定。 尝试调整交换文件的大小,看看是否是导致问题的原因。 您是否曾经动态扩展交换文件(相对于预分配)?如果你这样做了,那可能就是导致问题的原因。 您是否总是在文件中写得尽可能低?可能是创建一个 26G 文件实际上可能不会用数据填充它,但是,如果创建它然后写入最后一个字节,操作系统可能必须在此之前将字节清零(延迟初始化,如果有的话)。李> 如果您只是预先分配整个文件(写入每个字节)而不取消链接,会发生什么情况?换句话说,在程序运行之间将文件保留在那里(当然,如果它不存在,则创建它)。然后在 Redis 的启动代码中,只需初始化文件(指针等)。这可能会消除上面第 4 点中的任何问题。 也可以在各种 BSD 站点上询问。我不确定 Apple 在幕后改变了多少,但 OSX 只是最低级别的 BSD(Pax 掩护)。 还可以考虑在 Apple 网站上询问(如果您还没有这样做的话)。

嗯,这是我的小贡献,希望它会有所帮助。祝你的项目好运。

【讨论】:

您好,您的评论很棒!非常感谢你。关于大小,确实重点是速度,但是有很多数据集通常只有整个数据集的 5% 被积极使用,所以有时大交换文件会很方便。在 Redis 中,用户可以配置交换文件的大小(页面大小和实际的页数)和 Redis 可以使用的 RAM 量,因此需要针对您的数据集很好地调整系统。顺便说一句:1)好主意。 2)确实,这可以确认是否是实际的文件分配时间。 3)很难从空间不足中恢复,但是...... (继续)我也会尝试使用 4。 5) 启动时间可能太长,我想逐步进行,并且考虑到它在 Linux 上可以工作,并且是第一个部署平台... 6 和 7) 也是好主意。精彩的评论和帮助。谢谢!在这一点上,我最好的猜测是,这只是 mac os x 在几次写入后试图在磁盘上分配文件,鉴于这是一个 26 GB 的文件,它需要很长时间。 antirez, re "5) 启动时间可能过大":我建议您在第一次运行程序时执行此操作一次并将交换文件保留在其中运行之间。这样,后续运行就不必创建文件。他们仍然必须初始化它,但希望这只是在它的开头写入一些指针类型值或零计数的情况。 这样,当你的程序启动时,你总是有一个交换文件——操作系统不可能在你使用它时懒惰地创建文件的位。如果空间不足,您仍然需要代码来扩展交换,但无论如何都是如此。交换保持其有史以来最大的大小(如果要减小它,只需删除 Redis 外部的文件,以便在运行时重新创建它,或者可以选择让 Redis 在启动时重新创建交换文件是否存在)。 哦,知道了,这也确实可行。交换文件根本不需要任何初始化,因为页面表是在内存中获取的。所以实际上 Redis 可以检查文件是否存在。当前的方法也很糟糕,因为它在 /tmp 中创建交换文件。用户希望在有 SSD 等快速驱动器的地方创建它。所以......实际上“命名”交换文件更好。 vm交换文件<...>。谢谢你的好主意!【参考方案3】:

您是否为您的文件关闭了文件缓存?即 fcntl(fd, F_GLOBAL_NOCACHE, 1)

【讨论】:

不,当系统中有空闲内存时,操作系统缓存可以缓存文件是个好主意。实际上,合法的用途之一是将 CPU 周期换成内存,因为存储在 VM 中的数据要小得多,但访问速度较慢。所以理论上它应该是一个普通的文件,但如果你认为这可能是我可以实际尝试的问题。我会报告我的发现。感谢您的回答。【参考方案4】:

您是否尝试过使用 DTrace 和/或 Instruments(Apple 的实验性 dtrace 前端)进行调试?

Exploring Leopard with DTrace

Debugging Chrome on OS X

【讨论】:

我尝试使用 dtruss 来查看呼叫,但没有太成功,也没有提示为什么需要这么长时间。可能是操作系统正在做一些阻塞的事情,比如在 ftruncate 之后在磁盘上实现部分文件?我会尝试更多,感谢您的链接和答案。【参考方案5】:

正如 Linus 在 Git 邮件列表中所说:

“我知道 OS X 人很难接受它,但是 OS X 文件系统通常是完全和彻底的废话——甚至比 窗户。”

【讨论】:

有趣,但没有帮助的答案。

以上是关于C 程序在 Mac OS X Snow Leopard 上执行磁盘 I/O 时卡在不间断等待的主要内容,如果未能解决你的问题,请参考以下文章

在 10.6 (Snow Leopard) 之后以编程方式设置 Mac OS X 音量

如何在 Mac OS X Snow Leopard 上启用 mysqli?

Joomla 在 Mac OS X Snow Leopard 上使用默认 Apache

iPhone SDK 4 仅适用于 Mac OS X Snow Leopard?

Mac OS X Snow Leopard 上的 MOD_WSGI 困难

Mac OS X Snow Leopard 的默认 /etc/sysctl.conf 设置?