Hudzilla.org - the homepage of Paul Hudson
Contents > Miscellaneous topics > Process control Wish List | Report Bug | About Me ]

16.1.1     Taking control of PHP: pcntl_signal()

This is NOT the latest copy of this book; click here for the latest version.

bool pcntl_signal ( int signo, callback handle [, bool restart_syscalls])

If you have used your Unix box for any number of time, you will likely be well aware that signals are used all over the place for a variety of tasks. If you are new to the concept of signals, you should think of them as messages between programs that carry very simple instructions. For example, when you want to quit a command-line application in Unix, you just hit Ctrl-C and it stops, right? But why does it stop?

Well, behind the scenes the Ctrl-C combination generates the interrupt signal, SIGINT, which gets sent to the application. Unless the application has its own specific handler for the SIGINT signal, the default is used: "terminate immediately". Of course, an application could very well define its own handler for SIGINT that ignores it, so how can you force an application to be terminated? The obvious way is "kill -9 <PID>", which sends the signal SIGKILL to the application. Unlike SIGINT, SIGKILL does not just interrupt the program, it terminates it without further question. More importantly, SIGKILL cannot be overridden by a program, so it will always work.

Although this sounds like Unix has two signals for the same thing, they are just very similar - many applications override SIGINT so that they shutdown cleanly, whereas SIGKILL always terminates irrespective of the current program state. There is also SIGTERM, which again is very similar to both SIGINT and SIGKILL, which is the default termination command sent by the kill command. To confuse things further, there is yet another signal that looks like these: SIGQUIT. This is identical to SIGINT with the exceptions that it is generated by pressing Ctrl-\ rather than Ctrl-C and also that it generates a core dump if necessary.

Through the use of the pcntl_signal() function, we can configure our PHP scripts to respond to signals however we want it to - excepting SIGKILL, that is. This takes a signal name as its first parameter, a callback PHP function to call as a signal handler as its second parameter, and finally a third parameter we will look at shortly. First, here is an example script:

<?php
    
declare(ticks = 1);

    
pcntl_signal(SIGTERM, "signal_handler");
    
pcntl_signal(SIGINT, "signal_handler");

    function
signal_handler($signal) {
        switch(
$signal) {
            case
SIGTERM:
                print
"Caught SIGTERM\n";
                exit;
            case
SIGKILL:
                print
"Caught SIGKILL\n";
                exit;
            case
SIGINT:
                print
"Caught SIGINT\n";
                exit;
        }
    }

    while(
1) {
        
//
    
}
?>

The very first line kicks off with a call to the declare() function that we looked at a long time ago. This is, to the best of my knowledge, the only place declare() is used in the entire book, excluding the other process control functions we will be looking at shortly.

Moving on, our new pcntl_signal() function is used twice to tie both SIGTERM and SIGINT to the function signal_handler(), defined next. Note that both SIGTERM and SIGINT are PHP constants that evaluate to their Unix equivalents.

The callback function we define, signal_handler(), takes a signal as its only parameter. When a signal comes in from the OS, this gets passed into the function so that you can determine was signal was received. Thus, a switch/case block is used to take the appropriate action based on the type of the signal. Note that there is a case block in there to match SIGKILL - try adding a signal_handler() call for it and see what PHP says!

In the switch/case block, each of the possible signals that we trap print out a short error message and exit the script immediately. If you do not call exit, both SIGINT and SIGTERM will not terminate PHP. While this might sound nice ("hey, if the user wants to quit they should read my manual where it says you need to press Ctrl-Alt-PQR-9 to quit!"), in practice this is simply frustrating for users. SIGINT and SIGTERM should quit, unless you have a very specific reason not to.

The final part of the script is an infinite loop - it whizzes around and around because the condition is simply "1", which means the script will never stop executing. Of course, this is perfect for our test of signals, as the only thing that will terminate the script will be a signal coming in. You need not worry about the maximum script execution time being hit, as you should be running this script through the CLI SAPI, and that has the maximum execution time disabled.

Go ahead and run the script - you should notice it will not exit until you hit either Ctrl-C or send "killall php" through another terminal. Just for kicks, note what happens when you use "kill -9 php" from another terminal - PHP will exit immediately, printing out "Killed".

Of course, exiting the program is the default action of SIGINT and SIGTERM, so why bother with the code above in your own scripts? Well, the idea is that you do more work than just exiting immediately - closing any open files, saving data, writing to a log file, etc, can and should be done when the proper signals are received. Just do not forget to use "exit" when you have finished your handling, and remember that if your script does not end very soon after SIGINT/SIGTERM is received, the user may get impatient and resort to "kill -9".





<< 16.1 Process control   16.1.2 Timing your signals: pcntl_alarm() >>
Table of Contents
Want to see this stuff in print? PHP in a Nutshell takes the core topics covered here, adds in thousands of edits from the editorial team and myself, and combines them to make an unbeatable reference for PHP programmers at all levels.



My latest book has hundreds more tips on how to use PHP, Apache, and MySQL, plus Perl, Python, shell scripts, performance tuning, and more!



Top-right shadow
 
Bottom-left shadow Bottom shadow

Comments from other readers
tinkertim - 07 Sep 2008

Ticks are needed so that PHP knows to poll for signals, that makes perfect sense.

What I can't figure out how to do is get pcntl_signal() to tell me if a signal is currently being handled or ignored, so that my modular class that must deal with certain signals doesn't clobber existing handlers (rather, it should explain that it needs to handle those signals and bail).

For instance, I'm writing a class that wraps some command line tools that I wrote (in C). To avoid exec() timeouts, its much easier to background these things and have the programs send my php front end a SIGUSR1/SIGUSR2 (on success/failure respectively) .. then PHP knows that its time to go gather the output.

I modified my C programs so that I can add a --callback-pid = [pid of php script], which causes them to send the appropriate signals if specified.

However, if SIGUSR1 is currently being ignored, or another handler exists for it .. this is a conflict for someone who hopes to just include the class that I'm making and get up and running.

Likewise, for PHP programs designed to be run from the console, SIGTSTP, SIGTTOU and SIGTTIN become interesting if someone control-z's the process.

Is there a way for a modular class to know if these are already being ignored or handled?

I know, I know .. PHP is not C :)

Unix Programmer - 07 Sep 2008

Why is the 'declare()' call used? Is it needed to allow the signals to be delivered? Just curious.



Add comment
Please note that by posting a comment here you are committing it to the public domain. This is important so that others can make use of your code themselves, and also so that I can incorporate helpful notes directly into the main text. Comments are limited to 2000 characters in length.

If you are reporting an error in the content, please tell me directly.

Your name/email address:
Your comment:
 
Now, in order to verify that you're a real person, please answer this simple question: what is one plus nine?
The answer is:
(please write in
numbers, eg 19)


Top-right shadow
 
Bottom-left shadow Bottom shadow