× 警告!旧版文档已经暂停维护,请查看新版文档。点击前往新版文档

Process->exec

执行一个外部程序,此函数是exec系统调用的封装。

bool Process->exec(string $execfile, array $args)
  • $execfile指定可执行文件的绝对路径,如 "/usr/bin/python"
  • $args是一个数组,是exec的参数列表,如 array('test.py', 123),相当于python test.py 123

执行成功后,当前进程的代码段将会被新程序替换。子进程蜕变成另外一套程序。父进程与当前进程仍然是父子进程关系。

父进程与新进程之间可以通过标准输入输出进行通信,必须启用标准输入输出重定向。

$execfile必须使用绝对路径,否则会报文件不存在错误
由于exec系统调用会使用指定的程序覆盖当前程序,子进程需要读写标准输出与父进程进行通信
如果未指定redirect_stdin_stdout = true,执行exec后子进程与父进程无法通信

调用示例

$process = new \Swoole\Process(function (\Swoole\Process $childProcess) {
    // 不支持这种写法
    // $childProcess->exec('/usr/local/bin/php /var/www/project/yii-best-practice/cli/yii 
    t/index -m=123 abc xyz');

     // 封装 exec 系统调用
     // 绝对路径
     // 参数必须分开放到数组中
    $childProcess->exec('/usr/local/bin/php', ['/var/www/project/yii-best-practice/cli/yii', 
    't/index', '-m=123', 'abc', 'xyz']); // exec 系统调用
});
$process->start(); // 启动子进程

父进程与exec子进程使用管道进行通信:

// exec - 与exec进程进行管道通信
use Swoole\Process;
$process = new Process(function (Process $worker) {
    $worker->exec('/bin/echo', ['hello']);
    $worker->write('hello');
}, true); // 需要启用标准输入输出重定向
$process->start();
echo "from exec: ". $process->read(). "\n";

执行 shell 命令

exec方法与PHP提供的shell_exec不同,它是更底层的系统调用封装。如果需要执行一条shell命令,请使用以下方法:

$worker->exec('/bin/sh', array('-c', "cp -rf /data/test/* /tmp/test/"));

  • clownfish

    能写个例子吗?用swoole_process->exec 创建了一个进程,如何跟子进程通讯

  • 晚安

    @clownfish 接下来的write和read操作就是和子进程通信的操作了。

  • 广训

    请问: 说该命令是对系统命令exec()的封装,但是执行类似的: ./yii default/a 2016-10-01 2016-12-01 >>/Users/zhgxun/Public/html/logs/20170326/18054/18071_out.txt 2>>/Users/zhgxun/Public/html/logs/20170326/18054/18071_error.txt 系统命令时,使用swoole_process->exec() 正常的输出不能输出至正确文件或者错误文件,这个该怎么理解???

  • Ben

    @clownfish,exec 是打开一个外部程序,并不是创建进程

  • DavidYANXW

    exec后,当前进程和父进程仍然是父子进程关系。 父子进程通信方式有管道和队列。

  • DavidYANXW

    父进程和exec进程通过管道通信。 // 父进程读取,exec进程写入 echo "Father|From Worker: ".$process->read(); // exec进程: $buffer = "message from exec".PHP_EOL; // 方式1:echo输出 echo $buffer; // 方式2:fwrite标准输出 fwrite(STDOUT, $buffer);

    // 父进程写入,exec进程读取 // 父进程 $process->write("hello worker[$pid]\n"); // exec进程 buffer = "get message from master:".fgets(STDIN); file_put_contents("/tmp/debug.log", getmypid()."||".$buffer, FILE_APPEND);

  • 15573742616

    public function public_reload() { $log_path = self::LOG_DEBUG; // 检查服务是否开启 $str = $this->chechPublicService(); if ($str) {// 获取pid if (!$server_pid = $this->getValFromString($str)) { return json(['code' => 0, 'msg' => '没有匹配到pid,请检查代码是否获取pid的函数逻辑有误']); } //return $server_pid; if (file_exists($log_path)) { unlink($log_path); } // 杀死进程 $process = new \Swoole\Process(function(\Swoole\Process $c_process)use($log_path, $server_pid) { // 检查公众号的服务器是否还在运行 $c_process->exec('/bin/sh', ['-c', "kill -9 $server_pid >/dev/null 1>$log_path"]); $c_process->exit(0); }); $process->start(); // 检查是否成功运行杀死命令。 // 之所以要轮询,是因为要确保上一个子进程执行完成(写入文件即为执行完成)

            echo ('nan dao zhi jie yue guo, buz zou ma ' . PHP_EOL);
            while (true) {
                echo ('jian cha sha si fuwu' . PHP_EOL);die;
                if (file_exists($log_path)) {
                    var_dump('exit:');die;
                    $check = $this->chechPublicService();
    
                    if ($check) {
                        return json(['code' => 0, 'msg' => '公众号定时服务没有成功杀死,请到服务器查看原因']);
                    }
                    break;
                }
            }
        }
    
        /*chmod($root, 0777);
        FileHelper::simple_write(self::LOG_DEBUG, $str);*/
        //重启公众号服务器
        while(true) {
            if (file_exists($log_path)) {
                unlink($log_path);
                $process = new \Swoole\Process(function(\Swoole\Process $c_process) use($log_path) {
                    //chmod('/data/www/template_msg_push/job/app_job/we_chat/controller/PublicTemplateSend.php', 0777);
                    // 检查公众号的服务器是否还在运行
                    $c_process->exec('/bin/sh', ['-c', "nohup php /data/www/template_msg_push/job/app_job/we_chat/controller/PublicTemplateSend.php >$log_path 2>/data/www/template_msg_push/runtime/PublicTemplateSend.txt &"]);
                    $c_process->exit(0);
                });
                $process->start();
                break;
            }
        }
        // 等待子进程退出,并回收
        \Swoole\Process::signal(SIGCHLD, function($sig) {
            //必须为false,非阻塞模式
            while($ret =  \Swoole\Process::wait(false)) {
                // 子进程退出,则执行下面逻辑
                echo 'success catch child process';
            }
        });
        // 查看重启的log文件并返回,注意这里并不能判断是否重启成功,要看返回的具体信息。
        $log_arr = $this->read($log_path);
        return json(['code' => 1, 'msg' => $log_arr]);
    }
    

    各位讲讲这是个什么逻辑,前面的断点都没有运行,子进程有退出,程序直接跑回收子进程的process::wait();然后就直接执行process::wait()后面的返回了,先不说,其它都不说,明明设置了断点,断点后的回收子进程执行我们不讲,回收子进程后面的代码逻辑还执行,而且前面的逻辑都没有走,这是什么鬼?

  • 15573742616

    process-exec()这个函数是通过子进程去执行脚本命令(这个函数里的脚本是一个异步,完全与主程序流程脱钩的),所以主程序流程里也不能直接用$process->exit(0)退出,然后回收,因为无法确保process->exec()是否运行完成,结果就是,每运行一次,服务器就会多一个进程,不会被释放;不知道有没有什么方案解决

  • 15573742616

    用process::kill(pid) 杀死进程,静态方法注意

  • 18201842514

    好像用的是C的execv函数执行
    https://linux.die.net/man/3/execv
    The exec() family of functions replaces the current process image with a new process image.

    调用$process->exec后,原来的子进程被替换掉了,所以只有一个子进程

  • 18201842514

    该方法的好处是创建一个全新的php环境
    https://github.com/swoole/swoole-src/issues/2197#issuecomment-446547230

  • 夢X啦哆

    exec() 的时候报 No such file or directory[2],但是我的可执行文件却是可以找得到的,是怎么回事呢?