Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Subsequent pcntl_signal signals not kicking off handler

Lemme begin by giving a basic description of the code I have. I start off with a main parent process (NOTE: I am not showing all functions for simplicity. Let me know if you need me to expand at any point):

declare(ticks=1);
pcntl_signal(SIGHUP, array('forker', 'restartSignalHandler'));
if(forker_is_not_running()){
    new Forker();
}
class Forker {
    private $active_forks = array();
    private $parent_pid = null;

    public function __construct(){
        $this->parent_pid = getmypid();
        $this->create_fork();
        $this->wait_for_active();
    }

    public function wait_for_active(){
        while(!empty($this->active_forks)){
            foreach($this->active_forks as $k=>$fork){
                if($this->fork_no_longer_running($fork)){
                    unset($this->active_forks[$k]);
                }
            }
        }
    }

    // Pseudo code
    public function fork_no_longer_running($pid){
        // return true if 'ps -elf | grep $pid' doesn't returns only the grep command
        // else return false (aka the fork is still running)
    }

    public function create_fork(){
        $pid = pcntl_fork();
        if($pid == -1){
            posix_kill($this->parent_pid, SIGTERM);
        } else if($pid){
            // add the pid to the current fork
            $this->active_forks[] = $pid;
        } else {
            // Run our process
            pcntl_exec('/usr/bin/php', array('/domain/dev/www/index.php','holder','process'));
            exit(0);
        }
    }

    public function restartSignalHandler(){
        $forks = $this->active_forks;
        foreach($forks as $pid){
            $this->create_fork();
            posix_kill($pid, SIGINT);
        }
    }
}

class holder {
    public function process(){
        $x = new Processor();
    }
}

class Processor {
    public function __construct(){
        pcntl_signal(SIGINT, array($this, "shutdownSignalHandler"));
    }
    public function shutdownSignalHandler(){
        echo "Shutting down";
        exit;
    }
}

Here is what is happening:

  1. I start my script and I properly get the processes (e.g. Parentpid:2, childpid:3)
  2. I then send the parent a SIGHUP signal and it properly kills and starts a new child process (e.g. Parentpid: 2, childpid:4)
  3. I then send the parent a 2nd SIGHUP signal and it properly tries and adds a new child process, but it refuses to kill the 2nd childpid. (e.g. Parentpid:2, undyingchildpid:4, newchildpid:5)

Lemme know if that needs more details/doesn't make sense. I can't figure out why the first time it would properly kill the children, but the 2nd time it doesn't.

The even WEIRDER part is that when I change it so that I change my restart handler so that it keeps trying to kill the child with a SIGINT, it fails each time, but when I send it a SIGKILL command it kills the child process:

if($time_passed > 60){
    posix_kill($pid, SIGKILL);
}

I need the child to be able to be killed by SIGINT in order to handle it properly. I don't want to just SIGKILL it. Is there any reason as to why the 2nd time around SIGINT wouldn't work, but SIGKILL will?

like image 914
Aram Papazian Avatar asked Mar 12 '12 21:03

Aram Papazian


1 Answers

First of all, you do not need to fork. Your code does an exec inside the child, you could basically just run the exec whithout forking, and the OS will spawn your command as a child. If you want to use fork, just include the file in the child rather than execing.

public function create_fork(){
    //no need to actually fork!
    pcntl_exec('/usr/bin/php', array('/domain/dev/www/index.php','holder','process'));
}

//if you want to fork, better do it like this : 


public function create_fork(){
    $pid = pcntl_fork();
    if($pid == -1){
        posix_kill($this->parent_pid, SIGTERM);
    } else if($pid){
        // add the pid to the current fork
        $this->active_forks[] = $pid;
    } else {
       // Run our process
       include '/domain/dev/www/index.php';
       SomeClass::someMethod();
       exit(0);
    }
}

Also, when using fork, you need to waitpid for the children. So, in your code you need to insert something like :

//somewhere in a loop : 
$pidOfExittedChild = pcntl_waitpid (-1, $status, WNOHANG);
if ($pidOfExittedChild) {
    //a child has exitted, check its $status and do something
}

Check more at : http://php.net/manual/en/function.pcntl-waitpid.php

like image 147
capi Avatar answered Oct 18 '22 15:10

capi