I put an entry out here that detailed how to find the process ID of a running process and then how to re-nice it from within a bash shell script. This first script referenced processes that could be identified by a single pid. But what about processes that spawn multiple pids, or multiple processes that are all named the same but are, in fact, separate instances?
Here is a small bash script segment that identifies process PIDs; puts them into an array; then uses that array to renice the processes. In this case, the processes are VoIP applications that use RTP (real time protocol), and, in particular SIP (Session Initiation Protocol) to put voice traffic on the wire and usually need immediate CPU access.
This script does many things before it gets to this point… we pick it up here at the renice process… Note that some of the variables you see here were instantiated early in the script. Also, to get the code to fit neatly on the page here, I took out a lot of the formatting of the echo statements that allows for clear logging…
######################### echo " Readjusting CPU timeslices..." | tee -a $LOG_FILE PROCESS_ONE='RTPApplication1' PROCESS_TWO='RTPApplication2' echo "Renicing service priorities..." | tee -a $LOG_FILE echo "Here then, is PROCESS_ONE: $PROCESS_ONE" | tee -a $LOG_FILE echo "And here is PROCESS_TWO: $PROCESS_TWO" | tee -a $LOG_FILE echo "Instantiating the PID arrays..." | tee -a $LOG_FILE
I need the log to have as much useful data as possible, hence the redundant echoing of the PROCESS_ONE and PROCESS_TWO variables. I expect this script to be managed and run by other voice engineers, and the log needs to be useful to them as well as to me.
I am piping the ‘tee -a’ command into the $LOG_FILE so that I can run this script from the command line and see the results of it on stdout as well as write those results to the log.
voiceProcess1=( $( ps -C $PROCESS_ONE -o pid= ) ) voiceProcess2=( $( ps -C $PROCESS_TWO -o pid= ) ) echo "Now logging how many elements are in each array " | tee -a $LOG_FILE processOneArrayLength=${#voiceProcess1[@]} processTwoArrayLength=${#voiceProcess2[@]} echo "voiceProcess1 contains $processOneArrayLength elements" | tee -a $LOG_FILE echo "voiceProcess2 contains $processTwoArrayLength elements" | tee -a $LOG_FILE echo "" | tee -a $LOG_FILE
What I just did was to create two arrays, one called voiceProcess1 and the other called voiceProcess2. Then I populated that array with the ps -C command that probably displays several PIDs for each process.
After that, I needed to count the number of objects in each array to ensure that there is actually something in each one. I am trying to avoid running a renice process against non-existent array elements.
Then I wrote the array lengths to the log to maintain the record.
Now I can run the renice procedure:
echo "Now renicing all those processes" | tee -a $LOG_FILE echo "First, PROCESS_ONE..." | tee -a $LOG_FILE if [ $processOneArrayLength -gt 0 ];then for ((i = 0; i < $processOneArrayLength; i++ )); do renice -19 ${voiceProcess1[$i]} done else echo "There are no PROCESS_ONE services running" | tee -a $LOG_FILE echo "Emailing the ADMIN and continuing..." | tee -a $LOG_FILE mail -s "There are no voice server PROCESS_ONE services running on $HOSTNAME. Check the logs" $ADMIN < $LOG_FILE fi
Here I use an if-else statement to ensure that the PROCESS_ONE array has more than zero elements in it. Because of the nature of the application, it is possible that there will be only one instance of the service running with one associated PID. In which case, the array will only have one element in it.
If the array is empty, the else statement kicks in and emails the admin. Note, that the script does not exit here even though an empty array creates an error condition. The reason for that is because renicing is not mission critical. Instead, it is considered performance tuning. The admin will get the notice, and will need to deal with correcting the problem, but that doesn’t mean the rest of the script shouldn’t run.
Now it’s time to deal with PROCESS_TWO in the same way. I like a lot of white space in my logs which is why I add a bunch of these blank echoes as you can see in this next line:
echo "" | tee -a $LOG_FILE echo "Second, PROCESS_TWO..." | tee -a $LOG_FILE if [ $processTwoArrayLength -gt 0 ];then for ((i = 0; i < $processTwoArrayLength; i++ )); do renice -19 ${voiceProcess2[$i]} done else echo "There are no PROCESS_TWO services running" | tee -a $LOG_FILE echo "Emailing the ADMIN and continuing..." | tee -a $LOG_FILE mail -s "There are no voice server PROCESS_TWO services running on $HOSTNAME. Check the logs" $ADMIN < $LOG_FILE fi
Then I end this section of the script with a ‘#’ separator. I wanted any engineer who managed the script to be able to read the log without having to struggle to figure out where things were.
echo "#######################" | tee -a $LOG_FILE
