Skip to content

Instantly share code, notes, and snippets.

@hypersoft
Last active May 27, 2020 08:12
Show Gist options
  • Save hypersoft/b8d45379c43b0d0e2fa0c7bfc7f57669 to your computer and use it in GitHub Desktop.
Save hypersoft/b8d45379c43b0d0e2fa0c7bfc7f57669 to your computer and use it in GitHub Desktop.
User-Level: Process IO, CPU and Memory throttling using systemd-run, in a convenient shell script.
#!/usr/bin/bash
function throttle.help() { cat<<EOF
throttle [OPTIONS] COMMAND
OPTIONS:
-- Stop options processing and execute COMMAND using systemd-run.
-c or --cpu PERCENTAGE Limit the process and children to the percentage
set by the following percentage. DO NOT supply
the percentage-sign with this option. All child
process will be limited to the percentage
specified for every cpu on the system.
Note that this is an integer value, for example:
throttle --cpu 89 ...
89 in the above example, means: 89% of each cpu.
-m or --memory BYTES Limit the process and children's memory usage to
the amount of BYTES specified as K, M, G, or T.
For example: throttle -m 248M ...
248M in the above example, means 248 megabytes.
-b or --block-io BYTES Limit the process and children's io bandwidth to
the amount of BYTES specified as K, M, G, or T.
This option will configure bandwidth limiting on
all block devices.
For example: throttle -b 480M ...
480M in the above example, means 480 megabytes
per-second.
-h or --help Show this help screen.
GUI OPTIONS
Fire up a zenity dialog, and request a feature setting from the user:
--request-memory, --request-cpu and --request-block-io
You will be prompted for your password to configure the systemd-units,
depending on your system configuration. If your system uses polkit,
you can typically find the configuration in:
/usr/share/polkit-1/actions/org.freedesktop.systemd1.policy
(C) 2018; Hypersoft-Systems: U.-S.-A.
EOF
}
while [[ ${1:0:1} == - ]]; do
[[ "$1" == -- ]] && {
shift; break;
}
[[ "$1" =~ ^-(-cpu|c) ]] && {
UPROC="$2"; shift 2; continue;
}
[[ "$1" == --request-cpu ]] && {
UPROC=`zenity --title "Set CPU Limit for process" --entry --text "How much should CPU usage (0-100) be limited by?" --entry-text "33" || echo quit`;
[[ $UPROC == quit ]] && exit 1;
shift; continue
}
[[ "$1" =~ ^-(-memory|m) ]] && {
UMEM="$2"; shift 2; continue;
}
[[ "$1" == --request-memory ]] && {
UMEM=`zenity --title "Set memory Limit for process" --entry --text "How many bytes should memory consumption be limited by?" --entry-text "480M" || echo quit`;
[[ $UMEM == quit ]] && exit 1;
shift; continue
}
[[ "$1" =~ ^-(-block-io|b) ]] && {
UBLKIO="$2"; shift 2; continue;
}
[[ "$1" == --request-block-io ]] && {
UBLKIO=`zenity --title "Set Block IO Limit for process" --entry --text "How many bytes-per-second should block-io be limited by?" --entry-text "480M" || echo quit`;
[[ $UBLKIO == quit ]] && exit 1;
shift; continue
}
[[ "$1" =~ ^-(-help|h) ]] && {
throttle.help; shift; exit 1;
}
# unknown argument:
break;
done;
# takes a bandwidth-measurement suffixed with: K, M, G, or T, the specified bandwidth is parsed as Kilobytes, Megabytes, Gigabytes, or Terabytes, respectively, to the base of 1000.
[[ -n $UBLKIO ]] && {
USER_BLOCK_SETTING=`ls /dev/disk/by-uuid/* | while read line; do echo "-p IOAccounting=true -p IOWriteBandwidthMax='$line $UBLKIO'"; done`
};
# takes a percentage without the percent-sign. percentage-value applies to each processor-core.
[[ -n $UPROC ]] && {
CORES=`grep -c ^processor /proc/cpuinfo`;
USER_CPU_SETTING="-p CPUAccounting=true -p CPUQuota="`echo $CORES'*'$UPROC | bc`"%"
};
# takes a memory-measurement suffixed with: K, M, G, or T, the specified memory is parsed as Kilobytes, Megabytes, Gigabytes, or Terabytes, respectively, to the base of 1000.
[[ -n $UMEM ]] && {
USER_MEMORY_SETTING="-p MemoryAccounting=true -p MemoryHigh=$UMEM";
};
# this command is evaluated becuase BLKIO creates parameters with embedded spaces.
eval systemd-run -G --uid=$UID $USER_BLOCK_SETTING $USER_MEMORY_SETTING $USER_CPU_SETTING --scope "$@";
exit $?;
@hypersoft
Copy link
Author

I use this script to throttle firefox and Android Studio. I call it "jail". These apps are ALWAYS MISBEHAVING. They are my bitches now. Hopefully they stay that way. I generally use desktop: launchers/shortcuts to build "the prison" for those who are found guilty as charged.

Works great.

I am currently using this policy kit configuration:

  <action id="org.freedesktop.systemd1.manage-units">
    <description>Manage system services or other units</description>
...
    <message>Authentication is required to manage system services or other units.</message>
...
    <defaults>
      <allow_any>auth_admin</allow_any>
      <allow_inactive>auth_admin</allow_inactive>
      <allow_active>yes</allow_active>
    </defaults>
  </action>

There is likely a better way to handle this, documentation is so sparse on the topic, i went with the first-gun-smoking.

@hypersoft
Copy link
Author

By the way, my system is running fairly snappy and happy! I do have to say this because its a fact: These software developers who make these misbehaving apps are morons.

I run android-studio at 60% cpu on an intel Intel(R) Pentium(R) CPU G620 @ 2.60GHz with 768MB of RAM-LIMIT, 480M BLKIO (usb 2.0 speed) and it runs faster than it does when you let it go all the way: frequently locking up the whole system.

For firefox 128M RAM, 30% CPU.

SHIT HAS NEVER BEEN SO FUCKING BREEZY.

@hypersoft
Copy link
Author

Final rant: they guy(s) who wrote systemd-run are morons for asking me to enter MY password to set process limits on a user-process. Also, option --no-ask-password is VOID. Another round of moron-mania... And the guys at ArchLinux are morons, because for whatever reason, I can't use the --user option. sudo throttle DELINQUENT, doesn't work either. "Can't find display" and all that non-sense. I think you have to write a service and a helper script to pipe commands you want to launch. Anyway, too much BS for control of my own processes: FROM THE BEGINNING.

@tbarbette
Copy link

tbarbette commented Aug 21, 2019

Why is throttle.sh -b 1024 dd if=/dev/urandom of=/tmp/BIN bs=1M count=1 conv=fdatasync not working?

PS : I share your angriness

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment