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