连续旋转的sync()调用如何导致高IO等待?

连续旋转的sync()调用如何导致高IO等待?

据我从sync()库调用中了解到,它允许进程将所有脏缓冲区刷新到磁盘

sync()系统调用的服务例程sys_sync()调用一系列辅助函数:

wakeup_bdflush(0);
sync_inodes(0);
sync_supers( );
sync_filesystems(0);
sync_filesystems(1);
sync_inodes(1);

如果我运行sync()一次调用,之后缓冲区中就不应该有任何内容。

我正在使用一个名为stressthat 的工具,当运行时-i flag 的工具在sync()上产生N个工作线程,

stress --i 1

该命令假设消耗高 IO 等待,因为它不断发送sync()调用,我使用确认它ltrace

sync()                                                                                                               = 0
sync()                                                                                                               = 0
sync()                                                                                                               = 0
sync()                                                                                                               = 0
sync()                                                                                                               = 0
sync()                                                                                                               = 0
sync()                                                                                                               = 0
sync()                                                                                                               = 0
sync()                                                                                                               = 0
sync()                                                                                                               = 0
sync()                                                                                                               = 0
sync()                                                                                                               = 0
sync()                                                                                                               = 0
sync()                                                                                                               = 0
sync()                                                                                                               = 0
sync()                                                                                                               = 0
sync()                                                                                                               = 0
sync()                                                                                                               = 0
sync()                                                                                                               = 0
sync()                                                                                                               = 0
sync()                                                                                                               = 0
sync()                                                                                                               = 0
sync()                                                                                                               = 0
sync()                                                                                                               = 0
sync()                                                                                                               = 0
sync()                                                                                                               = 0
sync()                                                                                                               = 0
sync()                                                                                                               = 0
sync()                                                                                                               = 0
sync()                                                                                                               = 0
sync()                                                                                                               = 0
sync()                                                                                                               = 0
sync()                                                                                                               = 0
sync()                                                                                                               = 0
sync()                                                                                                               = 0
sync()                                                                                                               = 0
sync()                                                                                                               = 0

有人可以解释一下为什么连续同步调用会消耗大量 IO 吗?我假设在第一次同步调用之后缓冲区应该是空的。

答案1

sync() 类似于数据库提交,但针对的是您的磁盘。 (事实上​​,对于事务数据库,提交通常涉及sync()调用来帮助确保ACID......你必须想尽办法来阻止它)而不是通过操作系统的正常性能优化渠道,你告诉操作系统:“立即执行此操作!”对于旋转,机械磁盘的性能完全取决于物理:驱动器需要时间将磁头定位到实际执行请求的操作所需的位置(即使到达那里,它也必须等待磁盘旋转当您不断(甚至偶尔)同步时,您就绕过了很多通过缓冲和调度来优化这项工作的机会。另请注意,当您调用sync()时,您这样做是为了所有挂起的文件系统写入,包括元数据,而不仅仅是单个文件或文件系统。

让我们看一个简化的场景,您有一个程序需要将四个扇区写入磁盘:一个在最里面的磁道(w1),一个在最外面的磁道(w2),一个在磁盘的中间磁道(w3) ),最后回到最里面的轨道(w4)......

如果你的程序写入w1然后调用sync(),你的系统会立即让驱动器执行写入,这会导致驱动器一直移动到最里面的磁道并写入扇区。然后 w2 发生,后面跟着一个sync(),所以现在驱动器必须一直走到外部磁道来写入扇区。接下来发生 w3,驱动器必须移回磁盘中心并写入扇区。最后发生 w4,驱动器必须返回最里面的磁道并写入扇区。当驱动器磁头移动时,CPU 在做什么?字面意义上的等待。

如果您没有进行所有这些sync()调用,操作系统将缓冲所有这些写入并立即返回到程序(“是的,是的......我最终会得到它”),现在它有w1- w4 缓冲。操作系统和驱动器都可以以任何有意义的方式安排请求,而无需强制驱动器独立甚至按顺序写入每个扇区。因此,假设操作系统决定在 w4 进入后执行写入操作,它将向驱动器发送所有 4 个写入操作,并且驱动器可以看到应该首先执行 w1 和 w4(即最里面的磁道),然后应该执行 w3 (中间轨道),最后是 w2(最外面的轨道)。因此,它只执行 3 次,而不是 4 次单独的查找(以毫秒为单位)。并且每次查找的线性距离更短,因此比在sync() 场景中更快。开始将这种低效率乘以数百倍甚至更多,你就会开始明白为什么当你不断地同步()时你要花费所有的时间等待。闪存驱动器具有类似的调度效率(即对同一块进行多次写入等),但它们的影响程度与机械驱动器不同。

相关内容