Qt :进程

在 Qt 中,我们使用QProcess来表示一个进程。这个类可以允许我们的应用程序开启一个新的外部程序,并且与这个程序进行通讯。

//!!! Qt5
QString program = "C:/Windows/System32/cmd.exe";
QStringList arguments;
arguments << "/c" << "dir" << "C:\\";
QProcess *cmdProcess = new QProcess;
QObject::connect(cmdProcess, &QProcess::readyRead, [=] () {
    QTextCodec *codec = QTextCodec::codecForName("GBK");
    QString dir = codec->toUnicode(cmdProcess->readAll());
    qDebug() << dir;
});
cmdProcess->start(program, arguments);

这是一段 Qt5 的程序,并且仅能运行于 Windows 平台。简单来说,这段程序通过 Qt 开启了一个新的进程,这个进程相当于执行了下面的命令:

C:\\Windows\\System32\\cmd.exe /c dir C:\\

前面我们所说的信号槽机制,类似于前面我们介绍的QNetworkAccessManager,都是异步的。与QNetworkAccessManager不同在于,QProcess提供了同步函数:

  • waitForStarted():阻塞到进程开始;
  • waitForReadyRead():阻塞到可以从进程的当前读通道读取新的数据;
  • waitForBytesWritten():阻塞到数据写入进程;
  • waitForFinished():阻塞到进程结束;

注意,在主线程(调用了QApplication::exec()的线程)调用上面几个函数会让界面失去响应。

普通的信号槽机制等,并不适用于进程间的相互通信(Inter-Process Communication,IPC)。进程是操作系统的基本调度单元,因此,进程间交互不可避免与操作系统的实现息息相关。

Qt 提供了四种进程间通信的方式:

  1. 使用共享内存(shared memory)交互:这是 Qt 提供的一种各个平台均有支持的进程间交互的方式。
  2. TCP/IP:其基本思想就是将同一机器上面的两个进程一个当做服务器,一个当做客户端,二者通过网络协议进行交互。除了两个进程是在同一台机器上,这种交互方式与普通的 C/S 程序没有本质区别。Qt 提供了 QNetworkAccessManager 对此进行支持。
  3. D-Bus:freedesktop 组织开发的一种低开销、低延迟的 IPC 实现。Qt 提供了 QtDBus 模块,把信号槽机制扩展到进程级别(因此我们前面强调是“普通的”信号槽机制无法实现 IPC),使得开发者可以在一个进程中发出信号,由其它进程的槽函数响应信号。
  4. QCOP(Qt COmmunication Protocol):QCOP 是 Qt 内部的一种通信协议,用于不同的客户端之间在同一地址空间内部或者不同的进程之间的通信。目前,这种机制只用于 Qt for Embedded Linux 版本。

通用的 IPC 实现大致只有共享内存和 TCP/IP 两种。

Qt 使用QSharedMemory类操作共享内存段。我们可以把QSharedMemory看做一种指针,这种指针指向分配出来的一个共享内存段。而这个共享内存段是由底层的操作系统提供,可以供多个线程或进程使用。因此,QSharedMemory可以看做是专供 Qt 程序访问这个共享内存段的指针。同时,QSharedMemory还提供了单一线程或进程互斥访问某一内存区域的能力。当我们创建了QSharedMemory实例后,可以使用其create()函数请求操作系统分配一个共享内存段。如果创建成功(函数返回true),Qt 会自动将系统分配的共享内存段连接(attach)到本进程。

前面我们说过,IPC 离不开平台特性。作为 IPC 的实现之一的共享内存也遵循这一原则。有关共享内存段,各个平台的实现也有所不同:

  • Windows:QSharedMemory不“拥有”共享内存段。当使用了共享内存段的所有线程或进程中的某一个销毁了QSharedMemory实例,或者所有的都退出,Windows 内核会自动释放共享内存段。
  • Unix:QSharedMemory“拥有”共享内存段。当最后一个线程或进程同共享内存分离,并且调用了QSharedMemory的析构函数之后,Unix 内核会将共享内存段释放。注意,这里与 Windows 不同之处在于,如果使用了共享内存段的线程或进程没有调用QSharedMemory的析构函数,程序将会崩溃。
  • HP-UX:每个进程只允许连接到一个共享内存段。这意味着在 HP-UX 平台,QSharedMemory不应被多个线程使用。

在即将写入共享内存之前,我们需要请求系统创建一个共享内存段(QSharedMemory::create()函数),创建成功则开始写入共享内存段。需要注意的是,在读取或写入共享内存时,都需要使用QSharedMemory::lock()函数对共享内存段加锁。共享内存段就是一段普通内存,所以我们使用 C 语言标准函数memcpy()复制内存段。不要忘记之前我们对共享内存段加锁,在最后需要将其解锁。

如果共享内存段已经连接,还是用QBuffer读取二进制数据,然后生成图片。注意我们在操作共享内存段时还是要先加锁再解锁。最后在读取完毕后,将共享内存段断开连接。

注意,如果某个共享内存段不是由 Qt 创建的,我们也是可以在 Qt 应用程序中使用。不过这种情况下我们必须使用QSharedMemory::setNativeKey()来设置共享内存段。使用原始键(native key)时,QSharedMemory::lock()函数就会失效,我们必须自己保护共享内存段不会在多线程或进程访问时出现问题。

IPC 使用共享内存通信是一个很常用的开发方法。多个进程间得通信要比多线程间得通信少一些,不过在某一族的应用情形下,比如 QQ 与 QQ 音乐、QQ 影音等共享用户头像,还是非常有用的。

猜你喜欢

转载自blog.csdn.net/u013288800/article/details/81382452