QProcess死因没有明显的原因

时间:2013-04-06 01:48:11

标签: qt pipe qprocess

在编写Qt应用程序的一个看似简单的部分时,它会运行一个子进程并从其标准输出中读取数据,但我偶然发现了一个让我感到困惑的问题。应用程序应该从子进程读取数据块(原始视频帧)并在它们到达时处理它们:

  1. 启动QProcess
  2. 收集数据,直到有足够的一帧
  3. 处理框架
  4. 返回第2步
  5. 这个想法是使用信号和插槽来实现处理循环 - 这在我在下面提供的简单的,简化的示例中可能看起来很愚蠢,但在原始应用程序的框架内似乎完全合理。所以我们走了:

    app::app() {
      process.start("cat /dev/zero");
      buffer = new char[frameLength];
      connect(this, SIGNAL(wantNewFrame()), SLOT(readFrame()), Qt::QueuedConnection);
      connect(this, SIGNAL(frameReady()), SLOT(frameHandler()), Qt::QueuedConnection);
      emit wantNewFrame();
    }
    

    我从这里开始一个简单的过程(cat /dev/zero),这样我们就可以确信它不会耗尽数据。我还建立了两个连接:一个在需要帧时启动读取,另一个在帧到达时调用数据处理功能。请注意,这个简单的示例在单个线程中运行,因此连接将成为排队类型以避免无限递归。 wantNewFrame()信号启动第一帧的获取;当控件返回到事件循环时,它会被处理。

    bool app::readFrame() {
      qint64 bytesNeeded = frameLength;
      qint64 bytesRead = 0;
      char* ptr = buffer;
      while (bytesNeeded > 0) {
        process.waitForReadyRead();
        bytesRead = process.read(ptr, bytesNeeded);
        if (bytesRead == -1) {
          qDebug() << "process state" << process.state();
          qDebug() << "process error" << process.error();
          qDebug() << "QIODevice error" << process.errorString();
          QCoreApplication::quit();
          break;
        }
        ptr += bytesRead;
        bytesNeeded -= bytesRead;
      }
      if (bytesNeeded == 0) {
        emit frameReady();
        return true;
      } else
        return false;
    }
    

    读取框架:基本上,我只是在数据到达时将数据填充到缓冲区中。最后的frameReady()信号宣告帧已准备就绪,从而导致数据处理功能运行。

    void app::frameHandler() {
      static qint64 frameno = 0;
      qDebug() << "frame" << frameno++;
      emit wantNewFrame();
    }
    

    一个简单的数据处理器:它只计算帧数。完成后,它会发出wantNewFrame()以重新开始阅读周期。

    就是这样。为了完整起见,我还将在此处发布头文件和main()。

    app.h:

    #include <QDebug>
    #include <QCoreApplication>
    #include <QProcess>
    
    class app : public QObject
    {
    Q_OBJECT
    public:
      app();
      ~app() { delete[] buffer; }
    
    signals:
      void wantNewFrame();
      void frameReady();
    
    public slots:
      bool readFrame();
      void frameHandler();
    
    private:
      static const quint64 frameLength = 614400;
      QProcess process;
      char* buffer;
    };
    

    main.cpp中:

    #include "app.h"
    
    int main(int argc, char** argv)
    {
        QCoreApplication coreapp(argc, argv);
        app foo;
        return coreapp.exec();
    }
    

    现在是奇怪的部分。这个程序处理一个随机数量的帧就好了(我已经看到了从十五到一千多的任何东西),但最终停止并抱怨QProcess崩溃了:

    $ ./app
    frame 1
    ...
    frame 245 
    frame 246 
    frame 247 
    process state 0 
    process error 1 
    QIODevice error "Process crashed" 
    

    进程状态0表示“未运行”,进程错误1表示“崩溃”。我调查了它,发现子进程收到了一个SIGPIPE - 即父进程关闭了它的管道。但我绝对没有关于这种情况发生的原因和原因。还有其他人吗?

1 个答案:

答案 0 :(得分:0)

代码有点奇怪(不使用readyRead信号,而是依赖于延迟信号/插槽)。正如您在讨论中指出的那样,您已经看到thread on the qt-interest ML我询问过类似问题的地方。我刚刚意识到我当时也使用了QueuedConnection。我无法解释为什么它是错的 - 在我看来,排队的信号“应该有效”。盲目的是,Qt的实现使用的invokeMethod以某种方式与您的信号传递竞争,以便在Qt有机会处理数据之前清空读取缓冲区。这意味着Qt将最终读取零字节并(正确地)将其解释为EOF,从而关闭管道。

我找不到引用的“Qt任务217111”了,但是他们的Jira中有一些关于waitForReadyRead无法按照用户期望工作的报告,请参阅例如QTBUG-9529

我会把它带到Qt的“兴趣”邮件列表中,并且不要使用waitFor...系列方法。我同意他们的文件值得更新。