Controlling the CPU temperature, limiting overheating, using CPUlimit to control services and scripts


Stopping the hotness

My HTPC is unfortunately inside an inclosed cabinet. I have added two holes with fans on the bottom of the cabinet, but it can still get quite warm without some additional controls on the CPU usage. I did the following to get the temperature under control at all times. In my case, and based on my usage, using the powersave CPU frequency governor is ideal. All CPU cores will run at the minimum frequency, which is plenty for mythtv to run well, while keeping the temperatures low.

1. Install linux-tools-generic and cpufrequtils.

Linux-tools-common includes cpupower, which provides useful information and control from the commandline. Cpufrequtils has similar functionality and also easily sets the governor after each reboot.

2. Modify the cpufrequtils init.d script to enable the right governor at boot.

Here are the commands that will do that:

sudo apt-get install linux-tools-generic cpufrequtils
sudo sed -i 's/^GOVERNOR=.*/GOVERNOR="powersave"/' /etc/init.d/cpufrequtils
sudo service cpufrequtils reload

Upon running the last command, you should see the governor changed for each CPU.
Running “cpupower frequency-info” will provide you with details about the current CPU status.

An additional option is to install the “thermald” package that should monitor and try to control the temperature more actively. See http://www.webupd8.org/2014/04/prevent-your-laptop-from-overheating.html for more details.

3. Fix the boot scripts

There are a few more steps to actually get this working upon reboot in Ubuntu 14.04 at least. There is a default service called ondemand that changes the CPU governor from “performance” which is the default during the boot process to “ondemand” for 60 seconds after it is started. The cpufrequtils service sets the governor as well, immediately upon starting. The following commands will fix that up by deactivating the ondemand service and changing the cpufrequtils boot sequence to 99 rather than 19:

sudo update-rc.d -f ondemand remove
sudo update-rc.d -f cpufrequtils remove
sudo update-rc.d cpufrequtils start 99 2 3 4 5 .

Daemon/Service for CPULimit

Based on this thread, I added a init.d script to autoatically apply cpulimit to certain processes.

1. Install cpulimit and gawk packages.

sudo apt-get install cpulimit gawk

2. Create cpulimit daemon script

As root, save the following to /usr/bin/cpulimit_daemon.sh, modifying the variables at the top. Use either the blacklist or whitelist, not both. Also, make sure you don’t put any spaces in variable, for example WHITE_PROCESS_LIST= “mysql|firefox|gedit” (notice space character after = character) is not going to work correctly. You need to specify the variable without spaces: WHITE_PROCESS_LIST=”mysql|firefox|gedit”.

#!/bin/bash
# ==============================================================
# CPU limit daemon - set PID's max. percentage CPU consumptions
# ==============================================================

# Variables
CPU_LIMIT=30       	# Maximum percentage CPU consumption by each PID
DAEMON_INTERVAL=10  	# Daemon check interval in seconds
BLACK_PROCESSES_LIST="avconv|mythcommflag" # Limit only processes defined in this variable. If variable is empty (default) all violating processes are limited.
WHITE_PROCESSES_LIST=   # Limit all processes except processes defined in this variable. If variable is empty (default) all violating processes are limited.

# Check if one of the variables BLACK_PROCESSES_LIST or WHITE_PROCESSES_LIST is defined.
if [[ -n "$BLACK_PROCESSES_LIST" &&  -n "$WHITE_PROCESSES_LIST" ]] ; then    # If both variables are defined then error is produced.
   echo "At least one or both of the variables BLACK_PROCESSES_LIST or WHITE_PROCESSES_LIST must be empty."
   exit 1
elif [[ -n "$BLACK_PROCESSES_LIST" ]] ; then                                 # If this variable is non-empty then set NEW_PIDS_COMMAND variable to bellow command
   NEW_PIDS_COMMAND="top -b -n1 -c | grep -E '$BLACK_PROCESSES_LIST' | gawk '\$9>CPU_LIMIT {print \$1}' CPU_LIMIT=$CPU_LIMIT"
elif [[ -n "$WHITE_PROCESSES_LIST" ]] ; then                                 # If this variable is non-empty then set NEW_PIDS_COMMAND variable to bellow command
   NEW_PIDS_COMMAND="top -b -n1 -c | gawk 'NR>6' | grep -E -v '$WHITE_PROCESSES_LIST' | gawk '\$9>CPU_LIMIT {print \$1}' CPU_LIMIT=$CPU_LIMIT"
