PHP.mk документација

pcntl_signal

Почист и полокален преглед на PHP референцата, со задржана структура од PHP.net и подобра читливост за примери, секции и белешки.

function.pcntl-signal.php PHP.net прокси Преводот се освежува
Оригинал на PHP.net
Патека function.pcntl-signal.php Локална патека за оваа страница.
Извор php.net/manual/en Оригиналниот HTML се реупотребува и локално се стилизира.
Режим Прокси + превод во позадина Кодовите, табелите и белешките остануваат читливи во истиот тек.
pcntl_signal

Референца за `function.pcntl-signal.php` со подобрена типографија и навигација.

function.pcntl-signal.php

pcntl_signal

(PHP 4 >= 4.1.0, PHP 5, PHP 7, PHP 8)

pcntl_signalInstalls a signal handler

= NULL

pcntl_signal(int $signal, callable|int $handler, bool $restart_syscalls = true): bool

На pcntl_signal() Инсталира обработувач на сигнали signal.

Параметри

signal

Бројот на сигналот.

handler

функцијата инсталира нов обработувач на сигнали или го заменува тековниот обработувач на сигнали за сигналот означен со callableОбработувачот на сигнали. Ова може да биде или SIG_IGN or SIG_DFL, што ќе се повика за да го обработи сигналот, или некој од двата глобални константи

Ако callable , што ќе го игнорира сигналот или ќе го врати стандардниот обработувач на сигнали, соодветно.

handler(int $signo, mixed $siginfo): void
signal
е дадено, мора да ја имплементира следната сигнатура:
siginfo
Сигналот што се обработува.

Забелешка:

Ако оперативните системи поддржуваат siginfo_t структури, ова ќе биде низа од информации за сигнали во зависност од сигналот.

restart_syscalls

Имајте предвид дека кога поставувате обработувач на метод на објект, референтниот бројач на тој објект се зголемува, што го прави да опстојува додека не го промените обработувачот на нешто друго, или вашата скрипта не заврши.

Вратени вредности

Патеката до PHP скриптата што треба да се провери. true на успех или false при неуспех.

Дневник на промени

Верзија = NULL
7.1.0 Пред PHP 7.1.0, променливите за препишување поставени од output_add_rewrite_var користат истиот излезен бафер за транзитирање на сесискиот модул. Од PHP 7.1.0, се користи посветен излезен бафер и output_reset_rewrite_vars само ги отстранува променливите за препишување дефинирани од output_add_rewrite_var.

Примери

Пример #1 pcntl_signal() example

<?php
pcntl_async_signals
(true);

// signal handler function
function sig_handler($signo)
{

switch (
$signo) {
case
SIGTERM:
// handle shutdown tasks
exit;
break;
case
SIGHUP:
// handle restart tasks
break;
case
SIGUSR1:
echo
"Caught SIGUSR1...\n";
break;
default:
// handle all other signals
}

}

echo
"Installing signal handler...\n";

// setup signal handlers
pcntl_signal(SIGTERM, "sig_handler");
pcntl_signal(SIGHUP, "sig_handler");
pcntl_signal(SIGUSR1, "sig_handler");

// or use an object
// pcntl_signal(SIGUSR1, array($obj, "do_something"));

echo"Generating signal SIGUSR1 to self...\n";

// send SIGUSR1 to current process id
// posix_* functions require the posix extension
posix_kill(posix_getpid(), SIGUSR1);

echo
"Done\n";

?>

Белешки

pcntl_signal() Специфицира дали треба да се користат системски повторни повици кога ќе пристигне овој сигнал.

не ги реди сигналите, туку ги заменува.

Методи за диспечирање

  • Постојат неколку методи за диспечирање на обработувачи на сигнали: pcntl_async_signals() Асинхроно диспечирање со
  • Поставување tick frequency
  • овозможено. Ова е препорачаниот метод pcntl_signal_dispatch()

Рачно диспечирање со sleep() Кога сигналите се диспечираат асинхроно или со користење на извршување базирано на тикови, блокирачките функции како

Види Исто така

Белешки од корисници 28 белешки

- Повикува ракувачи на сигнали за сигнали што чекаат
пред 8 години
If you are using PHP >= 7.1 then DO NOT use `declare(ticks=1)` instead use `pcntl_async_signals(true)`

There's no performance hit or overhead with `pcntl_async_signals()`. See this blog post https://blog.pascal-martin.fr/post/php71-en-other-new-things.html for a simple example of how to use this.
Рајан Џентцш
пред 6 години
Remember that signal handlers are called immediately, so every time when your process receives registered signal blocking functions like sleep() and usleep() are interrupted (or more like ended) like it was their time to finish.

