Previous | Table of Contents | Next |
In many scripts, there are critical sections where commands that require a large amount of time to complete are executed. On rare occasions, these commands might not finish processing. In order to deal with this situation, you need to set up a timer within the script. When the timer expires, you should terminate the program and inform the user about the abnormal exit.
In this section you will walk through a simple script that demonstrates the major aspects of setting up a timer using the ALARM signal and a signal handler.
The main body of this script needs to perform the following actions:
If the timer expires before the program finishes executing, the handler for the ALARM signal needs to terminate the program.
The main body looks like the following:
# main() trap AlarmHandler 14 SetTimer 15 $PROG & CHPROCIDS="$CHPROCIDS $!" wait $! UnsetTimer echo "All Done." exit 0
The only thing in the main body that was not explicitly mentioned previously is the CHPROCIDS variable. This variable is required to maintain a list of all the child processes of the script, because the possible child process are
AlarmHandler
The first thing that you do is to install a handler for the ALARM signal. This handler is the function AlarmHandler:
AlarmHandler() { echo "Got SIGALARM, cmd took too long." KillSubProcs exit 14 }
This is a simple function that prints a message to the screen, calls the function KillSubProcs, and then exits with an exit code of 14 to indicate that the alarm was triggered.
The KillSubProcs function is a simple function that kills all the child processes of the script as stored in the variable CHPROCIDS:
KillSubProcs() { kill ${CHPROCIDS:-$!} if [ $? -eq 0 ] ; then echo "Sub-processes killed." ; fi }
SetTimer
The next task that you perform is to set the timer using the SetTimer function:
SetTimer() { DEF_TOUT=${1:-10}; if [ $DEF_TOUT -ne 0 ] ; then sleep $DEF_TOUT && kill -s 14 $$ & CHPROCIDS="$CHPROCIDS $!" TIMERPROC=$! fi }
This function takes a single argument that indicates the number of sections that it should set a timer for. The default is 10 seconds.
As you can see, setting the timer is fairly trivial. All you do is issue a sleep command with a timeout and then use the kill command to send the shells process ID (stored in $$) an ALARM signal. In order to continue processing, you place this timer command in the background.
Because the timer is a background process, you need to update the list of child processes with its ID. You also save the process ID of the timer in the variable TIMERPROC so that you can later unset the timer.
UnsetTimer
The last function is the UnsetTimer function that unsets the timer set by the SetTimer function. Unsetting the timer basically means killing its process:
UnsetTimer() { kill $TIMERPROC }
The Complete Timer Script
The complete timer script follows:
#! /bin/sh AlarmHandler() { echo "Got SIGALARM, cmd took too long." KillSubProcs exit 14 } KillSubProcs() { kill ${CHPROCIDS:-$!} if [ $? -eq 0 ] ; then echo "Sub-processes killed." ; fi } SetTimer() { DEF_TOUT=${1:-10}; if [ $DEF_TOUT -ne 0 ] ; then sleep $DEF_TOUT && kill -s 14 $$ & CHPROCIDS="$CHPROCIDS $!" TIMERPROC=$! fi } UnsetTimer() { kill $TIMERPROC } # main() trap AlarmHandler 14 SetTimer 15 $PROG & CHPROCIDS="$CHPROCIDS $!" wait $! UnsetTimer echo "All Done." exit 0
In this chapter, I introduced the concept of signals. Signals inform a program or script that an important event has occurred.
You learned about the most common signals encountered in shell programming. I also listed several methods of obtaining a complete list of all the signals understood by your system. In this section you also covered the concept of delivering signals and the default actions associated with a signal.
In the second section of this chapter I introduced the three main methods of handling a signal in a script. Now you know how to catch signals and handle them using signal handlers. You also know how to ignore signals. Finally I showed you how to use signals to set up a timer inside your scripts.
# main() trap CleanUp 2 3 15 trap Init 1 PROG=$1 Init while : ; do wait $! $PROG & done
Previous | Table of Contents | Next |