else
   NEW_PIDS_COMMAND="top -b -n1 -c | gawk 'NR>6 && \$9>CPU_LIMIT {print \$1}' CPU_LIMIT=$CPU_LIMIT"
fi

# Search and limit violating PIDs
while sleep $DAEMON_INTERVAL
do
   NEW_PIDS=$(eval "$NEW_PIDS_COMMAND")                                                                    # Violating PIDs
   LIMITED_PIDS=$(ps -eo args | gawk '$1=="cpulimit" {print $3}')                                          # Already limited PIDs
   QUEUE_PIDS=$(comm -23 <(echo "$NEW_PIDS" | sort -u) <(echo "$LIMITED_PIDS" | sort -u) | grep -v '^$')   # PIDs in queue

   for i in $QUEUE_PIDS
   do
       cpulimit -p $i -l $CPU_LIMIT -z &   # Limit new violating processes
   done
done

3. Create the init.d script

As root, save the following to /etc/init.d/cpulimitd:

#!/bin/sh
#
# Script to start CPU limit daemon
#
set -e

case "$1" in
start)
if [ $(ps -eo pid,args | gawk '$3=="/usr/bin/cpulimit_daemon.sh"  {print $1}' | wc -l) -eq 0 ]; then
    nohup /usr/bin/cpulimit_daemon.sh >/dev/null 2>&1 &
    ps -eo pid,args | gawk '$3=="/usr/bin/cpulimit_daemon.sh"  {print}' | wc -l | gawk '{ if ($1 == 1) print " * cpulimit daemon started successfully"; else print " * cpulimit daemon can not be started" }'
else
    echo " * cpulimit daemon can't be started, because it is already running"
fi
;;
stop)
CPULIMIT_DAEMON=$(ps -eo pid,args | gawk '$3=="/usr/bin/cpulimit_daemon.sh"  {print $1}' | wc -l)
CPULIMIT_INSTANCE=$(ps -eo pid,args | gawk '$2=="cpulimit" {print $1}' | wc -l)
CPULIMIT_ALL=$((CPULIMIT_DAEMON + CPULIMIT_INSTANCE))
if [ $CPULIMIT_ALL -gt 0 ]; then
    if [ $CPULIMIT_DAEMON -gt 0 ]; then
        ps -eo pid,args | gawk '$3=="/usr/bin/cpulimit_daemon.sh"  {print $1}' | xargs kill -9   # kill cpulimit daemon
    fi

    if [ $CPULIMIT_INSTANCE -gt 0 ]; then
        ps -eo pid,args | gawk '$2=="cpulimit" {print $1}' | xargs kill -9                    # release cpulimited process to normal priority
    fi
    ps -eo pid,args | gawk '$3=="/usr/bin/cpulimit_daemon.sh"  {print}' | wc -l | gawk '{ if ($1 == 1) print " * cpulimit daemon can not be stopped"; else print " * cpulimit daemon stopped successfully" }'
else
    echo " * cpulimit daemon can't be stopped, because it is not running"
fi
;;
restart)
$0 stop
sleep 3
$0 start
;;
status)
ps -eo pid,args | gawk '$3=="/usr/bin/cpulimit_daemon.sh"  {print}' | wc -l | gawk '{ if ($1 == 1) print " * cpulimit daemon is running"; else print " * cpulimit daemon is not running" }'
;;
esac
exit 0

4. Setup the scripts to run automatically

Run the following to set executable bit and start it automatically near the end of boot:

sudo chmod 755 /usr/bin/cpulimit_daemon.sh /etc/init.d/cpulimitd
sudo update-rc.d cpulimitd defaults 95 05

Uninstalling cpulimit daemon

If you want to get rid of it, do the following:

sudo service cpulimitd stop             # Stop cpulimit daemon and all cpulimited processes
sudo update-rc.d -f cpulimitd remove    # Remove symbolic links
sudo rm /etc/init.d/cpulimitd           # Delete cpulimit boot-up script
sudo rm /usr/bin/cpulimit_daemon.sh    # Delete cpulimit daemon script
sudo apt-get remove cpulimit # if you no longer want the program even available to run
sudo apt-get remove gawk # if you no longer need this either

Leave a Reply

Your email address will not be published. Required fields are marked *