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....");
        ...
      }


发布了23 篇原创文章 · 获赞 6 · 访问量 12万+
展开阅读全文

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 大白 设计师: CSDN官方博客

分享到微信朋友圈

×

扫一扫,手机浏览