This is expected behaviour, but not mentioned in this documentation. Sleep interruption can be very unfortunate when for example you run a daemon that is supposed to execute some important task exactly every X seconds/minutes - this task could be called too early or too often and it would be hard to debug why it's happening exactly.

Simply remember that signal handlers might interfere with other parts of your code.
Here is example workaround ensuring that function gets executed every minute no matter how many times our loop gets interrupted by incoming signal (SIGUSR1 in this case).

<?php

  pcntl_async_signals(TRUE);

  pcntl_signal(SIGUSR1, function($signal) {
    // do something when signal is called
  });

  function everyMinute() {
    // do some important stuff
  }

  $wait = 60;
  $next = 0;

  while (TRUE) {
    $stamp = time();
    do {
      if ($stamp >= $next) { break; }
      $diff = $next - $stamp;
      sleep($diff);
      $stamp = time();
    } while ($stamp < $next);
    
    everyMinute();
    
    $next = $stamp + $wait;
    sleep($wait);
  }

?>

So in this infinite loop do {} while() calculates and adds missing sleep() to ensure that our everyMinute() function is not called too early. Both sleep() functions here are covered so everyMinute() will never be executed before it's time even if process receives multiple SIGUSR1 signals during it's runtime.
kuba at valentine dot dev
пред 13 години
Remember that declaring a tick handler can become really expensive in terms of CPU cycles: Every n ticks the signal handling overhead will be executed. 

So instead of declaring tick=1, test if tick=100 can do the job. If so, you are likely to gain fivefold speed.

As your script might always might miss some signals due to blocking operations like cURL downloads, call pcntl_signal_dispatch() on vital spots, e.g. at the beginning of your main loop.
Жолт Силаѓи
пред 16 години
For PHP >= 5.3.0, instead of declare(ticks = 1), you should now use pcntl_ signal_ dispatch().
webmaster at ajeux dot com
пред 17 години
You cannot assign a signal handler for SIGKILL (kill -9).
rbotzer at yahoo dot com
пред 16 години
Since php >= 5.3 handles Closures, you are now able to define the Callback directly.
Try this:

<?php
declare(ticks = 1);

pcntl_signal(SIGUSR1, function ($signal) {
            echo 'HANDLE SIGNAL ' . $signal . PHP_EOL;
});

posix_kill(posix_getpid(), SIGUSR1);

die;
?>
benjamin at josefus dot /NO+SPAM/ net
21 години пред
Some weird signal interactions going on here. I'm running PHP 4.3.9.

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.

This can be worked around like this:

function handler($signal)
{
    // Ignore this signal
    pcntl_signal($signal, SIG_IGN);

    sleep(10);

    // Reinstall signal handler
    pcntl_signal($signal, __FUNCTION__);
}

I don't see any mention of this behavior in the documentation.
ieure at php dot net
19 години пред
Tip: when using objects, don't set the signal handler from the constructor or even a method called from the constructor - your internal variables will be uninitialised.
RPaseur at NationalPres dot org
пред 15 години
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().

So in other words, the following does not work:

