Cgroup 意外地将 SIGSTOP 传播给父级
Cgroup unexpectedly propagates SIGSTOP to the parent
我有一个小脚本 运行 cgroup 中的一个命令,它限制 CPU 时间:
$ cat cgrun.sh
#!/bin/bash
if [[ $# -lt 1 ]]; then
echo "Usage: [=10=] <bin>"
exit 1
fi
sudo cgcreate -g cpu:/cpulimit
sudo cgset -r cpu.cfs_period_us=1000000 cpulimit
sudo cgset -r cpu.cfs_quota_us=100000 cpulimit
sudo cgexec -g cpu:cpulimit sudo -u $USER "$@"
sudo cgdelete cpu:/cpulimit
我让命令运行:./cgrun.sh /bin/sleep 10
然后我从另一个终端向睡眠命令发送 SIGSTOP。此时父命令 sudo
和 cgexec
不知何故也收到了这个信号。然后,我将 SIGCONT 发送到睡眠命令,它允许睡眠继续。
但是此时sudo
和cgexec
都停止了,再也没有收割sleep进程的僵尸了。我不明白这怎么会发生?我该如何预防呢?此外,我无法将 SIGCONT 发送到 sudo
和 cgexec
,因为我正在从用户发送信号,而这些命令 运行 作为 root.
这是它在 htop 中的样子(省略了一些列):
PID USER S CPU% MEM% TIME+ Command
1222869 user S 0.0 0.0 0:00.00 │ │ └─ /bin/bash ./cgrun.sh /bin/sleep 10
1222882 root T 0.0 0.0 0:00.00 │ │ └─ sudo cgexec -g cpu:cpulimit sudo -u user /bin/sleep 10
1222884 root T 0.0 0.0 0:00.00 │ │ └─ sudo -u desertfox /bin/sleep 10
1222887 user Z 0.0 0.0 0:00.00 │ │ └─ /bin/sleep 10
如何以 SIGSTOP 不反弹到父进程的方式创建 cgroup?
UPD
如果我使用 systemd-运行 启动进程,我不会观察到相同的行为:
sudo systemd-run --uid=$USER -t -p CPUQuota=10% sleep 10
我不会使用“cg 工具”,而是使用 shell 命令以“困难的方式”来创建 cpulimit cgroup(这是一个mkdir
),设置cfs参数(在对应的cpu.cfs_*文件中用echo
命令),创建一个sub-shell (...)
符号,将其移动到cgroup中(其pid的echo
命令进入cgroup的tasks
文件中)并在该子shell中执行请求的命令。
因此,cgrun.sh
看起来像这样:
#!/bin/bash
if [[ $# -lt 1 ]]; then
echo "Usage: [=10=] <bin>" >&2
exit 1
fi
CGTREE=/sys/fs/cgroup/cpu
sudo -s <<EOF
[ ! -d ${CGTREE}/cpulimit ] && mkdir ${CGTREE}/cpulimit
echo 1000000 > ${CGTREE}/cpulimit/cpu.cfs_period_us
echo 100000 > ${CGTREE}/cpulimit/cpu.cfs_quota_us
EOF
# Sub-shell in background
(
# Pid of the current sub-shell
# ($$ would return the pid of the father process)
MY_PID=$BASHPID
# Move current process into the cgroup
sudo sh -c "echo ${MY_PID} > ${CGTREE}/cpulimit/tasks"
# Run the command with calling user id (it inherits the cgroup)
exec "$@"
) &
# Wait for the sub-shell
wait $!
# Exit code of the sub-shell
rc=$?
# Delete the cgroup
sudo rmdir ${CGTREE}/cpulimit
# Exit with the return code of the sub-shell
exit $rc
运行它(之前我们获取当前shell的pid在另一个终端显示进程层级):
$ echo $$
112588
$ ./cgrun.sh /bin/sleep 50
这将创建以下流程层次结构:
$ pstree -p 112588
bash(112588)-+-cgrun.sh(113079)---sleep(113086)
停止 sleep
进程:
$ kill -STOP 113086
查看cgroup以验证sleep
命令被运行放入其中(它的pid在tasks
文件中)并且CFS参数设置正确:
$ ls -l /sys/fs/cgroup/cpu/cpulimit/
total 0
-rw-r--r-- 1 root root 0 nov. 5 22:38 cgroup.clone_children
-rw-r--r-- 1 root root 0 nov. 5 22:38 cgroup.procs
-rw-r--r-- 1 root root 0 nov. 5 22:36 cpu.cfs_period_us
-rw-r--r-- 1 root root 0 nov. 5 22:36 cpu.cfs_quota_us
-rw-r--r-- 1 root root 0 nov. 5 22:38 cpu.shares
-r--r--r-- 1 root root 0 nov. 5 22:38 cpu.stat
-rw-r--r-- 1 root root 0 nov. 5 22:38 cpu.uclamp.max
-rw-r--r-- 1 root root 0 nov. 5 22:38 cpu.uclamp.min
-r--r--r-- 1 root root 0 nov. 5 22:38 cpuacct.stat
-rw-r--r-- 1 root root 0 nov. 5 22:38 cpuacct.usage
-r--r--r-- 1 root root 0 nov. 5 22:38 cpuacct.usage_all
-r--r--r-- 1 root root 0 nov. 5 22:38 cpuacct.usage_percpu
-r--r--r-- 1 root root 0 nov. 5 22:38 cpuacct.usage_percpu_sys
-r--r--r-- 1 root root 0 nov. 5 22:38 cpuacct.usage_percpu_user
-r--r--r-- 1 root root 0 nov. 5 22:38 cpuacct.usage_sys
-r--r--r-- 1 root root 0 nov. 5 22:38 cpuacct.usage_user
-rw-r--r-- 1 root root 0 nov. 5 22:38 notify_on_release
-rw-r--r-- 1 root root 0 nov. 5 22:36 tasks
$ cat /sys/fs/cgroup/cpu/cpulimit/tasks
113086 # This is the pid of sleep
$ cat /sys/fs/cgroup/cpu/cpulimit/cpu.cfs_*
1000000
100000
向sleep
进程发送SIGCONT信号:
$ kill -CONT 113086
进程结束,cgroup被销毁:
$ ls -l /sys/fs/cgroup/cpu/cpulimit
ls: cannot access '/sys/fs/cgroup/cpu/cpulimit': No such file or directory
获取脚本完成后的退出代码(即启动命令的退出代码):
$ echo $?
0
我有一个小脚本 运行 cgroup 中的一个命令,它限制 CPU 时间:
$ cat cgrun.sh
#!/bin/bash
if [[ $# -lt 1 ]]; then
echo "Usage: [=10=] <bin>"
exit 1
fi
sudo cgcreate -g cpu:/cpulimit
sudo cgset -r cpu.cfs_period_us=1000000 cpulimit
sudo cgset -r cpu.cfs_quota_us=100000 cpulimit
sudo cgexec -g cpu:cpulimit sudo -u $USER "$@"
sudo cgdelete cpu:/cpulimit
我让命令运行:./cgrun.sh /bin/sleep 10
然后我从另一个终端向睡眠命令发送 SIGSTOP。此时父命令 sudo
和 cgexec
不知何故也收到了这个信号。然后,我将 SIGCONT 发送到睡眠命令,它允许睡眠继续。
但是此时sudo
和cgexec
都停止了,再也没有收割sleep进程的僵尸了。我不明白这怎么会发生?我该如何预防呢?此外,我无法将 SIGCONT 发送到 sudo
和 cgexec
,因为我正在从用户发送信号,而这些命令 运行 作为 root.
这是它在 htop 中的样子(省略了一些列):
PID USER S CPU% MEM% TIME+ Command
1222869 user S 0.0 0.0 0:00.00 │ │ └─ /bin/bash ./cgrun.sh /bin/sleep 10
1222882 root T 0.0 0.0 0:00.00 │ │ └─ sudo cgexec -g cpu:cpulimit sudo -u user /bin/sleep 10
1222884 root T 0.0 0.0 0:00.00 │ │ └─ sudo -u desertfox /bin/sleep 10
1222887 user Z 0.0 0.0 0:00.00 │ │ └─ /bin/sleep 10
如何以 SIGSTOP 不反弹到父进程的方式创建 cgroup?
UPD
如果我使用 systemd-运行 启动进程,我不会观察到相同的行为:
sudo systemd-run --uid=$USER -t -p CPUQuota=10% sleep 10
我不会使用“cg 工具”,而是使用 shell 命令以“困难的方式”来创建 cpulimit cgroup(这是一个mkdir
),设置cfs参数(在对应的cpu.cfs_*文件中用echo
命令),创建一个sub-shell (...)
符号,将其移动到cgroup中(其pid的echo
命令进入cgroup的tasks
文件中)并在该子shell中执行请求的命令。
因此,cgrun.sh
看起来像这样:
#!/bin/bash
if [[ $# -lt 1 ]]; then
echo "Usage: [=10=] <bin>" >&2
exit 1
fi
CGTREE=/sys/fs/cgroup/cpu
sudo -s <<EOF
[ ! -d ${CGTREE}/cpulimit ] && mkdir ${CGTREE}/cpulimit
echo 1000000 > ${CGTREE}/cpulimit/cpu.cfs_period_us
echo 100000 > ${CGTREE}/cpulimit/cpu.cfs_quota_us
EOF
# Sub-shell in background
(
# Pid of the current sub-shell
# ($$ would return the pid of the father process)
MY_PID=$BASHPID
# Move current process into the cgroup
sudo sh -c "echo ${MY_PID} > ${CGTREE}/cpulimit/tasks"
# Run the command with calling user id (it inherits the cgroup)
exec "$@"
) &
# Wait for the sub-shell
wait $!
# Exit code of the sub-shell
rc=$?
# Delete the cgroup
sudo rmdir ${CGTREE}/cpulimit
# Exit with the return code of the sub-shell
exit $rc
运行它(之前我们获取当前shell的pid在另一个终端显示进程层级):
$ echo $$
112588
$ ./cgrun.sh /bin/sleep 50
这将创建以下流程层次结构:
$ pstree -p 112588
bash(112588)-+-cgrun.sh(113079)---sleep(113086)
停止 sleep
进程:
$ kill -STOP 113086
查看cgroup以验证sleep
命令被运行放入其中(它的pid在tasks
文件中)并且CFS参数设置正确:
$ ls -l /sys/fs/cgroup/cpu/cpulimit/
total 0
-rw-r--r-- 1 root root 0 nov. 5 22:38 cgroup.clone_children
-rw-r--r-- 1 root root 0 nov. 5 22:38 cgroup.procs
-rw-r--r-- 1 root root 0 nov. 5 22:36 cpu.cfs_period_us
-rw-r--r-- 1 root root 0 nov. 5 22:36 cpu.cfs_quota_us
-rw-r--r-- 1 root root 0 nov. 5 22:38 cpu.shares
-r--r--r-- 1 root root 0 nov. 5 22:38 cpu.stat
-rw-r--r-- 1 root root 0 nov. 5 22:38 cpu.uclamp.max
-rw-r--r-- 1 root root 0 nov. 5 22:38 cpu.uclamp.min
-r--r--r-- 1 root root 0 nov. 5 22:38 cpuacct.stat
-rw-r--r-- 1 root root 0 nov. 5 22:38 cpuacct.usage
-r--r--r-- 1 root root 0 nov. 5 22:38 cpuacct.usage_all
-r--r--r-- 1 root root 0 nov. 5 22:38 cpuacct.usage_percpu
-r--r--r-- 1 root root 0 nov. 5 22:38 cpuacct.usage_percpu_sys
-r--r--r-- 1 root root 0 nov. 5 22:38 cpuacct.usage_percpu_user
-r--r--r-- 1 root root 0 nov. 5 22:38 cpuacct.usage_sys
-r--r--r-- 1 root root 0 nov. 5 22:38 cpuacct.usage_user
-rw-r--r-- 1 root root 0 nov. 5 22:38 notify_on_release
-rw-r--r-- 1 root root 0 nov. 5 22:36 tasks
$ cat /sys/fs/cgroup/cpu/cpulimit/tasks
113086 # This is the pid of sleep
$ cat /sys/fs/cgroup/cpu/cpulimit/cpu.cfs_*
1000000
100000
向sleep
进程发送SIGCONT信号:
$ kill -CONT 113086
进程结束,cgroup被销毁:
$ ls -l /sys/fs/cgroup/cpu/cpulimit
ls: cannot access '/sys/fs/cgroup/cpu/cpulimit': No such file or directory
获取脚本完成后的退出代码(即启动命令的退出代码):
$ echo $?
0