最近,一位朋友问我HALT的历史,在运行HALT或重新启动之前,我们必须在SYNC/SYNC/SYNC舞蹈中停止的时间是什么时候。事实证明,这两者是相关的。
如果你浏览一下网络,你会发现有些人会给出这样的建议,比如关机时,输入sync;stop;保证安全。这条建议背后有一些很好的理由,但这些理由并不是马上就清楚的,而且值得一探。在探索之前,我被告知同步舞蹈的原因是V6中的一个驱动程序错误,这个错误在45年前就已经修复了……。但事实证明,告诉我这件事的人一定是弄错了,因为代码讲述了一个不同的故事……。
同步程序调用同步系统调用并退出(现在仍在)。Unix研究版中的同步系统调用的实现方式大致如下:
它将遍历系统中固定的缓冲区列表,写出脏的缓冲区。它使用bwrite()来实现这一点,这是同步的。每次写入都必须在下一次写入开始之前完成。Iupdat将从磁盘读取inode,更新该inode,然后再次同步写出。Bflush使用bwrite写入所有内容,但将缓冲区标记为B_ASYNC,这意味着在这种情况下它不会等待。也没有其他东西在等待了。因此,建议键入sync 3次,每次一行,以便有时间耗尽缓冲区(后续同步将不会在安静的系统上调度新的I/O)。用分号在一行中输入所有这三个字符,这一次没有给出具体的时间。
如果你看一下推荐信,你会发现它实际上相当聪明。每次键入一行,每次等待提示符,第一次会安排大量I/O,然后给操作员几秒钟的无害任务,让他们在采取任何操作之前完成I/O。内核避免了稍后系统在实现等待I/O完成时将面临的各种令人讨厌的死锁。
编辑:传授给我的一个小知识是第一个同步立即返回,但是第二个被阻止了。我在基于BSD或System V的系统中找不到这方面的证据……。尽管随着Unix中并发性的增加,针对同步代码中的多线程的保护量也在增加。
没有一个Research Unix版本需要重新启动系统调用。要重新启动程序,需要用SIGHUP杀死init,这反过来又会杀死其他所有东西,并在单个用户中派生一个新的shell。没有其他方法可以重新启动系统,如果init真的死了,就会发生不好的事情。但是没有干净的重启选项,也没有干净地停止内核的方法(除了电源开关)。
从消息来源来看,有一个小小的暗示,那就是有些事情是计划好的,但从未执行过。所有系统调用都在/usr/include/sys.s中定义。仔细检查会发现以下几点:
这证明我错了,对吧?嗯,也许不是。查看sysen.c,我们可以看到以下内容:
它将nosys&39;列为处理程序,因此没有实现。没有重新启动系统调用。我还会注意到还有另一个断开:系统调用59被列为setinf(不管是什么),但被实现为exe。
我能找到的对reboot(2)的第一个引用是在sysen.c的4.0BSD中,我们看到以下内容;
其中重新启动落在为其分配的插槽中。在/etc/中有一个新命令调用它(通过syscall号,而不是普通的包装器)。它在2.8BSD中没有,但在2.9BSD及更高版本中也以类似的形式出现。3BSD仍有指向nosys的占位符。因为2BSD的进化跟踪了4BSD,所以我不会再说了。
在4.0BSD(1980)中,reboot()只调用与机器相关的boot()例程。它调用update(),如上所述调度写入。它打印出正在等待IO完成。然而,等待的代码基本上是睡眠,所以如果所有的数据没有在5秒内传出,糟糕的事情就会发生。因此,同步停止舞蹈仍然是有用的建议。根据打字员的不同,它会让你的工作进行得很顺利,并轻松地将数据存入磁盘的时间增加一倍。4.1C(1982)将其调整为10s,并使用ifdef&d代码尝试等待所有脏缓冲区清除。
在4.2BSD(1983)中,开始等待写入代码。它尝试遍历系统中的Buf列表多达20次,让缓冲区排空。因此,这里取得了进展。4.3增加40ms*ITER(总计8.4秒)的延迟...。这至少会持续到4.4BSD(1993)……所以,当情况变得更好时,系统也会变得更好,可能会堆积越来越多的I/O。后续的BSD系统也在此基础上进行了改进,包括各种解决方法(主要是当系统变得足够大时,这样在下次同步调用开始之前,update(8)不会在30秒内刷新所有I/O)。
与此同时,在AT&;T方面,System III(1980)什么都没有。系统VR1没有任何内容。所有程序员工作台版本(PWB)均未重新启动(2)。
System Vr2(1984)定义了一个新的uadmin(2)系统调用。它的作用类似于间接系统调用(您向它传递了您希望它作为第一个参数执行的操作)。其中之一A_reboot是从fsck调用的,但是内核并不实现它。
快进到系统VR3(1987),我们找到一个实现。它基本上是调用/filessystem的umount,然后调用重置CPU。其他文件系统在uadmin(A_REBOOT,..)之前作为关闭过程的一部分进行卸载。被调用,因此在被调用时仅/保持挂载状态。这个对umount的调用会刷新脏缓冲区,并干净利落地展开其他所有内容,以便在调用CPU重置时不会出现任何挂起的情况。所以最终同步停止的问题已经解决了。嗯,也许..。没有超时,也没有任何避免死锁的方法。尽管如此,这仍然是一个相当干净的问题解决方案,特别是相对于BSD当时正在做的事情。
即使是从早期泄露的消息来源的可用性。然而,到SunOS达到4.1版本时,有一个vfs_syncall(),MD boot()函数调用它来同步所有内容(它只在所有调度的I/O完成时返回)。我没有检查过这里是否存在停机问题,但重新启动许多Sun的经验证据表明,实际上,如果有任何I/O不知何故被卡住了……或者我足够幸运地没有足够多的机器,在这些机器上开始出现薄片磁盘问题,那就很少有问题了。我找不到Sun源代码的任何早期版本,所以很难知道这个解决方案是什么时候进入树中的(我查看的代码可以追溯到1994年,也就是最初发布11年后)。“我怀疑Sun很早就解决了这个问题,但凭直觉没有证据证明这一点。”
其他UNIX,不仅仅是system V端口,在源代码形式中很难找到,所以我不能从原始来源说出他们是否在system VR3之前解决了这个问题。还有很多4.2BSD和4.3BSD端口都没有存活下来,无法接受检查。
这条一般规则的一个例外是我在位保护程序上找到的Unisoft 1.0内核的副本。它可以追溯到1986年(所以相对较晚)。它有一个重新启动系统调用(编号64,而不是55)。该系统调用调用update(),就像4BSD';s一样,但在调用重置CPU的例程之前,会执行一个较大的for()循环(1到1,000,000)。该内核是一个V7端口,很可能是从某个4BSD发行版中获得了想法(可能还有代码)。这个内核似乎是索尼SUNIX的基础(它似乎是基于第7版的Unix,早于索尼基于4.2BSD的新闻操作系统)。新闻-操作系统的行为很可能像4.2BSD,但由于缺乏消息来源,我不能证实这一点。如果您知道更多关于SUNIX或NEWS-OS的信息,请留言。
Linux的同步调用是同步的。您可以获得与fsync相同的保证。此行为在1995年发布的1.3.20中引入。在此之前,同样的同步舞蹈建议也很有用,因为Linux的早期版本在处理磁盘写入方面比其他当代系统更具侵略性。虽然这有助于它在基准测试中竞争,但当Linux机器开始投入生产时,它造成了数据完整性问题(这是促使这一变化的原因之一)。作为关闭序列的一部分,现代Linux系统会清除所有脏缓冲区,并等待刷新完成,然后再继续重新引导,从而关闭或停止系统。
多年来,我一直被告知3sync舞蹈的原因是由于V6的DEC磁盘驱动程序中的一个驱动程序错误,很久以前就已经修复了。然而,深入研究它会发现,即使在Unix学会重新启动()本身之后,也有很好的理由来跳这种舞。