“rm -rf /” 如何能够删除系统中的所有文件?

我没有在Ubuntu上尝试过这个命令(出于显而易见的原因),所以我不确定Ubuntu是否允许执行它。但是它以删除所有文件而闻名。只是出于好奇,当内核和/bin被删除时会发生什么?rm如何维护运行时堆栈?rm如何与文件系统通信并完成删除?它如何与硬件通信?

20rm -rf / 不带 --no-preserve-root 参数是不会删除任何文件的。 - muru
52我第一次接触Linux是为了创建一个Ubuntu虚拟机,然后执行"rm -rf /"命令。我建议你也试试这个。设置过程非常快速,可以确保你的主机安全,并且非常有趣地观察操作系统的各个部分在你眼前崩溃。非常令人满意。 - DJMcMayhem
23这让我想起了我最喜欢的低调错误报告:https://bugzilla.redhat.com/show_bug.cgi?id=1202858 "预期结果:Squid被重新启动。实际结果:机器上的所有文件都被删除了。" - user12753
@muru 我不想去尝试是否真实的;-) - gerrit
10你应该阅读《Unix恢复传奇》(http://www.ee.ryerson.ca/~elf/hack/recovery.html)。只要你仍然登录到shell中,系统就还没有完全崩溃! - 200_success
2@gerrit 我做了。 :) - muru
顺便说一句,它在现代机器上运行得非常非常快 - tedder42
@200_success - 但是如果你完全不知道发生了什么,并且重新启动...就没那么好了。 - SSumner
此外,即使在/bin/bash(或其他)被删除之后,活动的shell仍然存在。 - Hagen von Eitzen
2@muru:虽然在Ubuntu上是正确的,但你所描述的行为**只适用于GNU coreutils rm**,而不是你应该教人们依赖的东西。在更标准的系统上,rm -rf /确实会删除所有内容。 - R.. GitHub STOP HELPING ICE
在UNIX系统中,文件直到最后一个引用(例如文件系统中的名称,打开的文件描述符)消失之前才会被删除。 - FUZxxl
6个回答

不要紧,/bin/rm被删除了也没关系。它只运行一次,到那时所有的内容都已经加载到内存中了,包括发送删除命令到文件系统和磁盘所需的其他所有内容。

侧边栏/更新:根据David Hoelzer的回答(并在评论中提到),硬链接/bin/rm指向的inode会一直保持直到rm完成(因为Linux处于打开状态)但是这个事实是无关紧要的;磁盘的状态根本不重要。

在运行之前,二进制文件被加载到内存中。即使你能手动销毁rm的磁盘数据,也不会影响或停止删除操作的完成(假设你没有以其他方式使磁盘不可用)。

不知道inode或硬链接是什么?这是我找到答案的地方。


无论如何,这也是为什么你可以删除当前内核的软件包而不会导致计算机崩溃的原因。只要安装了不同版本,它就能够启动。
再次强调,这是因为rm命令只被调用一次。以下代码在/bin/rm命令终止后将失败,因为它对每个文件名都调用了一次:
find / -exec rm {} \;

那么,find / -exec rm -rf {} +find / -print0 | xargs -0 rm -rf也很可能会失败,因为它们都有参数限制,这意味着在再次调用之前它们只能删除一定数量的文件。在整个过程中,/bin/rm可能会过期(并且被释放),而其他文件还没有被删除。虽然不能保证,但如果/bin/是最后一个进入的目录,这些方法可能会起作用。

