14.7. Sending and Receiving SignalsA Unix signal is a tiny message sent to a process. It can't say much; it's like a car horn honking -- does that honk you hear mean "look out -- the bridge collapsed" or "the light has changed -- get going" or "stop driving -- you've got a baby on the roof" or "hello, world"? Well, fortunately, Unix signals are a little easier to interpret than that, because there's a different one for each of these situations.[325]
Different signals are identified by a name (such as SIGINT, meaning "interrupt signal") and a corresponding small integer (in the range from 1 to 16, 1 to 32, or 1 to 63, depending on your Unix flavor). Signals are typically sent when a significant event happens, such as pressing the interrupt character (typically Control-C) on the terminal, which sends a SIGINT to all the processes attached to that terminal.[326] Some signals are sent automatically by the system, but they can also come from another process.
You can send signals from your Perl process to another process, but you have to know the target's process ID number. How to figure that out is a bit complicated,[327] but let's say you know that you want to send a SIGINT to process 4201. That's easy enough:
kill 2, 4201 or die "Cannot signal 4201 with SIGINT: $!"; It's named "kill" because one of the primary purposes of signals is to stop a process that's gone on long enough. You can also use the string 'INT' in place of the 2 there, because signal number 2 is SIGINT. If the process no longer exists,[328] you'll get a false return value, so you can also use this technique to see whether a process is still alive. A special signal number of 0 says "just check to see whether I could send a signal if I wanted to, but I don't want to, so don't actually send anything." So a process probe might look like:
unless (kill 0, $pid) { warn "$pid has gone away!"; } Perhaps a little more interesting than sending signals is catching signals. Why might you want to do this? Well, suppose you have a program that creates files in /tmp, and you normally delete those files at the end of the program. If someone presses Control-C during the execution, that leaves trash in /tmp, a very unpolite thing to do. To fix this, create a signal handler that takes care of the cleanup: my $temp_directory = "/tmp/myprog.$$"; # create files below here mkdir $temp_directory, 0700 or die "Cannot create $temp_directory: $!"; sub clean_up { unlink glob "$temp_directory/*"; rmdir $temp_directory; } sub my_int_handler { &clean_up; die "interrupted, exiting...\n"; } $SIG{'INT'} = 'my_int_handler'; . . # Time passes, the program runs, creates some temporary . # files in the temp directory, maybe somone presses Control-C . # Now it's the end of normal execution &clean_up; The assignment into the special %SIG hash activates the handler (until revoked). The key is the name of the signal (without the constant SIG prefix), and the value is a string[329] naming the subroutine, without the ampersand. From then on, if a SIGINT comes along, Perl stops whatever it's doing and jumps immediately to the subroutine. Our subroutine cleans up the temp files and then exits. (And if nobody presses Control-C, we'll still call &clean_up at the end of normal execution.)
If the subroutine returns rather than exiting, execution resumes right where it was interrupted. This can be useful if the interrupt needs to actually interrupt something rather than causing it to stop. For example, suppose processing each line of a file takes a few seconds, which is pretty slow, and you want to abort the overall processing when an interrupt is processed, but not in the middle of processing a line. Just set a flag in the interrupt procedure, and check it at the end of each line's processing: my $int_count; sub my_int_handler { $int_count++ } $SIG{'INT'} = 'my_int_handler'; ... $int_count = 0; while (<SOMEFILE>) { ... some processing that takes a few seconds ... if ($int_count) { # interrupt was seen! print "[processing interrupted...]\n"; last; } } Now as each line is processed, the value of $int_count will be 0 if no one has pressed Control-C, and so the loop continues to the next item. However, if an interrupt comes in, the interrupt handler increments the $int_count flag, breaking out of the loop when checked at the end. So, you can either set a flag or break out of the program, and that covers most of what you'll need from catching signals. The current implementation of signal handlers is not entirely without faults,[330] however, so keep the stuff you're doing in there to an absolute minimum, or your program may end up blowing up sometime when you least expect it.
Copyright © 2002 O'Reilly & Associates. All rights reserved. |
|