Section 5.7. Signals


5.7. Signals

For lack of a better analogy, signals are a way to poke a stick at a process. Programs generate signals to trigger a handler for that signal in another process. The operating system pokes toosome signals are generated on unusual system events and may kill the program if not handled. If this sounds a little like raising exceptions in Python, it should; signals are software-generated events and the cross-process analog of exceptions. Unlike exceptions, though, signals are identified by number, are not stacked, and are really an asynchronous event mechanism outside the scope of the Python interpreter controlled by the operating system.

In order to make signals available to scripts, Python provides a signal module that allows Python programs to register Python functions as handlers for signal events. This module is available on both Unix-like platforms and Windows (though the Windows version defines fewer kinds of signals to be caught). To illustrate the basic signal interface, the script in Example 5-21 installs a Python handler function for the signal number passed in as a command-line argument.

Example 5-21. PP3E\System\Processes\signal1.py

 ########################################################################## # catch signals in Python; pass signal number N as a command-line arg, # use a "kill -N pid" shell command to send this process a signal;  most # signal handlers restored by Python after caught (see network scripting # chapter for SIGCHLD details); on Windows, signal module is available, # but it defines only a few signal types there, and os.kill is missing; ########################################################################## import sys, signal, time def now(): return time.ctime(time.time( ))        # current time string def onSignal(signum, stackframe):                 # python signal handler     print 'Got signal', signum, 'at', now( )      # most handlers stay in effect signum = int(sys.argv[1]) signal.signal(signum, onSignal)                   # install signal handler while 1: signal.pause( )                          # wait for signals (or: pass) 

There are only two signal module calls at work here:


signal.signal

Takes a signal number and function object and installs that function to handle that signal number when it is raised. Python automatically restores most signal handlers when signals occur, so there is no need to recall this function within the signal handler itself to reregister the handler. That is, except for SIGCHLD, a signal handler remains installed until explicitly reset (e.g., by setting the handler to SIG_DFL to restore default behavior, or to SIG_IGN to ignore the signal). SIGCHLD behavior is platform specific.


signal.pause

Makes the process sleep until the next signal is caught. A time.sleep call is similar but doesn't work with signals on my Linux box; it generates an interrupted system call error. A busy while 1: pass loop here would pause the script too but may squander CPU resources.

Here is what this script looks like running on Linux: a signal number to watch for (12) is passed in on the command line, and the program is made to run in the background with an & shell operator (available in most Unix-like shells):

 [mark@toy]$ python signal1.py 12 & [1] 809 [mark@toy]$ ps   PID TTY          TIME CMD   578 ttyp1    00:00:00 tcsh   809 ttyp1    00:00:00 python   810 ttyp1    00:00:00 ps [mark@toy]$ kill -12 809 [mark@toy]$ Got signal 12 at Fri Sep  8 00:27:01 2000 kill -12 809 [mark@toy]$ Got signal 12 at Fri Sep  8 00:27:03 2000 kill -12 809 [mark@toy]$ Got signal 12 at Fri Sep  8 00:27:04 2000 [mark@toy]$ kill -9 809           # signal 9 always kills the process 

Inputs and outputs are a bit jumbled here because the process prints to the same screen used to type new shell commands. To send the program a signal, the kill shell command takes a signal number and a process ID to be signaled (809); every time a new kill command sends a signal, the process replies with a message generated by a Python signal handler function.

The signal module also exports a signal.alarm function for scheduling a SIGALRM signal to occur at some number of seconds in the future. To trigger and catch timeouts, set the alarm and install a SIGALRM handler as shown in Example 5-22.

Example 5-22. PP3E\System\Processes\signal2.py

 ########################################################################## # set and catch alarm timeout signals in Python; time.sleep doesn't play # well with alarm (or signal in general in my Linux PC), so we call # signal.pause here to do nothing until a signal is received; ########################################################################## import sys, signal, time def now(): return time.ctime(time.time( )) def onSignal(signum, stackframe):                 # python signal handler     print 'Got alarm', signum, 'at', now( )       # most handlers stay in effect while 1:     print 'Setting at', now( )     signal.signal(signal.SIGALRM, onSignal)       # install signal handler     signal.alarm(5)                               # do signal in 5 seconds     signal.pause( )                               # wait for signals 

Running this script on Linux causes its onSignal handler function to be invoked every five seconds:

 [mark@toy]$ python signal2.py Setting at Fri Sep  8 00:27:53 2000 Got alarm 14 at Fri Sep  8 00:27:58 2000 Setting at Fri Sep  8 00:27:58 2000 Got alarm 14 at Fri Sep  8 00:28:03 2000 Setting at Fri Sep  8 00:28:03 2000 Got alarm 14 at Fri Sep  8 00:28:08 2000 Setting at Fri Sep  8 00:28:08 2000 

Generally speaking, signals must be used with cautions not made obvious by the examples we've just seen. For instance, some system calls don't react well to being interrupted by signals, and only the main thread can install signal handlers and respond to signals in a multithreaded program.

When used well, though, signals provide an event-based communication mechanism. They are less powerful than data streams such as pipes, but are sufficient in situations in which you just need to tell a program that something important has occurred and don't need to pass along any details about the event itself. Signals are sometimes also combined with other IPC tools. For example, an initial signal may inform a program that a client wishes to communicate over a named pipethe equivalent of tapping someone's shoulder to get their attention before speaking. Most platforms reserve one or more SIGUSR signal numbers for user-defined events of this sort. Such an integration structure is sometimes an alternative to running a blocking input call in a spawned thread.

See also the os.kill(pid, sig) call for sending signals to known processes from within a Python script on Unix-like platforms (the required process ID can be obtained from the os.fork call's child process ID return value or from other interfaces). Also watch for the discussion about using signal handlers to clean up zombie processes in the Internet scripting part later in this book.




Programming Python
Programming Python
ISBN: 0596009259
EAN: 2147483647
Year: 2004
Pages: 270
Authors: Mark Lutz

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net