7正如@DavidHoelzer所解释的那样,未链接的文件不需要已经在内存中才能继续工作。内核知道有一个打开的文件句柄,因此它会保留文件数据以满足任何请求(包括页面调入),直到最后一个句柄被关闭。 - nobody
@AndrewMedico 这可能适用于大型的开放二进制文件,但我不认为对于一个60KB的二进制文件会有任何分页。虽然我不知道如何测试这个。 - Oli
但我认为这个可能会成功: find / -exec rm {} \+ - Desty
4@Desty,不,那样是行不通的,只要/bin/rm不足以作为最后一批中的结束标志;-exec ... {} +(反斜杠转义是不必要的)仍然会导致多次执行;不是每个文件一次,而是根据可以适应ARG_MAX的参数数量来进行分批执行。 - Charles Duffy
6@Oli,这不是关于分页的问题;引用计数器是与索引节点(inode)相关联的,而不是与目录项相关联的,而且打开的文件句柄被视为一个引用(就像硬链接一样),防止索引节点被释放。文件大小根本不是一个因素,并且即使没有任何交换空间(因此完全没有分页),这种情况仍然会发生。 - Charles Duffy
2无论您是否有交换空间,分页将用于所有内存映射文件。这包括所有可执行文件和库文件。事实上,缺乏交换空间可能意味着更多的分页操作用于内存映射文件。 - kasperd
1@kasperd,没错 - 我在那一点上确实有所错误。更重要的是 - 当引用计数由于打开的句柄而非零时,文件大小对于inode的释放是无关紧要的。 - Charles Duffy
2@CharlesDuffy 是的,大小确实无关紧要。对文件进行内存映射不会导致文件的任何内容被加载,直到访问为止。并且如果需要,用于加载文件中访问部分的内存可以再次释放,之后如果再次访问,则会从文件中重新加载。因此,只要文件被映射,它确实必须保留在文件系统上,对于一个页面大小的文件和足够大以覆盖整个地址空间的文件,行为方式是相同的。(对于动态链接所需的写时复制映射来说,细节稍微复杂一些。) - kasperd
我觉得我在一些词上有点绊倒("paging"可能不是个好主意),但是为了澄清,我并不反对保留打开文件的inode,因为这是相当容易证明的;我只是认为这不是唯一的原因它只是另一种可能起作用的机制。就像你可以在一个非写时复制文件系统上编辑二进制文件而不会导致崩溃一样,这也不是唯一的原因。二进制镜像被加载到内存中,并且会一直保留在那里,直到运行结束(或者被分页出去)。即使你用空间激光照射其后备磁盘扇区,它仍然会继续运行。 - Oli
文件保持打开状态的事实实际上非常重要,因为并不是在程序运行时就立即加载到内存中。随着访问的进行,会逐渐将文件的一部分加载到内存中。如果内核需要释放内存,它们可以再次被丢弃(注意,它们只是被丢弃,而不是发送到交换空间,因为它们已经存在于源文件中的磁盘上),然后当程序再次需要它们时,会再次从磁盘中读取。 - psusi

我没有在Ubuntu上尝试过这个命令(出于明显的原因),所以我不确定Ubuntu是否允许执行它。
我试过了。`rm -rf / --no-preserve-root` 在直接打开的机器上的root会话中运行,同时我也通过ssh从另一台机器连接,并使用root账户。
结果是你开始收到很多像这样的消息:
`rm: 无法删除 '/...': 操作不允许`
或者:
`rm: 无法删除 '/...': 设备或资源忙`

enter image description here

令人惊讶的是,ssh连接一直保持打开状态,直到操作结束。只有当我关闭连接并尝试重新打开时,才出现了一个错误:

从套接字读取失败:对等方重置了连接

在机器上,还剩下四个目录:
  • /dev。这是存储设备文件的位置。
  • /proc——由内核创建的内存文件系统。
  • /run,用于守护进程的标准化文件系统位置。
  • /sys。这允许您获取有关系统及其组件的信息。
这意味着剩下的不多,也没有什么可做的了。你不能使用ls命令(尽管在使用键时,目录和文件的名称仍然会显示)。你可以在不同的目录中使用cd命令,并且可以使用echo命令,但是像cat这样的命令已经不再可用。
也没有sudo命令可用。 shutdown -h nowreboot也消失了,所以你唯一的选择似乎是手动关闭机器。注销(exit)无效,即使它显示一个漂亮的“注销”文本。
一旦你尝试重新启动机器,你会看到一个漂亮的GRUB错误15,然后,什么都不会发生,此时你可能开始想你的rm命令可能对系统造成了一些破坏。

enter image description here

你也可以做到

不,等等,不要在你的机器上这样做!

相反,你可以运行一个虚拟机(VM)。虚拟机的好处是使实验变得非常容易。由于你正在使用Ubuntu,你可能会对vmbuilder感兴趣。这是一个工具,可以让你在几分钟内部署虚拟机(官方文档声称可以在“大约一分钟”内完成,但实际时间,即使在快速硬件上,也需要大约两三分钟)。

一旦部署结束,你就有了一个可以玩耍的环境。如果你最终将其销毁,没关系:你再次部署该机器,两分钟后你就可以继续。

如果你使用像VMWare这样的软件,你可能还对快照感兴趣(请注意,免费的VMWare Player没有此功能;你必须购买VMware Workstation)。请注意,Hyper-V是免费的并支持快照(但你必须运行Windows)。

