pcntl_signal

Descrição

The pcntl_signal() function installs a new
signal handler or replaces the current signal handler for the signal indicated by signo.

Parâmetros

signo

The signal number.

handler

The signal handler. This may be either a callable, which
will be invoked to handle the signal, or either of the two global
constants SIG_IGN or SIG_DFL,
which will ignore the signal or restore the default signal handler
respectively.

Note that when you set a handler to an object method, that object's
reference count is increased which makes it persist until you either
change the handler to something else, or your script ends.

restart_syscalls

Specifies whether system call restarting should be used when this
signal arrives.

Valor Retornado

Retorna TRUE em caso de sucesso ou FALSE em caso de falha.

Changelog

Versão

Descrição

4.3.0

As of PHP 4.3.0 PCNTL uses ticks as the signal handle callback
mechanism, which is much faster than the previous mechanism. This
change follows the same semantics as using
"user ticks".
You must use the declare() statement to
specify the locations in your program where callbacks are allowed to
occur for the signal handler to function properly (as used in the
example below).

sleep() calls seem to be interrupted when any signal is received by the PHP script. But things get weird when you sleep() inside a signal handler.

Ordinarily, signal handlers are non-reentrant. That is, if the signal handler is running, sending another signal has no effect. However, sleep() seems to override PHP's signal handling. If you sleep() inside a signal handler, the signal is received and the sleep() is interrupted.

I was having some issues with processing a signal using an object method I use for something else as well. In particular, I wanted to handle SIGCHLD with my own method "do_reap()" which I also call after a stream_select timeout and that uses a non-blocking pcntl_waitpid function.

The method was called when the signal was received but it couldn't reap the child.

The only way it worked was by creating a new handler that itself called do_reap().

Multiple children return less than the number of children exiting at a given moment SIGCHLD signals is normal behavior for Unix (POSIX) systems. SIGCHLD might be read as "one or more children changed status -- go examine your children and harvest their status values". Signals are implemented as bit masks in most Unix systems, so there can only be 1 SIGCHLD bit set in any given kernel tick for a process.

Under my setup (FreeBSD 6.2-RELEASE / PHP 5.2.4 CLI) I've noticed that when a child exits the SIGCHLD handler in the parent is not always invoked. It seems to happen when two children exit near simultaneously.

In this instance the child prints "EXIT" and the parent prints "SIGCHLD received":

- EXIT- SIGCHLD received

This works as expected, but now look what happens when three exit in quick succession:

- EXIT- EXIT- EXIT- SIGCHLD received- SIGCHLD received

Because of this quirk any code which tries to limit the maximum number of children by incrementing on fork and decrementing on SIGCHLD will eventually end up with a single child (or no further forks), since the "active children" count is always above the maximum. I've noticed similar behaviour with using decrement after pcntl_wait(). Hopefully there's a workaround for this.

If you have a script that needs certain sections to not be interrupted by a signal (especially SIGTERM or SIGINT), but want to make your script ready to process that signal ASAP, there's only one way to do it. Flag the script as having received the signal, and wait for your script to say its ready to process it.

You set $allow_exit to true at all times when it is perfectly acceptable that your script could exit without warning. In sections where you really need the script to continue running, you set $allow_exit to false. Any signals received while $allow_exit is false will not take effect until you set $allow_exit to true.

<? $allow_exit = true;

// unimportant stuff here. exiting will not harm anything

$allow_exit = false;

// really important stuff not to be interrupted

$allow_exit = true;

// more unimportant stuff. if signal was received during // important processing above, script will exit here?>

I have been having trouble reaping my child process. It seemed most of the time, children were reaped properly but *sometimes* they would stay as zombies. I was catching the CHLD signal to reap child processes with the following code:

/* when finished, just clean up the children */print "Reaping $kids children...\n";while ($kids) sleep(1);

print "Finished.\n";?>

The problem was, $kids never became zero so it would effectively wait forever. After wracking my brains (UNIX forks are new to me) I finally read the Perl IPC docs and viola, a solution! It turns out that because signal handlers are not re-entrant, my handler will not be called again while it is in use. The scenario that caused me trouble was that one child would exit and call the signal handler, which would pcntl_waitpid() it and decrement the counter. Meanwhile, another child would exit while the first child was still being reaped, so the second would never get to notify the parent!

The solution was to continually reap children from the SIGCHLD handler, so long as there were children to reap. Here is the *fixed* childFinished function:

In at least version 5.1.4, the parameter passed to the handler is not a strict integer.

I have had such problems as trying to add the signal to an array, but the array is completely screwed up when viewed (but not viewed immediately after being added). This occurs when the handler is a method (array($this, 'methodname')), or a traditional functions.

To avoid this bug, typecast the parameter to an integer:(note that each newline may appear to just be 'n'.)

The parent forked a child. The child did a bunch of stuff, and when it was done it sent SIGUSR1 to the parent and immediately exited.

Result: The child turned into a zombie process waiting for the parent to harvest it, as it should.

But the parent was unable to harvest the child because it was receiving an endless barrage of SIGUSR1s. In fact, it logged over 200000 before I shut it down (SIGKILL). The parent was not able to do anything else (respond to other events) during that time.

No, it wasn't a bug in my child code. Apparently, after sending a signal there is some "behind the covers" work that needs to occur to acknowledge signal completion, and when you exit immediately it is not able to happen, so the system just keeps trying.

Solution: I introduced a small delay in the child, after sending the signal, and before exiting. No more Sig loops...

----------

P.S. With respect to the note below. The whole point of the sleep function is to enable the processing of other events. So, yes, your non-renterent code, will suddenly get re-entered when you do a sleep, because you have just handed off control to the next pending event.

Ignoring the signal is only an option if the signal is unimportant to your program.... The better way to approach it, is to not do lengthy processing inside of the signal event. Instead set a global flag and get the heck out of there as fast as possible. Allow another part of your program to check the flag and do the processing outside of the signal event. Usually your program is in some kind of loop when it is receiving signals, so checking a flag regularly shouldn't be a problem.