QProcess因无明显原因而死亡
虽然编写了一个看似简单的Qt应用程序部分,它将运行一个子进程并从其标准输出中读取数据,但我偶然发现了一个让我感到困惑的问题。该应用程序应改为从子数据块(原始视频帧)并对其进行处理,他们到达:QProcess因无明显原因而死亡
- 开始QProcess中
- 收集数据,直到有一帧
- 过程框架是不够
- 回到步骤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 - 即父进程关闭了管道。但我有绝对没有想法在哪里和为什么会发生这种情况。还有其他人吗?
该代码有点奇怪(不使用readyRead
信号,而是依靠延迟信号/插槽)。正如你在讨论中指出的那样,你已经看到了我问过类似问题的thread on the qt-interest ML。我刚刚意识到我当时也使用了QueuedConnection
。我无法解释为什么它是错的 - 在我看来,排队的信号“应该起作用”。盲目的一点是,Qt实现中使用的invokeMethod
以某种方式与您的信号传递进行竞争,以便在Qt有机会处理数据之前清空读取缓冲区。这将意味着Qt最终将读取零字节并且(正确)将其解释为关闭管道的EOF
。
我找不到引用的“Qt任务217111”了,但是他们的Jira中有几个关于waitForReadyRead
的报告不像用户期望的那样工作,参见例如。 QTBUG-9529。
我想把它带到Qt的“兴趣”邮件列表中,并保持清除waitFor...
方法。我同意他们的文件值得更新。
整洁的建议,但不幸的是我没有从strace那里得到很多有用的信息。然而,它确实证实管道确实被关闭。我开始相信我可能没有做错任何事情,并且问题是由于Qt的一些内部竞争条件造成的。 – 2013-04-08 22:52:59
这是一个危险的结论 - 作为一个试图用Posix系统调用重写QProcess只是为了好玩的人,我确实承认它可能是非常诱人的:)。当然,你可以在''strace''的输出中看到''close()''。如果这还不足以弄清楚为什么会发生这种情况,那么您总是可以在像''gdb''这样的调试器下运行整个事件,并在''close''中设置一个断点/ catchpoint。 – 2013-04-09 14:38:18
我在Qt的代码中找到了一些东西,我确实知道close()被调用的地方。现在......哈哈哈!我正要将一个链接粘贴到一个关于Qt-interest的电子邮件线索上,这个线索可能与这个问题有关,并且刚刚认识到它是你开始线程的。 :-)好吧,无论如何,[这里](http://lists.qt.nokia.com/public/qt-interest/2009-May/006341.html)都是链接。我对发生的事情的印象是,主事件循环以某种方式得到关于在管道上等待的新数据的虚假通知,只是发现没有真正存在的东西。 – 2013-04-10 15:38:27