快照的好处在于你可以在几毫秒内拍摄一张。回滚到快照需要更长时间,但通常只需几秒钟。这使得实验变得更加容易和快速。
这种实验不仅限于操作系统本身。您可以做各种涉及软件的事情。有一个可疑的应用程序?在虚拟机中测试一下——如果是病毒,它不会造成任何伤害。想要在数据库上测试一个操作,考虑到它可能会影响环境?在虚拟机中进行测试。
如果你在真正的非测试机器上这样做会怎么样呢?
坏事会发生。请注意,rm命令保护你免受自己的伤害:rm -rf / 是行不通的:你需要使用--no-preserve-root选项。然而,如果你真的错误地删除了所有东西,会怎么样呢?
rm命令只是解除文件链接,但数据仍然存在于硬盘上。这使得以后可以恢复数据(这就是为什么当硬盘不再工作时,你不应该随意丢弃带有敏感数据的硬盘)。
这意味着你只需要一台备用电脑和一个硬盘盒,就能够恢复几乎所有的文件。重要的是要避免在硬盘上写入任何内容以进行恢复:你所写入的数据将覆盖未连接的文件。
正如200_success评论中提到的文章所指出的那样,如果你聪明行事,甚至可以不用备用电脑就能够找回机器。如果你只关心数据,我觉得没必要麻烦了——使用备用电脑恢复数据要容易得多。

