/**
 * 运行指令
 * @param callable|null $callback
 * @return self
 * @throws \RuntimeException
 * @throws ProcessFailedException
 */
public function mustRun($callback = null)
{// 运行指令
    if ($this->isSigchildEnabled() && !$this->enhanceSigchildCompatibility) {
        // 如果是个 单独进程 可以,但是 单独预习的环境不行
        throw new \RuntimeException('This PHP has been compiled with --enable-sigchild. You must use setEnhanceSigchildCompatibility() to use this method.');
    }// 这里异常

    if (0 !== $this->run($callback)) {// 如果 执行 回调 函数 结果 不是 0
        throw new ProcessFailedException($this);// 抛出异常
    }

    return $this;// 返回对象本身
}// 两个特殊情况异常检测 外包执行,执行完成后,返回对象本身

/**
 * 启动进程并写到 STDIN 输入后返回。
 * @param callable|null $callback
 * @throws \RuntimeException
 * @throws \RuntimeException
 * @throws \LogicException
 */
public function start($callback = null)
{// 启动进程 并写到 STDIN 输入后返回
    if ($this->isRunning()) {// 如果当前的正在运行,抛出异常 进程执行中
        throw new \RuntimeException('Process is already running');
    }
    if ($this->outputDisabled && null !== $callback) {// 如果禁止输出 并且 回调函数不为空
        throw new \LogicException('Output has been disabled, enable it to allow the use of a callback.');
    }// 必须允许 回调函数存在

    $this->resetProcessData();//重置进程 数据
    $this->starttime = $this->lastOutputTime = microtime(true);// 进程开始时间
    $this->callback  = $this->buildCallback($callback);// 创建回调函数
    $descriptors     = $this->getDescriptors();// 获取描述

    $commandline = $this->commandline;// 命令行 命令 进行转移

    if ('\\' === DS && $this->enhanceWindowsCompatibility) {// 如果 分隔符 为 \ 并且 允许执行窗口命令
        $commandline = 'cmd /V:ON /E:ON /C "(' . $commandline . ')';// 通过 cmd 命令执行
        foreach ($this->processPipes->getFiles() as $offset => $filename) {// 进程管道获取文件
            $commandline .= ' ' . $offset . '>' . Utils::escapeArgument($filename);// 进行命令组合
        }
        $commandline .= '"';// 组合成为新命令

        if (!isset($this->options['bypass_shell'])) {// 如果没有设置 bypass_shell 的方式
            $this->options['bypass_shell'] = true;
        }
    }

    // 通过进程 函数 进行 执行,可以通过php 来执行 命令行程序
    $this->process = proc_open($commandline, $descriptors, $this->processPipes->pipes, $this->cwd, $this->env, $this->options);

    if (!is_resource($this->process)) {// 如果进程执行完成 不是 资源句柄
        throw new \RuntimeException('Unable to launch a new process.');// 进程有问题,哈哈
    }
    $this->status = self::STATUS_STARTED;// 状态 等于 当前程序执行状态

    if ($this->tty) {// 如果是这个鬼东西
        return;
    }

    $this->updateStatus(false);// 更新状态
    $this->checkTimeout();// 更新 输出时间
}

/**
 * 重启进程
 * @param callable|null $callback
 * @return Process
 * @throws \RuntimeException
 * @throws \RuntimeException
 */
public function restart($callback = null)
{// 重启进程
    if ($this->isRunning()) {// 异常抛出
        throw new \RuntimeException('Process is already running');
    }

    $process = clone $this;// 复制一份
    // 在 类实例化 普通的 复制是不行的,必须是 clone 才是真正的复制,跟变量不一样

    $process->start($callback);// 重启

    return $process;// 返回 复制的进程
}

/**
 * 等待要终止的进程
 * @param callable|null $callback
 * @return int
 */
public function wait($callback = null)
{// 等待要终止的进程
    $this->requireProcessIsStarted(__FUNCTION__);// 当前函数进入到 请求进程 是否开始的判读

    $this->updateStatus(false);//更新 状态
    if (null !== $callback) {// 如果 为空
        $this->callback = $this->buildCallback($callback);// 回调函数重见
    }

    do {// 执行
        $this->checkTimeout();// 跳出
        $running = '\\' === DS ? $this->isRunning() : $this->processPipes->areOpen();// 根据不同的情形 判断是否 running
        $close   = '\\' !== DS || !$running;
        $this->readPipes(true, $close);
    } while ($running);// 当循环

    while ($this->isRunning()) {// 当运行 休息 1000秒
        usleep(1000);
    }

    if ($this->processInformation['signaled'] && $this->processInformation['termsig'] !== $this->latestSignal) {
        throw new \RuntimeException(sprintf('The process has been signaled with signal "%s".', $this->processInformation['termsig']));
    }//终止进程

    return $this->exitcode;// 终止完成
}