略微加速

略速 - 互联网笔记

pcntl_fork多进程僵尸进程的问题

2016-06-01 leiting (3082阅读)

标签 PHP

http://blog.csdn.net/system1024/article/details/51162372

因业务需要用到了pcntl_fork 处理多客户端连接处理数据的需求

但测试下来出现一个问题:

fork 之后, 若等待子进程返回, 那么程序就会阻塞, 不等待子进程返回, 则会出现僵尸进程

$obj = new service('127.0.0.1', 50000);  
$obj->run();  
  
class service {  
  
    private $socket_id;  
    private $socket_cid;  
    private $pid;  
  
    function __construct($host, $port) {  
        $this->socket_id = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);  
        if (!$this->socket_id) {  
            $this->logs("create socket error");  
            return false;  
        }  
        if (!socket_bind($this->socket_id, $host, $port)) {  
            $this->logs(socket_strerror(socket_last_error()));  
            socket_close($this->socket_id);  
            return false;  
        }  
        if (!socket_listen($this->socket_id)) {  
            $this->logs(socket_strerror(socket_last_error()));  
            socket_close($this->socket_id);  
            return false;  
        }  
    }  
  
    public function run() {  
        if (!$this->socket_id) {  
            $this->logs("create socket error");  
            return false;  
        }  
        $this->logs("Starting...");  
        while (true) {  
            $this->socket_cid = socket_accept($this->socket_id);  
            $ip = "";  
            socket_getpeername($this->socket_cid, $ip);  
            $this->logs("Client IP: {$ip}");  
            if (!is_resource($this->socket_cid)) {  
                $this->logs(socket_strerror(socket_last_error()));  
                socket_close($this->socket_id);  
                return false;  
            }  
            $this->pid = pcntl_fork();  
            if ($this->pid == -1) {  
                $this->logs(socket_strerror(socket_last_error()));  
                socket_close($this->socket_cid);  
                return false;  
            } else if ($this->pid) {  
                $status = 0;  
                $s = pcntl_wait($status, WNOHANG); //等待子进程中断,防止子进程成为僵尸进程, 但这样会阻塞, 意思是只能处理一个客户端连接, 多个进入排队  
                //$s = pcntl_waitpid(0, $status, WUNTRACED); //如果不等待, 则子进程处理完之后, 没有对其进行处理, 会成为僵尸进程  
                $this->logs("Status: " . $status . ", pcntl_waitpid: " . $s);  
            } elseif ($this->pid == 0) { //child process  
                $k = 0;  
                while (true) {  
                    if ($data = socket_read($this->socket_cid, 1024)) {  
                        $this->logs("Read Data: {$data}");  
                        $flag = socket_write($this->socket_cid, date('Y-m-d H:i:s') . '|已收到');  
                        $this->logs("Write Result: {$flag}");  
                    }  
                    sleep(1);  
                    $k++;  
                    $this->logs("Wait...\t" . date('Y-m-d H:i:s'));  
                    if ($k > 30) {  
                        break;  
                    }  
                }  
                socket_close($this->socket_cid);  
                exit(1);  
            }  
        }  
        socket_close($this->socket_id);  
    }  
  
    /** 
     * 打日志 
     * @param string $msg 
     * @return int 
     */  
    private function logs($msg) {  
        $logs_filename = LOGS_PATH;  
        if (!file_exists($logs_filename)) {  
            @mkdir($logs_filename, 0777, true);  
        }  
        $logs_filename .= date('j') . '.log';  
        $logs_data = date('[H:i:s]') . " {$msg}\n";  
        return file_put_contents($logs_filename, $logs_data, FILE_APPEND);  
    }  
  
    function __destruct() {  
        if (is_resource($this->socket_id)) {  
            socket_close($this->socket_id);  
        }  
        if (is_resource($this->socket_cid)) {  
            socket_close($this->socket_cid);  
        }  
    }  
  
}

在网上搜了半天, 这位仁兄的解决办法是对的(http://blog.csdn.net/e421083458/article/details/22186475), 就是增加信号处理,

在socket accept之前加上 pcntl_signal(SIGCHLD, SIG_IGN); //如果父进程不关心子进程什么时候结束,子进程结束后,内核会回收。 

也就是在run方法开始处 :)


北京半月雨文化科技有限公司.版权所有 京ICP备12026184号-3