更新:这不再是一个问题,而是更多的摘要。哦,好吧......
bash,dash和zsh都带有内置命令ulimit
。每个都有一个选项-t
,它接受一个数字作为参数,被理解为进程可能消耗的CPU时间(以秒为单位)。此后,他们将收到一个信号。很清楚。
但有很多事情尚不清楚。我发现其中有些意外。特别是,您获得的行为取决于shell和底层操作系统。我创建了一个表格,总结了可变性的程度。我还包括用于自动获取这些结果的脚本的代码。最后一个测试需要root权限,如果您注释掉test_shell_sudo $shell
,则可以阻止其运行。
| | Darwin/zsh | Darwin/bash | FreeBSD/zsh | FreeBSD/bash | FreeBSD/dash | Linux/zsh | Linux/bash | Linux/dash | | ulimit -t sets | soft limit | both limits | soft limit | both limits | both limits | soft limit | both limits | both limits | | ulimit -t gets | soft limit | soft limit | soft limit | soft limit | soft limit | soft limit | soft limit | soft limit | | Hard limits can be set below the soft limit | yes | no | yes | yes | yes | yes | no | no | | Soft limits can be set above the hard limit | yes | no | yes | no | no | yes | no | no | | Hard limits can be raised without privileges | yes | no | yes | no | no | yes | no | no | | soft signal | SIGXCPU | SIGXCPU | SIGXCPU | SIGXCPU | SIGXCPU | SIGXCPU | SIGXCPU | SIGXCPU | | hard signal | SIGXCPU | SIGXCPU | SIGKILL | SIGKILL | SIGKILL | SIGKILL | SIGKILL | SIGKILL | | Number of SIGXCPUs sent | one | one | one | one | one | multiple | multiple | multiple | | Raising soft beyond hard limit raises it | yes | impossible* | yes | no | no | yes | impossible* | impossible* | * even as root
#!/usr/bin/env bash
get_sigcode() {
/bin/kill -l |
tr '\n[a-z]' ' [A-Z]' |
awk -v name=$1 '
{ for (i=1; i<=NF; ++i) if ($i == name) print i }'
}
create_runner() {
cat > sig.c <<'EOF'
#include <stdlib.h>
#include <stdio.h>
int
main()
{
int runs = 0;
double x = 0.0;
for (;;runs++) {
x += (double)rand() / RAND_MAX;
if (x >= 1e7) {
printf("Took %d iterations to reach 1000.\n", runs);
x = 0.0;
runs = 0;
}
}
return 0;
}
EOF
cc sig.c -o sig
rm -f sig.c
echo Successfully compiled sig.c
}
create_counter() {
cat > sigcnt.c <<'EOF'
#include <stdatomic.h>
#include <signal.h>
#include <stdlib.h>
#include <stdio.h>
#include <time.h>
sig_atomic_t sig_received;
void handle_signal(int signum) {
sig_received = signum;
}
int
main()
{
signal(SIGXCPU, handle_signal);
int sigxcpu_cnt = 0;
time_t start, now;
time(&start);
int runs = 0;
double x = 1;
for (;;) {
if (sig_received == SIGXCPU) {
sigxcpu_cnt++;
sig_received = 0;
}
time(&now);
if (now - start > 5) {
switch (sigxcpu_cnt) {
case 0:
fprintf(stderr, "none\n");
exit(0);
case 1:
fprintf(stderr, "one\n");
exit(0);
default:
fprintf(stderr, "multiple\n");
exit(0);
}
}
// Do something random that eats CPU (sleeping is not an option)
x += (double)rand() / RAND_MAX;
if (x >= 1e7) {
printf("Took %d iterations to reach 1000.\n", runs);
x = 0.0;
runs = 0;
}
}
}
EOF
cc sigcnt.c -o sigcnt
rm -f sigcnt.c
echo Successfully compiled sigcnt.c
}
echo_underscored() {
out1=$1
out2=''
for ((i=0; i < ${#out1}; ++i)); do
out2+='='
done
echo $out1
echo $out2
}
test_shell() {
shell=$1
echo_underscored "Testing shell: $shell"
f() {
$shell -c 'ulimit -St 3; ulimit -t 2; ulimit -Ht; ulimit -St' | tr -d '\n'
}
case `f` in
22)
t_sets='both limits';;
unlimited2)
t_sets='soft limit';;
*)
echo UNEXPECTED;;
esac
echo "ulimit -t sets: ${t_sets}"
f() {
$shell -c 'ulimit -St 3; ulimit -Ht 4; ulimit -St 3; ulimit -t'
}
case `f` in
3)
t_gets='soft limit';;
*)
echo UNEXPECTED;;
esac
echo "ulimit -t gets: ${t_gets}"
f() {
$shell -c 'ulimit -St 2; ulimit -Ht 1' >/dev/null 2>&1 &&
echo yes || echo no
}
ht_can_set_below_soft=`f`
echo "Hard limits can be set below the soft limit: ${ht_can_set_below_soft}"
f() {
$shell -c 'ulimit -St 1; ulimit -Ht 2; ulimit -St 3' >/dev/null 2>&1 &&
echo yes || echo no
}
st_can_set_above_hard=`f`
echo "Soft limits can be set above the hard limit: ${st_can_set_above_hard}"
f() {
$shell -c 'ulimit -St 1; ulimit -Ht 2; ulimit -Ht 3' >/dev/null 2>&1 &&
echo yes || echo no
}
hard_can_be_raised=`f`
echo "Hard limits can be raised without privileges: ${hard_can_be_raised}"
f() {
$shell -c 'ulimit -St 1; ./sig' >/dev/null 2>&1
echo $?
}
case $((`f` - 128)) in
${sigxcpu})
soft_signal=SIGXCPU;;
${sigkill})
soft_signal=SIGKILL;;
*)
echo UNEXPECTED;
esac
echo "soft signal: ${soft_signal}"
f() {
$shell -c 'ulimit -St 1; ulimit -Ht 1; ./sig' >/dev/null 2>&1
echo $?
}
case $((`f` - 128)) in
${sigxcpu})
hard_signal=SIGXCPU;;
${sigkill})
hard_signal=SIGKILL;;
*)
echo UNEXPECTED;;
esac
echo "hard signal: ${hard_signal}"
f() {
$shell -c 'ulimit -St 1; ./sigcnt 2>&1 >/dev/null'
}
sigxcpus_sent=`f`
echo "Number of SIGXCPUs sent: ${sigxcpus_sent}"
}
test_shell_sudo() {
shell=$1
echo_underscored "Testing shell with sudo: $shell"
f() {
sudo $shell -c 'ulimit -St 1; ulimit -Ht 1; ulimit -St 2 && ulimit -Ht' \
2>/dev/null;
}
out=`f`; ret=$?;
if [[ $ret == 0 ]]; then
case $out in
1)
raising_soft_beyond_hard='no';;
2)
raising_soft_beyond_hard='yes';;
*)
echo UNEXPECTED;;
esac
else
raising_soft_beyond_hard='impossible'
fi
echo "Raising soft beyond hard limit raises it: ${raising_soft_beyond_hard}"
}
main() {
echo "Testing on platform: $(uname)"
sigxcpu=$(get_sigcode XCPU)
sigkill=$(get_sigcode KILL)
echo Number of signal SIGXCPU: ${sigxcpu}
echo Number of signal SIGKILL: ${sigkill}
create_runner
create_counter
echo
for shell in zsh bash dash; do
which $shell >/dev/null || continue;
test_shell $shell
echo
done
for shell in zsh bash dash; do
which $shell >/dev/null || continue;
test_shell_sudo $shell
echo
done
}
main
corresponding gist还带有更好的表格。
答案 0 :(得分:2)
首先,这里是ulimits的绝对规则,包括shell在内的所有进程都被限制为:
考虑到这一点:
- 我是否可以再次提前通过ulimit调用设置的限制?
醇>
软限制,是的。硬限制,没有。
bash似乎认为不,而zsh认为是。
Bash默认设置硬限制。 Zsh默认设置软限制。
Zsh记录了这一点,但bash却没有。无论如何,strace
告诉所有人:
$ strace -e setrlimit zsh -c 'ulimit -t 1'
setrlimit(RLIMIT_CPU, {rlim_cur=1, rlim_max=RLIM64_INFINITY}) = 0
$ strace -e setrlimit bash -c 'ulimit -t 1'
setrlimit(RLIMIT_CPU, {rlim_cur=1, rlim_max=1}) = 0
- 我会收到什么信号?
醇>
如果超过软CPU限制,则为will receive a SIGXCPU。之后发生的事情在POSIX中是未定义的。根据其手册页,Linux将每秒重新发送一次SIGXCPU,直到达到硬限制为止,此时你就是SIGKILL&#39。
我有宽限期吗?
您可以通过设置软限制来选择自己的宽限期。
在zsh
上,设置硬限制而不设置软限制将导致限制适用于儿童而不是外壳:
zsh% ulimit -H -t 1
zsh% ( while true; do true; done ) # is a child, soon killed
zsh% while true; do true; done # not a child, never dies
如果您一次设置两个限制,它们将应用于当前shell,如bash
:
zsh% ulimit -SH -t 1
zsh% while true; do true; done # will now die, just like bash
我不知道这背后的理由是什么。