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

来源:互联网 发布:智能开关 app 源码 编辑:程序博客网 时间:2024/05/20 03:45

因业务需要用到了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....");        ...      }


0 0
原创粉丝点击