Section 14.7. Sending and Receiving Signals


14.7. Sending and Receiving Signals

A 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 outthe bridge collapsed," "the light has changedget going," "stop drivingyou've got a baby on the roof," or "hello, world"? Fortunately, Unix signals are easier to interpret than that because there's a different one for each of these situations.[*] 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 Ctrl-C) on the terminal, which sends a SIGINT to all the processes attached to that terminal.[] Some signals are sent automatically by the system, but they can come from another process.

[] And you thought that pressing Ctrl-C stopped your program. Actually, it simply sends the SIGINT signal, and that stops the program by default. As youll see later in this chapter, you can make a program that does something different when SIGINT comes in rather than stopping at once.

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,[] but say you know that you want to send a SIGINT to process 4201. Thats easy enough:

[] Usually, you have the process ID because its a child process you produced with fork, or you found it in a file or from an external program. Using an external program can be difficult and problematic, which is why many long-running programs save their own current process ID into a file, usually described in the program's documentation.

     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 use the string 'INT' in place of the 2 there because signal number 2 is SIGINT. If the process no longer exists,[*] you'll get a false return value, so you can use this technique to see if a process is alive. A special signal number of 0 says, "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." A process probe might look like:

[*] Sending a signal will also fail if you're not the superuser and it's someone else's process. It would be rude to send SIGINT to someone else's programs, anyway.

     unless (kill 0, $pid) {       warn "$pid has gone away!";     } 

Perhaps more interesting than sending signals is catching signals. Why might you want to do this? 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 Ctrl-C during the execution, that leaves trash in /tmp, an impolite 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 someone presses Ctrl-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[] naming the subroutine without the ampersand. From then on, if a SIGINT comes along, Perl will stop whatever its doing and jump to the subroutine. Our subroutine cleans up the temp files and exits. (If nobody presses Ctrl-C, we'll still call &clean_up at the end of normal execution.)

[] The value can be a subroutine reference, but were not doing those here.

If the subroutine returns rather than exiting, execution will resume right where it was interrupted. This can be useful if the interrupt needs to interrupt something rather than causing it to stop. For example, suppose processing each line of a file takes a few seconds, which is slow, and you want to abort the overall processing when an interrupt is processed but not in the middle of processing a line. 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;       }     } 

As each line is processed, the value of $int_count will be 0 if no one has pressed Ctrl-C, 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 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,[*] however, so keep the stuff you're doing in there to a minimum, or your program may end up blowing up sometime when you least expect it.

[*] This is one of the top items on the Perl developers' list of things to fix, so we expect reliable signal handling to be one of the first items on the new feature list for Perl 6. The problem is that a signal may come in at any time, even when Perl isn't ready for one. For example, if Perl is in the middle of allocating some memory when a signal comes in, the signal handler can accidentally allocate some memory and your program is dead. You can't control when your Perl code will allocate memory, but XSUB code (usually written in C) can safely handle signals. See the Perl documentation for more information about this advanced topic.



Learning Perl
Learning Perl, 5th Edition
ISBN: 0596520107
EAN: 2147483647
Year: 2003
Pages: 232

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