VirtualBox支持磁盘快照。 - Nathan Osman
1请注意,病毒通常会被设计成能够检测虚拟机,所以我不建议使用这种病毒检测方法。一个小问题:那四个剩余的目录不是真正的目录,对吗?它们实际上并不在硬盘上?运行这个命令后,硬盘上还剩下什么? - raptortech97
2@raptortech97 rm并不能真正从硬盘上擦除文件,它只是将磁盘上的实际数据与文件系统树“解除关联”,标记为可用(以便在正常的计算机使用中可能被覆盖)。所以,如果你执行rm -rf ~,并不是所有的东西都会丢失,只要你迅速采取行动(例如使用extundelete)。你可以把它想象成你邮箱中“已删除”文件夹的更不可靠版本,如果你不等待太久,你可以找回文件,但最终它将被清除。 - Thomas
@raptortech97 另一方面,如果由于某种原因你没有使用 rm 而是使用了 shred,那基本上就是游戏结束了,不过你可能会有时间意识到自己的错误并中止操作,因为 shredding 需要更长的时间。 - Thomas
5被保持的目录很可能是某种形式的挂载点。而继续工作的命令是bash内置命令,而不是独立的二进制文件。所以虽然ls已经不存在了,for i in /*; do echo $i; done应该能够正常工作。而要替代cat,你可以使用类似这样的命令while read i; do echo $i; done < /proc/self/maps - MvG
注意:您仍然可以使用“Alt+SysRq+<按钮>”重新启动系统。请按照以下顺序使用它们:“REISUB”。 - Nazar554
为了更有趣,用零填充MBR。我曾经不小心这样做过一次,花了几个小时从可启动的活动磁盘重新构建分区表。它就像rm命令,但只需要几分之一秒的时间,而且在重启之前没有任何迹象表明你的计算机已经损坏。 - phyrfox

原因是文件命名层(通过ls看到的)实际上只是为了方便你。文件系统驱动程序和内核只关心inode。当通过名称引用文件时,它会立即转换为包含所有元数据的inode,包括权限、磁盘上的数据块、所有者ID、组ID和链接计数。
链接计数才是真正重要的。在UNIX系统上删除文件时,实际的系统调用是unlink。底层发生的是,指向该inode的链接计数(文件命名层中的文件名数目)被减少。当链接计数达到零时,文件系统知道文件已被删除。
当使用rm删除文件时,它还会编辑目录文件(是的,它只是一个包含文件名和inode以及其他一些对于本答案不重要的位的文件)。然而,解除链接才是实际释放磁盘资源的操作。
这导致了一些其他有趣的效果。首先,可能会打开一个链接计数为零的文件。当执行rm -rf /命令删除/bin/rm的入口时,就会发生这种情况。文件是打开的(存在一个文件句柄),但是inode被标记为已删除(链接计数为0)。直到文件句柄关闭,磁盘资源才不会被释放和重用。
另一个有趣的效果是当inode的链接计数大于零,但文件命名层中没有指向它的内容时。从某种意义上说,这是一个非常隐蔽的文件 :). 要访问它,您必须使用低级别的方法通过inode号码而不是名称(因为没有名称)来引用它,或者使用十六进制编辑器编辑目录条目以指向该inode。
第三个有趣的效果是如果将链接计数减少到零,但仍然将目录条目指向该inode。如果您愿意,可以尝试进行实验。然而,很明显,这两种情况都会导致文件系统处于不一致状态。

换个角度看,链接计数并不为零,因为打开一个文件会在/proc中添加一个链接。 - OrangeDog
@OrangeDog,即使卸载了procfs,这种行为仍然存在。 - Charles Duffy
2@OrangeDog Charles Duffy是正确的。/proc中的文件句柄不会修改inode,调整链接计数。 - David Hoelzer
/proc和/sys是当前系统(内核)状态的反映。只有在这些文件和目录中选择的操作才会实际改变系统状态。 - user

之前的回答都很好,但我想澄清一个细节:
`rm` 不仅仅是一个命令,它是一个在 `PATH` 中找到的程序。
因此,当你执行以下操作时会发生以下情况:
- 你以 root 身份调用 `rm -rf /` - 程序 `rm` 的实例被加载到内存中,带有参数 `-rf` 和 `/` - 基于这些参数,程序 `rm` 开始执行操作(遍历挂载的根分区并递归删除对其的引用[抱歉,有点技术性;)]) - 一旦完成,`rm` 程序的实例将被卸载 - 此时,内存中只剩下先前加载的程序(例如,在 Ubuntu 中打开终端时的 bash、桌面环境、内核、驱动程序等) - 如果你尝试调用任何其他命令(在 Linux 中,这意味着它是一个独立的程序),它将失败,因为在 PATH 位置中找不到该程序(而且 PATH 位置也不存在了)。然而,一旦加载,所有已加载的程序仍然可以运行。
只是为了理解它的工作原理,试着在Ubuntu上安装LAMP(在Virtualbox中),一些脚本和PHP opcode缓存,然后调用这个邪恶的命令。令人惊讶的是(如果你足够幸运并且你的opcode缓存没有注意到php文件被删除),你仍然可以通过apache web服务器从外部访问php脚本!
附注:即使以root身份运行,这个邪恶的命令也不会删除“所有东西”,它无法删除一些内核特权进程(位于/proc目录下)和一些出现在系统中的设备(作为文件存在的/dev目录下)。事实上,root并不像我们想象的那样万能,而内核则不同。
另外,作为一个思考,你在删除尝试时仍然会有被另一个进程“锁定”的文件。

在Linux上,当以root身份运行时,你确实可以删除设备节点。但是,是的,你不能从/proc中删除任何内容,因为它是一个只读文件系统。同样适用于/sys。我相信你也不能删除挂载点。 - Brian
@AlexKey 我建议进行编辑,以澄清你所说的“非内核嵌入式命令”是什么意思(或者完全避免使用这个短语)。听起来你是在说有一些命令可以通过shell运行,它们直接在内核中实现,因此无论如何都能正常工作。(但是,正如你可能知道但许多读者可能不知道的那样,并非如此:当你运行像cd这样的命令时,它调用的是该名称的shell内置命令--该命令是内置于shell而不是内核中的。)你是指Alt+SysRq "命令"吗? - Eliah Kagan
@Brian 这取决于发行版吗?我在各种不同的发行版上工作过,有趣的是,我犯了这个错误多次。回想起来,在检查了 / 目录的残留物后,我发现还有一些东西在 /dev 目录下,但可能是像光驱或软盘之类的设备... - Alexey Kamenskiy
@EliahKagan,我一直努力保持不依赖特定的发行版,所以我使用了这个术语。它意味着并不是在所有系统中,cli命令都指的是外部程序。但是感谢你指出这一点,我会澄清这个观点。 - Alexey Kamenskiy
@AlexKey 我相信你无法删除 /dev/pts,因为它是一个挂载点。(而且还是一个只读文件系统。) - Brian

一旦硬盘上的所有内容被清除,内核仍然在工作,但由于没有设备、程序、命令等,它会陷入僵局。
操作系统将无法正常工作。
而且奥利所说的是对的,命令会被加载/执行到内存中,除非你终止这个进程(当然,如果kill命令仍然存在的话^^)。

4为什么内核会卡住?MainMa的回答提出了不同的观点,并支持了我所预期的情况。 - MvG
4程序运行在内存中,而不是硬盘上。只有在重新启动之前,内核才会意识到出现了问题。 - phyrfox
也许我需要改变我所使用的词语,内核更或多或少地"卡住"了,没有设备、程序等等,如果你不在根控制台前面,就不能乱来,甚至在那个控制台上也不能乱来。但是我会在回答中修改我的措辞,因为它会产生误导,我同意这一点。 - s1mmel

请注意,如果系统具有selinux并且selinux处于强制执行模式,并且selinux的策略设置正确,则不会发生太多事情。
Selinux是强制访问控制,这意味着,在许多方面中,根用户实际上没有比系统上的任何其他用户更多的破坏系统的能力。
Selinux在内核中执行;您必须破坏内核才能绕过它。
在设计良好的具有良好Selinux策略的系统上,root将无法在系统上做太多事情。
Android的后期版本之所以强制执行Selinux就是为了这个原因。