php pcntl_fork 多进程僵尸进程的问题

因业务需要用到了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方法开始处 :)


    public function run() {
        if (!$this->socket_id) {
            $this->logs("create socket error");
            return false;
        }
        pcntl_signal(SIGCHLD, SIG_IGN);
        $this->logs("Starting....");
        ...
      }


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值