Compare commits
2 Commits
30a9fa9c0c
...
697e67c406
Author | SHA1 | Date | |
---|---|---|---|
697e67c406 | |||
0b5c26b6dc |
311
cpulimit-all.sh
Executable file
311
cpulimit-all.sh
Executable file
|
@ -0,0 +1,311 @@
|
||||||
|
#!/bin/sh
|
||||||
|
set -e
|
||||||
|
|
||||||
|
kill_child_jobs() {
|
||||||
|
# From https://stackoverflow.com/a/23336595
|
||||||
|
# Kills all child processes, not just jobs.
|
||||||
|
pkill -P $$
|
||||||
|
}
|
||||||
|
cleanup() {
|
||||||
|
kill_child_jobs
|
||||||
|
}
|
||||||
|
|
||||||
|
# From https://unix.stackexchange.com/a/240736
|
||||||
|
for sig in INT QUIT HUP TERM; do
|
||||||
|
trap "
|
||||||
|
cleanup
|
||||||
|
trap - $sig EXIT
|
||||||
|
kill -s $sig "'"$$"' "$sig"
|
||||||
|
done
|
||||||
|
trap cleanup EXIT
|
||||||
|
|
||||||
|
|
||||||
|
verbose=y
|
||||||
|
cpulimit_args="--lazy"
|
||||||
|
limit=""
|
||||||
|
pids=""
|
||||||
|
exes=""
|
||||||
|
paths=""
|
||||||
|
max_processes=100
|
||||||
|
max_depth=3
|
||||||
|
watch_interval=2
|
||||||
|
subprocess_watch_interval=0.5
|
||||||
|
while [ $# -gt 0 ]
|
||||||
|
do
|
||||||
|
opt="$1"
|
||||||
|
case "$opt" in
|
||||||
|
*=*)
|
||||||
|
arg="${1#*=}"
|
||||||
|
opt="${1%%=*}"
|
||||||
|
argshift=1
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
if [ "$#" -ge 2 ]
|
||||||
|
then
|
||||||
|
arg="$2"
|
||||||
|
else
|
||||||
|
arg=""
|
||||||
|
fi
|
||||||
|
argshift=2
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
case "$opt" in
|
||||||
|
--max-depth)
|
||||||
|
case $arg in
|
||||||
|
''|*[!0-9]*)
|
||||||
|
echo "Invalid max depth: $arg, must be a non-negative integer."
|
||||||
|
exit 5
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
max_depth=$arg
|
||||||
|
shift $argshift
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
;;
|
||||||
|
--max-processes)
|
||||||
|
case $arg in
|
||||||
|
''|*[!0-9]*)
|
||||||
|
echo "Invalid max processes: $arg, must be a positive integer."
|
||||||
|
exit 5
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
max_processes=$arg
|
||||||
|
shift $argshift
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
;;
|
||||||
|
--watch-interval)
|
||||||
|
watch_interval="$arg"
|
||||||
|
shift $argshift
|
||||||
|
;;
|
||||||
|
--subprocess-watch-interval)
|
||||||
|
subprocess_watch_interval="$arg"
|
||||||
|
shift $argshift
|
||||||
|
;;
|
||||||
|
-p|--pid)
|
||||||
|
if [ -z "$pids" ]
|
||||||
|
then
|
||||||
|
pids="$arg"
|
||||||
|
else
|
||||||
|
pids="$pids
|
||||||
|
$arg"
|
||||||
|
fi
|
||||||
|
shift $argshift
|
||||||
|
;;
|
||||||
|
-e|--exe)
|
||||||
|
if [ -z "$exes" ]
|
||||||
|
then
|
||||||
|
exes="$arg"
|
||||||
|
else
|
||||||
|
exes="$exes
|
||||||
|
$arg"
|
||||||
|
fi
|
||||||
|
shift $argshift
|
||||||
|
;;
|
||||||
|
-P|--path)
|
||||||
|
if [ -z "$paths" ]
|
||||||
|
then
|
||||||
|
paths="$arg"
|
||||||
|
else
|
||||||
|
paths="$paths
|
||||||
|
$arg"
|
||||||
|
fi
|
||||||
|
shift $argshift
|
||||||
|
;;
|
||||||
|
-l|--limit)
|
||||||
|
limit="$arg"
|
||||||
|
cpulimit_args="$cpulimit_args --limit=$arg"
|
||||||
|
shift $argshift
|
||||||
|
;;
|
||||||
|
-c|--cpu)
|
||||||
|
cpulimit_args="$cpulimit_args --cpu=$arg"
|
||||||
|
shift $argshift
|
||||||
|
;;
|
||||||
|
-v|--verbose)
|
||||||
|
cpulimit_args="$cpulimit_args --verbose"
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
-q|--quiet)
|
||||||
|
verbose=""
|
||||||
|
cpulimit_args="$cpulimit_args --quiet"
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
-k|--kill)
|
||||||
|
cpulimit_args="$cpulimit_args --kill"
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
-r|--restore)
|
||||||
|
cpulimit_args="$cpulimit_args --restore"
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
-s|--signal)
|
||||||
|
cpulimit_args="$cpulimit_args --signal=$arg"
|
||||||
|
shift $argshift
|
||||||
|
;;
|
||||||
|
--)
|
||||||
|
shift
|
||||||
|
break
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo "Programming error: unexpected argument \"$1\" (opt=\"$opt\", arg=\"$arg\")"
|
||||||
|
exit 3
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
if [ -z "$limit" ]
|
||||||
|
then
|
||||||
|
echo "Must specify a CPU percentage to limit to (-l/--limit)."
|
||||||
|
exit 4
|
||||||
|
fi
|
||||||
|
if [ -z "$pids" ] && [ -z "$exes" ] && [ -z "$paths" ] && [ "$#" -eq 0 ]
|
||||||
|
then
|
||||||
|
echo "Must specify at least one PID, executable file, path, or command line to limit."
|
||||||
|
exit 5
|
||||||
|
fi
|
||||||
|
|
||||||
|
watched_pids=""
|
||||||
|
limit_pid() {
|
||||||
|
if echo "$watched_pids" | grep --silent -F "$1"
|
||||||
|
then
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
if [ "$(echo "$watched_pids" | wc -l)" -ge "$max_processes" ]
|
||||||
|
then
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -n "$verbose" ]
|
||||||
|
then
|
||||||
|
if [ -n "$3" ]
|
||||||
|
then
|
||||||
|
echo "Limiting $3: "
|
||||||
|
fi
|
||||||
|
echo "cpulimit --pid=$1 $cpulimit_args"
|
||||||
|
fi
|
||||||
|
# shellcheck disable=SC2086 # $cpulimit_args really is the intended args.
|
||||||
|
cpulimit --pid="$1" $cpulimit_args &
|
||||||
|
cpulimit_pid=$!
|
||||||
|
new_watched="$1:$2:$cpulimit_pid"
|
||||||
|
if [ -z "$watched_pids" ]
|
||||||
|
then
|
||||||
|
watched_pids="$new_watched"
|
||||||
|
else
|
||||||
|
watched_pids="$watched_pids
|
||||||
|
$new_watched"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
limit_pids() {
|
||||||
|
pids="$1"
|
||||||
|
depth="$2"
|
||||||
|
while read -r pid
|
||||||
|
do
|
||||||
|
# From https://stackoverflow.com/a/3951175
|
||||||
|
case $pid in
|
||||||
|
''|*[!0-9]*)
|
||||||
|
# PID is not a number
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
limit_pid "$pid" "$depth" "$3"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done <<EOF
|
||||||
|
$pids
|
||||||
|
EOF
|
||||||
|
}
|
||||||
|
|
||||||
|
limit_by_executable() {
|
||||||
|
if [ -n "$exes" ]
|
||||||
|
then
|
||||||
|
while read -r exe
|
||||||
|
do
|
||||||
|
limit_pids "$(pgrep -x "$exe")" 0 "$exe"
|
||||||
|
done <<EOF
|
||||||
|
$exes
|
||||||
|
EOF
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -n "$paths" ]
|
||||||
|
then
|
||||||
|
while read -r path
|
||||||
|
do
|
||||||
|
limit_pids "$(pgrep -xf "$path")" 0 "$path"
|
||||||
|
done <<EOF
|
||||||
|
$paths
|
||||||
|
EOF
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
limit_by_subprocess() {
|
||||||
|
if [ -z "$watched_pids" ]
|
||||||
|
then
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
while read -r watched
|
||||||
|
do
|
||||||
|
depth="$(echo "$watched" | cut -d: -f2)"
|
||||||
|
if [ "$max_depth" -gt "$depth" ]
|
||||||
|
then
|
||||||
|
# Make sure the parent is still alive.
|
||||||
|
if ps -p "$(echo "$watched" | cut -d: -f3)" >/dev/null
|
||||||
|
then
|
||||||
|
ppid="$(echo "$watched" | cut -d: -f1)"
|
||||||
|
limit_pids "$(pgrep -P "$ppid")" "$((depth + 1))" "Child of $ppid"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
done <<EOF
|
||||||
|
$watched_pids
|
||||||
|
EOF
|
||||||
|
}
|
||||||
|
|
||||||
|
clean_dead_cpulimit() {
|
||||||
|
if [ -z "$watched_pids" ]
|
||||||
|
then
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
tmp="$(echo "$watched_pids" | while read -r watched
|
||||||
|
do
|
||||||
|
if ps -p "$(echo "$watched" | cut -d: -f3)" >/dev/null
|
||||||
|
then
|
||||||
|
echo "$watched"
|
||||||
|
fi
|
||||||
|
done)"
|
||||||
|
watched_pids="$tmp"
|
||||||
|
}
|
||||||
|
|
||||||
|
if [ "$#" -gt 0 ]
|
||||||
|
then
|
||||||
|
"$@" &
|
||||||
|
limit_pid "$!" 0 "program run on command line: $*"
|
||||||
|
fi
|
||||||
|
|
||||||
|
limit_pids "$pids" 0
|
||||||
|
while true
|
||||||
|
do
|
||||||
|
clean_dead_cpulimit
|
||||||
|
if [ -z "$exes" ] && [ -z "$paths" ] && [ -z "$watched_pids" ]
|
||||||
|
then
|
||||||
|
# If there's nothing left to wait for, then exit.
|
||||||
|
exit
|
||||||
|
fi
|
||||||
|
limit_by_executable
|
||||||
|
num_watched_before="$(echo "$watched_pids" | wc -l)"
|
||||||
|
limit_by_subprocess
|
||||||
|
num_watched_after="$(echo "$watched_pids" | wc -l)"
|
||||||
|
if [ "$num_watched_before" -eq "$num_watched_after" ]
|
||||||
|
then
|
||||||
|
if [ "$watch_interval" = "0" ]
|
||||||
|
then
|
||||||
|
wait
|
||||||
|
else
|
||||||
|
sleep "$watch_interval"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
sleep "$subprocess_watch_interval"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
Loading…
Reference in New Issue
Block a user