为什么我不能用叉子炸弹使系统崩溃?


54

最近,我一直在挖掘有关GNU / Linux中进程的信息,并且遇到了臭名昭著的fork炸弹:

:(){ : | :& }; :

从理论上讲,它应该无限地自我复制,直到系统用尽资源为止。

但是,我尝试在CLI DebianGUI Mint发行版上进行测试,并且似乎对系统影响不大。是的,这里创建了无数的进程,过了一会儿我读到控制台消息,例如:

bash:fork:资源暂时不可用

bash:fork:重试:没有子进程

但是一段时间后,所有进程都被杀死,一切恢复正常。我读过ulimit设置了每个用户的最大进程数,但是我似乎无法真正提高它。

对叉弹有哪些系统保护措施?它为什么不复制自身,直到一切冻结或至少滞后很多?有没有办法用叉子炸弹使系统真正崩溃?


2
您目前设定的最高PID是多少?
dsstorefile1

5
请注意,您不会使用前叉炸弹“使系统崩溃”……正如您所说的那样,您将耗尽资源并且无法生成新进程,但是系统不应崩溃
Josh

2
如果您:(){ :& :; }; :改为跑步会怎样?他们也都最终被杀死吗?那:(){ while :& do :& done; }; :
mtraceur

您的这个奇妙问题使我重新考虑了以前的“休假”投票。但是,“ I”在英语中总是大写,请不要再写烂了。
user259412

Answers:


86

您可能有一个使用systemd的Linux发行版。

Systemd为每个用户创建一个cgroup,一个用户的所有进程都属于同一个cgroup。

Cgroups是一种Linux机制,用于设置对系统资源的限制,例如最大进程数,CPU周期,RAM使用率等。这是一个不同的,更现代的资源限制层,而不是ulimit(使用getrlimit()syscall)。

如果运行systemctl status user-<uid>.slice(代表用户的cgroup),则可以看到该cgroup中允许的当前和最大任务数(进程和线程)。

$ systemctl状态用户-$ UID.slice
●user-22001.slice-UID 22001用户切片
   已加载:已加载
  插入:/usr/lib/systemd/system/user-.slice.d
           └─10-defaults.conf
   活动:活动自星期一2018-09-10 17:36:35 EEST; 1周3天前
    任务:17(限制:10267)
   记忆体:616.7M

默认情况下,systemd允许每个用户执行的最大任务数是“系统范围内的最大值”(sysctl kernel.threads-max)的33%;这通常约有10,000个任务。如果要更改此限制:

  • 在systemd v239和更高版本中,用户默认值是通过TasksMax =设置的:

    /usr/lib/systemd/system/user-.slice.d/10-defaults.conf
    

    要调整特定用户的限制(将立即应用并存储在/etc/systemd/system.control中),请运行:

    systemctl [--runtime] set-property user-<uid>.slice TasksMax=<value>
    

    systemctl edit也可以在此处使用覆盖设备设置的常规机制(例如),但是它们将需要重新启动。例如,如果您想更改每个用户的限制,则可以创建/etc/systemd/system/user-.slice.d/15-limits.conf

  • 在systemd V238和前面,用户默认经由设置UserTasksMax =/etc/systemd/logind.conf。更改值通常需要重新启动。

有关此的更多信息:


5
12288个进程(减去炸弹之前已经产生的进程)除了尝试创建一个新进程外什么也不做,实际上并不会影响现代系统。
桅杆

13

无论如何,这不会使现代Linux系统崩溃。

它会创建大量的进程,但是在进程空闲时并不会真正消耗太多的CPU。现在,RAM用完之前,进程表中的插槽用完了。

如果您不是Hkoof所指出的cgroup限制,则以下更改仍会使系统崩溃:

:(){ : | :& : | :& }; :

5
这实际上取决于您认为使系统崩溃的原因。在大多数情况下,进程表中的插槽用完将使系统屈服,即使它并没有完全引起内核恐慌。
奥斯汀·海默加恩

4
@AustinHemmelgarn:这就是为什么明智的系统为root保留最后4个左右进程ID的原因。
约书亚

2
为什么流程会“闲置”?每个分叉的进程都可以无限循环地创建更多进程。因此,它会花费大量时间(fork一遍又一遍)的系统调用开销,而其余​​时间则用于执行函数调用(大概会在shell的调用堆栈中为每个调用使用更多的内存)。
mtraceur

4
@mtraceur:仅在分叉开始失败时才会发生。
约书亚

1
哦,我拿回去了。我在脑海中模拟了一个稍微不同的前叉炸弹实现的逻辑(像这样:(){ :& :; }; ::),而不是问题中的那个。实际上,我还没有完全考虑过所给出的原型流程的执行流程。
mtraceur

9

上世纪90年代,我无意间对自己释放了其中之一。我无意间在其中包含fork()命令的C源文件中设置了执行位。当我双击它时,csh尝试运行它,而不是在想要的编辑器中打开它。

即使那样,它也不会使系统崩溃。Unix足够健壮,因此您的帐户和/或操作系统将具有进程限制。相反,发生的事情是它变得非常迟钝,任何需要启动流程的操作都可能失败。

幕后情况是进程表中充满了试图创建新进程的进程。如果其中一个进程终止(由于进程表已满而导致派生错误,或者由于拼命的操作员试图恢复其系统的完整性),其他进程之一将愉快地派生一个新进程来填充虚空。

“叉式炸弹”基本上是一个无意识的自我修复系统,该系统在执行任务时会保持您的过程表已满。阻止它的唯一方法是立即杀死他们。


1
立即杀死所有对象比您想象的要容易-首先将它们全部停止。
Score_Under

2
@Score_Under-如果我不立即赶往离我最近的哈里斯·夜鹰(Harris Nighthawk),看看是否能解决那里的问题,希望您能原谅我。我在想让PID在故障叉死掉之前发送一个信号并将其发送出去,这可能是一个挑战,但是我必须尝试一下。
TED

@TED kill -9 -1可能是您的朋友(与运行fork炸弹的用户相同;并非与root用户在一起)。
安德烈亚斯·克雷

@AndreasKrey-这个标志看起来并不熟悉,所以我怀疑我的90年代夜鹰有这个标志。
TED

1
@TED:-1不是一个标志。kill仅使用一个选项,然后停止解析选项。这将杀死进程ID -1,它是所有进程的别名。
约书亚
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.