如何使 fork() 失败?

如何使 fork() 失败?

对于我正在编写的脚本来说,正确处理失败非常重要fork()(请参阅https://rachelbythebay.com/w/2014/08/19/fork/寻找为什么)。

但是,如何在不使我的系统因进程而崩溃的情况下,可靠地使fork()测试我的错误例程失败?

我尝试了这样的事情:

#!/bin/bash

set -x

whoami

number_of_processes=$(ps auxf | wc -l)
number_of_processes_plus=$((number_of_processes+480))

echo "found $number_of_processes processes, setting the limit to $number_of_processes_plus"

ulimit -u $number_of_processes_plus

perl -e '
use strict;
use warnings;

my $pid = fork();
my $errno = $!;

print "ERRNO: $errno\n";

if($pid == -1) {
        warn "PID was -1!!! errno: >>>$errno<<<\n";
} else {
        if(defined $pid) {
                if($pid == 0) {
                        warn "Child-process\n";
                } else {
                        warn "Parent-process\n";
                }
        } else {
                warn "\$pid NOT DEFINED";
        }
}
'

从我认为对此的理解来看,这应该获取进程数(通过ps auxf | wc -l)并设置ulimit -u为该数 + 480。480 只是通过反复尝试直到 perl 进程启动而得出的。如果我将其设置为低于 480,则只会得到

forkfail.sh: fork: retry: Die Ressource ist zur Zeit nicht verfügbar
forkfail.sh: fork: retry: Die Ressource ist zur Zeit nicht verfügbar
forkfail.sh: fork: retry: Die Ressource ist zur Zeit nicht verfügbar
...

(资源不可用。)

如果我将其设置为远高于 +480,它总是可以工作。如果设置为 480,它有时可以工作,有时则不行。但我似乎无法让它可靠地失败。

如何做到这一点而不产生系统允许的那么多进程,从而导致系统无法使用?

答案1

systemd

在许多 Linux 系统上,您可以使用基于 cgroup通过 systemd 的服务属性进行任务限制TasksMax=,可以精确针对一个特定的命令:

$ systemd-run --user --collect --pty -p TasksMax=1 /bin/sh
sh-5.1$ ls
sh:fork:重试:资源暂时不可用

限制

ulimit -u仅统计由你的 UID,因此a中的标志ps auxf没有多大意义。但除此之外,在 Linux 上,它实际上不计算进程,而是计算任务– 可以是独立进程,也可以是线程。

$ man 2 setrlimit

限制NPROC
              这是对现存进程数量的限制(或者更多
              在 Linux 上,线程)是调用者的真实用户 ID
              过程。只要当前属于
              该进程的真实用户 ID 大于或等于此
              限制,fork(2)失败并出现错误 EAGAIN。

(随着 Linux NPTL 实现线程的方式,它们已经成为主要的对象类型——进程只是“线程组领导者”,而 PID 实际上是“线程组 ID”。)

现在,your_processes + your_threads(正确计数)和your_processes + other_uid_processes(您正在使用的计数)之间的差异恰好约为 480。

ps包含线程,请使用H选项。(或者-L选项。或者-T选项。)

n=$(ps uxH | wc -l)
n=$((n - 4))
   # Discount the $() subshell process; the 'ps' process;
   # the 'wc' process; and the ps header line, for a total of 4. Usually.
ulimit -n $n

或者,由于限制是基于 UID 的,如果您习惯在专用 UI​​D 下启动测试脚本(该 UID 尚未运行其他进程),则更可靠sudo。您甚至可以使用包装器调用 setuid() 为每次运行随机选择一个新的 UID(从与实际帐户不对应的范围内)。

这样做可以避免竞争条件,即在您的调用和实际测试脚本之间任务数量发生显着变化ps(例如,由于后台 cronjobs 启动/完成,或者由于浏览器唤醒某些选项卡并暂停其他选项卡)。

$ sudo -u nobody sh -c "(ulimit -u 4; ps xH)"
    PID TTY      STAT   TIME COMMAND
1686939 pts/1    S+     0:00 sh -c (ulimit -u 4; ps xH)
1686940 pts/1    S+     0:00 sh -c (ulimit -u 4; ps xH)
1686941 pts/1    R+     0:00 ps xH

$ sudo -u nobody sh -c "(ulimit -u 3; ps xH)"
sh: fork: retry: Resource temporarily unavailable

相关内容