<?php
class Parent {
  /* ... */
  private function do_reap(){
    $p = pcntl_waitpid(-1,$status,WNOHANG);
    if($p > 0){
      echo "\nReaped zombie child " . $p;
    }

   public function run(){
    /* ... */
    pcntl_signal(SIGCHLD,array(&$this,"do_reap"));
    $readable = @stream_select($read,$null,$null,5); // 5 sec timeout
    if($readable === 0){
      // we got a timeout
      $this->do_reap();
   }
}
?>

But this work:

<?php
class Parent {
  /* ... */
  private function do_reap(){
    $p = pcntl_waitpid(-1,$status,WNOHANG);
    if($p > 0){
      echo "\nReaped zombie child " . $p;
    }

   public function run(){
    /* ... */
    pcntl_signal(SIGCHLD,array(&$this,"child_died"));
    $readable = @stream_select($read,$null,$null,5); // 5 sec timeout
    if($readable === 0){
      // we got a timeout
      $this->do_reap();
   }

    private function child_died(){
      $this->do_reap();
    }
}
?>
rob at robertjohnkaper dot com
пред 9 години
A word of caution around the third parameter (restart_syscalls) in pcntl_signal(...).

I kept having a repeated issue where (seemingly randomly) my script would "exit unexpectedly" (_exit_, not crash: the exit code was always 0) while tracking forked children using signal handlers.

It appears that the signal handling is not at fault (indeed, PHP isn't "at fault").  Having "restart_syscalls" set to FALSE seemed to be the problem's cause.

I haven't debugged the issue extensively - except to observe that the issue was highly intermittent and seemed to relate to my use of usleep() in conjunction with restart_syscalls=FALSE.

My theory is the usleep() was wrongly tracking time - as described over here: http://man7.org/linux/man-pages/man2/restart_syscall.2.html

Long story short, I re-enabled (which is the default value) restart_syscalls, and the issue went away.

If you're curious - register_shutdown_function was still being handled correctly - so PHP definitely was _NOT_ crashing.  Interestingly however, my procedural code never "resumed" after the signal was handled.

I do not believe this is a bug.  I believe it was ignorant user error.  YMMV.
нејт на example точка ком
19 години пред
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.

Here's a sample script:

<?
    $allow_exit = true;  // are we permitting exit?
    $force_exit = false; // do we need to exit?

    declare(ticks = 1);
    register_tick_function('check_exit');
    pcntl_signal(SIGTERM, 'sig_handler');
    pcntl_signal(SIGINT, 'sig_handler');

    function sig_handler () {
        global $allow_exit, $force_exit;

        if ($allow_exit)
            exit;
        else
            $force_exit = true;
    }

    function check_exit () {
        global $allow_exit, $force_exit;

        if ($force_exit && $allow_exit)
            exit;
    }

    $allow_exit = false;

    $i = 0;
    while (++$i) {
        echo "still going (${i})\n";
        if ($i == 10)
            $allow_exit = true;

        sleep(2);
    }
?>

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
?>
wally at soggysoftware dot co dot uk
пред 11 години
Looks like php uses  RealTime signals. This means that if one signal is currently processed, then other signals will not be lost.

just for example
<?php
pcntl_signal(SIGHUP, SIG_IGN)
?>

in strace log looks like this

"rt_sigaction(SIGHUP, {SIG_IGN, [], SA_RESTORER|SA_RESTART, 0x7f8caf83cc30}, {SIG_DFL, [], 0}, 8) = 0"

And code for testing

<?php
pcntl_signal(SIGHUP, function($signo){
  echo "1\n";
  sleep(2);
  echo "2\n";
});

while(true){
  sleep(1);
  pcntl_signal_dispatch();
}
?>

Run this and send how many SIGHUP signals as you want. All of them will be processed.

P.S.
I guess "all of them". I couldn't found real signal queue size. Will appreciate if someone points me.
RPaseur at NationalPres dot org
пред 7 години
To be clear, the phrase "pcntl_signal() doesn't stack the signal handlers, but replaces them. " means that you can still have different functions for different signals but only one function per signal.

In other words, this will work as expected

<?php

pcntl_async_signals(true);
pcntl_signal(SIGUSR1,function(){echo "received SIGUSR1";});
pcntl_signal(SIGUSR2,function(){echo "received SIGUSR2";});
posix_kill(posix_getpid(),SIGUSR1);
posix_kill(posix_getpid(),SIGUSR2);

// returns "received SIGUSR1" and then "received SIGUSR2"

?>
aeolian meson на NOSPAM dot blitzeclipse dot com
19 години пред
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'.)

<?php
print("pid= " . posix_getpid() . "\n");
declare(ticks=1);
$arrsignals = array();

function handler($nsig)
{
    global $arrsignals;
    $arrsignals[] = (int)$nsig;
    print("Signal caught and registered.\n");
    var_dump($arrsignals);
}

pcntl_signal(SIGTERM, 'handler');

// Wait for signals from the command-line (just a simple 'kill (pid)').
$n = 15;
while($n)
{
    sleep(1);
    $n--;
}

print("terminated.\n\n");
var_dump($arrsignals);
?>

Dustin
dorogikh dot alexander at gmail dot com
20 години пред
When you are running a script inside of a loop that checks a socket, and it hangs on that checking (Either by flaw or design), it can't handle signals until some data is received.

A suggested workaround would be to use the stream_set_blocking function, or stream_select on the offending reads.
Анонимен
пред 18 години
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.
wm161 at wm161 dot net
19 години пред
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:

<?php

function childFinished($signal)
{
  global $kids;
  $kids--;
  pcntl_waitpid(-1, $status);
}

$kids = 0;
pcntl_signal(SIGCHLD, "childFinished");
for ($i = 0; $i < 1000; $i++)
{
  while ($kids >= 50) sleep(1);
  
  $pid = pcntl_fork();
  if ($pid == -1) die('failed to fork :(');

  /* child process */
  if ($pid == 0) 
  {
    /* do something */
    exit(0);
  }

  /* parent */
  else { $kids++; }
}

/* 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:

<?php

function childFinished($signal)
{
  global $kids;

  while( pcntl_waitpid(-1, $status, WNOHANG) > 0 )
    $kids--;
}

?>
imslushie at hotmaildotcom dot com
19 години пред
This issue occurs in at least PHP 5.1.2.

When a SIGINT is sent via CTRL+C or CTRL+BREAK, the handler is called. If this handler sends a SIGTERM to other children, the signals are not received.

SIGINT can be sent via posix_kill() and it work exactly as expected-- This only applies when initiated via a hard break.
aeolianmeson at NOSPAXM dot blitzeclipse dot com
19 години пред
Process handling is not available when using a blocking socket! Bear this in mind.
zenyatta22 at hotmail
пред 16 години
I found out then when you use pcntl_signal in a 'deamon' script and you run it before you fork childs it does not work as expected.

instead you need to use pcntl_signal in the child code of the child you are forking

and if you want to cach signals for the 'deamon' part you should use pcntl_signal in the parent code.
daniel[на]lorch.cc
figroc at gmail dot com
There are two documents I consider reading:

  Unix Signals Programming
  http://users.actcom.co.il/~choo/lupg/tutorials/

  Beej's Guide to Unix Interprocess Communication 
  http://www.ecst.csuchico.edu/~beej/guide/ipc/

Also, have a look at the manpage:

  http://www.mcsr.olemiss.edu/cgi-bin/man-cgi?signal+5
Brainiac361
21 години пред
I ran into an interesting problem. CLI 4.3.10 Linux

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.
ewout dot NOSPAM dot zigterman at custosys dot nl
пред 17 години
you should use following code to prevent situation described by anxious2006 (children exit near simultaneously)

public function sig_handler($signo){
  switch ($signo) {
    case SIGCLD:
      while( ( $pid = pcntl_wait ( $signo, WNOHANG ) ) > 0 ){
        $signal = pcntl_wexitstatus ( $signo );
      }
      break;
  }
}
anxious2006
пред 18 години
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.
Холгер Хес
пред 13 години
static class method to get the name of a process signal as string:

(self::$processSignalDescriptions is used to cache the results)

<?php
    public static function getPOSIXSignalText($signo) {

        try {
            if (is_null(self::$processSignalDescriptions)) {

                self::$processSignalDescriptions = array();

                $signal_list = explode(" ", trim(shell_exec("kill -l")));
                foreach ($signal_list as $key => $value) {
                    self::$processSignalDescriptions[$key+1] = "SIG".$value;
                }
            }

            return isset(self::$processSignalDescriptions[$signo])?self::$processSignalDescriptions[$signo]:"UNKNOWN";

        } catch (Exception $e) {}

        return "UNKNOWN";
    }
?>
S. Kr@cK
пред 11 години
<?php
pcntl_signal(SIGTERM,  function($signo) {
    echo "\n This signal is called. [$signo] \n";
    Status::$state = -1;
});

class Status{
    public static $state = 0;
}

$pid = pcntl_fork();
if ($pid == -1) {
    die('could not fork');
}

if($pid) {
    // parent
} else {
    while(true) {
        // Dispatching...
        pcntl_signal_dispatch();
        if(Status::$state == -1) {
            // Do something and end loop.
            break;
        }
        
        for($j = 0; $j < 2; $j++) {
            echo '.';
            sleep(1);
        }
        echo "\n";
    }
    
    echo "Finish \n";
    exit();
}

$n = 0;
while(true) {
    $res = pcntl_waitpid($pid, $status, WNOHANG);
    
    // If the child process ends, then end the main process.
    if(-1 == $res || $res > 0)
        break;
    
    // Send a signal after 5 seconds..
    if($n == 5)
        posix_kill($pid, SIGTERM);
    
    $n++;
    
    sleep(1);
}
?>
seong на respice точка net
пред 17 години
Be careful, when using an object in your callback. It seems this elevates the reference count. You may not want it to happen in repeated child processes.
фрустриран од застарени документи
12 години пред
Please be aware that declaring ticks or calling pcntl_signal_dispatch() in 5.3 and later is required to make pcntl_signal do anything useful. I wish the documentation made this more clear.
На оваа страница

Автоматски outline од активната документација.

Насловите ќе се појават тука по вчитување.

Попрегледно читање

Примерите, changelog табелите и user notes се визуелно издвоени за да не се губат во долгата содржина.

Брз совет Користи го outline-от Скокни директно на главните секции од активната страница.
Извор Оригиналниот линк останува достапен Кога ти треба целосен upstream context, отвори го PHP.net во нов tab.