2013-04-06 90 views
2

虽然编写了一个看似简单的Qt应用程序部分,它将运行一个子进程并从其标准输出中读取数据,但我偶然发现了一个让我感到困惑的问题。该应用程序应改为从子数据块(原始视频帧)并对其进行处理,他们到达:QProcess因无明显原因而死亡

  1. 开始QProcess中
  2. 收集数据,直到有一帧
  3. 过程框架是不够
  4. 回到步骤2

当时的想法是使用信号和槽实施加工循环 - 这可能看起来傻的简单,精简的例子,我在下面提供,但似乎entirel在原始申请的框架内合理。所以在这里,我们去:

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 - 即父进程关闭了管道。但我有绝对没有想法在哪里和为什么会发生这种情况。还有其他人吗?

回答

0

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

我找不到引用的“Qt任务217111”了,但是他们的Jira中有几个关于waitForReadyRead的报告不像用户期望的那样工作,参见例如。 QTBUG-9529

我想把它带到Qt的“兴趣”邮件列表中,并保持清除waitFor...方法。我同意他们的文件值得更新。

+0

整洁的建议,但不幸的是我没有从strace那里得到很多有用的信息。然而,它确实证实管道确实被关闭。我开始相信我可能没有做错任何事情,并且问题是由于Qt的一些内部竞争条件造成的。 – 2013-04-08 22:52:59

+0

这是一个危险的结论 - 作为一个试图用Posix系统调用重写QProcess只是为了好玩的人,我确实承认它可能是非常诱人的:)。当然,你可以在''strace''的输出中看到''close()''。如果这还不足以弄清楚为什么会发生这种情况,那么您总是可以在像''gdb''这样的调试器下运行整个事件,并在''close''中设置一个断点/ catchpoint。 – 2013-04-09 14:38:18

+0

我在Qt的代码中找到了一些东西,我确实知道close()被调用的地方。现在......哈哈哈!我正要将一个链接粘贴到一个关于Qt-interest的电子邮件线索上,这个线索可能与这个问题有关,并且刚刚认识到它是你开始线程的。 :-)好吧,无论如何,[这里](http://lists.qt.nokia.com/public/qt-interest/2009-May/006341.html)都是链接。我对发生的事情的印象是,主事件循环以某种方式得到关于在管道上等待的新数据的虚假通知,只是发现没有真正存在的东西。 – 2013-04-10 15:38:27