Qt中的QProcess与Boost.Interprocess:实现多进程编程

目录

QProcess简介

启动进程的不同方式

例子1:打开记事本程序

例子2:执行带有管道(|)的Linux命令

同步进程API

Boost.Interprocess简介

(一)共享内存:

(二)命名信号量与互斥锁示例

[(三) 资源清理](#(三) 资源清理)

实现多进程

使用QProcess管理多个进程

结合Boost.Interprocess实现进程间通信


QPocess是Qt提供的一个类,用于启动和管理外部进程。通过QPocess,开发者可以启动一个进程,与之通信(发送输入和读取输出),检查其状态,以及等待其完成。这在执行系统命令、运行其他程序或脚本时尤为有用。

Boost.Interprocess是Boost库中的一个模块,专门用于实现跨进程通信。它提供了多种机制,包括共享内存、命名信号量、互斥锁、消息队列等,允许不同进程之间高效地交换数据。

本文将详细介绍这两者的功能与用法,并展示如何在Qt项目中结合使用它们,实现高效的多进程编程。

QProcess简介

功能:

QPocess类用于启动和管理外部进程。

  • 启动外部程序:执行可执行文件,并传递命令行参数
  • 与外部进行通信:通过标准输入 输出 和 错误通道交换数据
  • 进程控制:终止 暂停 或 恢复进程,获取其退出状态
  • 同步与异步操作:支持阻塞和非阻塞的进程管理方式

用法

复制代码
#include <QProcess>

// 创建QProcess对象
QProcess *process = new QProcess(this);

// 连接信号槽以读取输出
connect(process, &QProcess::readyReadStandardOutput, this, &YourClass::handleStdOutput);

// 启动外部程序
process->start("external_program", QStringList() << "arg1" << "arg2");

// 检查进程是否启动成功
if (!process->waitForStarted()) {
    qDebug() << "Process failed to start.";
}

信号与槽 QProcess通过信号和槽机制通知进程的各种状态和数据。常用的信号包括:

  • started():进程开始执行时发出。
  • readyReadStandardOutput():进程的标准输出有数据可读时发出。
  • readyReadStandardError():进程的标准错误输出有数据可读时发出。
  • finished(int exitCode, QProcess::ExitStatus exitStatus):进程结束时发出。
  • errorOccurred(QProcess::ProcessError error):发生错误时发出。
  • stateChanged(QProcess::ProcessState newState):进程状态改变时发出。

以下示例展示了如何连接这些信号:

复制代码
QProcess process;
process->start("ls", QStringList() << "-l" << "/home/user");

// 连接信号以读取标准输出
connect(&process, &QProcess::readyReadStandardOutput, [&]() {
    qDebug() << "Standard output:" << process.readAllStandardOutput();
});

// 连接信号以读取标准错误
connect(&process, &QProcess::readyReadStandardError, [&]() {
    qDebug() << "Standard error:" << process.readAllStandardError();
});

// 连接信号以处理进程结束
connect(&process, QOverload<int, QProcess::ExitStatus>::of(&QProcess::finished),
          [&](int exitCode, QProcess::ExitStatus exitStatus) {
    qDebug() << "Process finished with exit code" << exitCode << exitStatus;
});

启动进程的不同方式

QProcess提供了多种方式来启动外部进程,主要包括:

  1. start() 方法
    • 异步启动:启动进程后立即返回,不会阻塞调用线程。

    • 支持交互:可以与进程实时交互,如读取输出、写入输入。

    • 重定向:可以选择性地重定向标准输入输出。

      QProcess process;
      process.start("notepad.exe");

  1. execute() 方法
    • 同步启动:启动进程并阻塞调用线程,直到进程结束。

    • 简单使用:不适合需要与进程交互的场景。

    • 输出重定向:进程的输出会直接转发到调用进程的标准输出。

      int exitCode = QProcess::execute("ls", QStringList() << "-l" << "/home/user");
      qDebug() << "Process exited with code" << exitCode;

  1. startDetached() 方法
    • 分离启动:启动独立的进程,与父进程完全分离。

    • 独立运行:子进程不会随着父进程的退出而结束。

    • 不支持通信:无法通过信号槽机制与子进程通信。

      bool started = QProcess::startDetached("notepad.exe", QStringList() << "C:/example.txt");
      if (started) {
      qDebug() << "Notepad started successfully.";
      }

例子1:打开记事本程序

点击按钮后启动记事本,并通过QProcess管理其生命周期。

mainwindow.h

复制代码
#ifndef MAINWINDOW_H  
#define MAINWINDOW_H  

#include <QMainWindow>
#include <QProcess>

QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE

class MainWindow : public QMainWindow  
{  
    Q_OBJECT  

        public:  
    MainWindow(QWidget *parent = nullptr);  
    ~MainWindow();  

    private slots:  
    void openProcess();  
    void readResult(int exitCode);  

    private:  
    Ui::MainWindow *ui;
    QProcess *p;  
};  

#endif // MAINWINDOW_H

mainwindow.cpp

复制代码
#include "mainwindow.h"  
#include "ui_mainwindow.h"  
#include <QPushButton>
#include <QMessageBox>
#include <QTextCodec>
#include <QDebug>

MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{  
    ui->setupUi(this);  

    p = new QProcess(this);  

    QPushButton *bt = new QPushButton("Execute Notepad", this);  
    bt->setGeometry(QRect(QPoint(100, 100), QSize(200, 50)));
    connect(bt, &QPushButton::clicked, this, &MainWindow::openProcess);  
}  

MainWindow::~MainWindow()  
{  
    delete ui;  
}  

void MainWindow::openProcess()  
{  
    p->start("notepad.exe");  
} 

// void MainWindow::readResult(int exitCode)  
// {  
//     if(exitCode == 0) {  
//         QTextCodec* gbkCodec = QTextCodec::codecForName("GBK");  
//         QString result = gbkCodec->toUnicode(p->readAll());  
//         QMessageBox::information(this, "Process Output", result);  
//     }  
// }

说明 :在这个示例中,当点击按钮时,程序会启动Windows的记事本程序。由于使用的是start()方法,记事本与主程序处于异步运行状态,主程序不会被阻塞。

例子2:执行带有管道(|)的Linux命令

在Qt中由于QProcess不直接支持管道命令,可以通过启动shell并传递整个命令作为参数。

widget.cpp

复制代码
#include "widget.h"
#include "ui_widget.h"
#include <QDebug>

Widget::Widget(QWidget *parent)
    : QWidget(parent)
        , ui(new Ui::Widget)
    {
        ui->setupUi(this);

        process = new QProcess(this);

        connect(process, &QProcess::readyReadStandardOutput, [&]() {
            qDebug() << "Standard output:" << QString::fromLocal8Bit(process->readAllStandardOutput());
        });

        connect(process, &QProcess::readyReadStandardError, [&]() {
            qDebug() << "Standard error:" << QString::fromLocal8Bit(process->readAllStandardError());
        });

        connect(process, QOverload<int, QProcess::ExitStatus>::of(&QProcess::finished),
        [&](int exitCode, QProcess::ExitStatus exitStatus)
        {
            qDebug() << "Process finished with exit code" << exitCode << exitStatus;
        });

        process->start("bash", QStringList() << "-c" << "ps -ef | grep firefox");

        process->waitForFinished();
    }

Widget::~Widget()
{
    delete ui;
}

说明 :通过启动bash并传递-c参数,可以执行带有管道的复杂命令。

同步进程API

QProcess提供了一组可以在没有事件循环的情况下使用的函数,通过挂起调用线程直到发出某些信号。这些方法包括:

  • waitForStarted():阻塞直到进程开始。
  • waitForReadyRead():阻塞直到有新数据可在当前读取通道上读取为止。
  • waitForBytesWritten():阻塞直到将一个有效载荷数据写入该进程为止。
  • waitForFinished():阻塞直到过程完成。

示例

复制代码
QProcess gzip;
gzip.start("gzip", QStringList() << "-c");
if (!gzip.waitForStarted())
    return false;

gzip.write("Qt rocks!");
gzip.closeWriteChannel();

if (!gzip.waitForFinished())
    return false;

QByteArray result = gzip.readAll();

注意 :从主线程(调用QApplication::exec()函数的线程)调用这些函数可能会导致用户界面卡住。建议在子线程中使用这些方法,或使用信号槽机制进行异步处理。

Boost.Interprocess简介

功能:

Boost.Interprocess是Boost库中的一个模块,是专门用于实现跨进程通信

  • 共享内存:多个进程可以访问同一块内存区域
  • 命名信号量与互斥锁:用于同步进程间操作,防止资源竞争
  • 消息队列:允许进程之间以消息为单位进行通信
  • 映射文件:在不同进程间共享文件内容

用法:

(一)共享内存:
  1. 创建和写入共享内存(生产者)

    #include <boost/interprocess/shared_memory_object.hpp>
    #include <boost/interprocess/mapped_region.hpp>
    #include

    using namespace boost::interprocess;

    void createSharedMemory() {
    // 删除已存在的共享内存
    shared_memory_object::remove("MySharedMemory");

    复制代码
     // 创建共享内存对象
     shared_memory_object shm(create_only, "MySharedMemory", read_write);
    
     // 设置共享内存大小
     shm.truncate(1024);
    
     // 映射共享内存
     mapped_region region(shm, read_write);
    
     // 写入数据
     const char *message = "Hello from producer!";
     std::memcpy(region.get_address(), message, std::strlen(message) + 1);

    }

  2. 读取共享内存(消费者)

    #include <boost/interprocess/shared_memory_object.hpp>
    #include <boost/interprocess/mapped_region.hpp>
    #include

    using namespace boost::interprocess;

    void readSharedMemory() {
    // 打开已存在的共享内存
    shared_memory_object shm(open_only, "MySharedMemory", read_only);

    复制代码
     // 映射共享内存
     mapped_region region(shm, read_only);
    
     // 读取数据
     const char *message = static_cast<const char*>(region.get_address());
     std::cout << "Message: " << message << std::endl;

    }

(二)命名信号量与互斥锁示例
  1. 生产者

    #include <boost/interprocess/sync/named_mutex.hpp>
    #include <boost/interprocess/sync/named_semaphore.hpp>

    using namespace boost::interprocess;

    void producer() {
    // 创建命名互斥锁
    named_mutex mutex(create_only, "MyMutex");

    复制代码
     // 创建命名信号量
     named_semaphore semaphore(create_only, "MySemaphore", 0);
    
     // 锁定互斥锁
     scoped_lock<named_mutex> lock(mutex);
    
     // 执行共享内存写入操作
     // ...
    
     // 释放互斥锁后通知消费者
     lock.unlock();
     semaphore.post();

    }

  2. 消费者

    #include <boost/interprocess/sync/named_mutex.hpp>
    #include <boost/interprocess/sync/named_semaphore.hpp>

    using namespace boost::interprocess;

    void consumer() {
    // 打开已存在的命名互斥锁
    named_mutex mutex(open_only, "MyMutex");

    复制代码
     // 打开已存在的命名信号量
     named_semaphore semaphore(open_only, "MySemaphore");
    
     // 等待信号量通知
     semaphore.wait();
    
     // 锁定互斥锁
     scoped_lock<named_mutex> lock(mutex);
    
     // 执行共享内存读取操作
     // ...

    }

(三) 资源清理

在使用完共享内存和同步对象后,务必进行资源清理,以避免资源泄漏。

复制代码
#include <boost/interprocess/shared_memory_object.hpp>
#include <boost/interprocess/sync/named_mutex.hpp>
#include <boost/interprocess/sync/named_semaphore.hpp>

using namespace boost::interprocess;

void cleanup() {
    shared_memory_object::remove("MySharedMemory");
    named_mutex::remove("MyMutex");
    named_semaphore::remove("MySemaphore");
}

实现多进程

使用QProcess管理多个进程

QProcess不仅可以启动和管理单个外部进程,还支持同时管理多个进程。以下示例展示了如何启动两个外部ping进程并分别管理它们的输出:

复制代码
#include <QProcess>
#include <QList>
#include <QDebug>

class ProcessManager : public QObject {
    Q_OBJECT
public:
    ProcessManager(QObject *parent = nullptr) : QObject(parent) {}

    void startProcesses() {
        // 启动第一个进程
        QProcess *process1 = new QProcess(this);
        connect(process1, &QProcess::readyReadStandardOutput, this, [=]() {
            qDebug() << "Process1 Output:" << process1->readAllStandardOutput();
        });
        process1->start("ping", QStringList() << "www.example.com");

        // 启动第二个进程
        QProcess *process2 = new QProcess(this);
        connect(process2, &QProcess::readyReadStandardOutput, this, [=]() {
            qDebug() << "Process2 Output:" << process2->readAllStandardOutput();
        });
        process2->start("ping", QStringList() << "www.google.com");

        // 存储进程以便后续管理
        processes.append(process1);
        processes.append(process2);
    }

    void stopAllProcesses() {
        foreach(QProcess *process, processes) {
            if (process->state() == QProcess::Running) {
                process->terminate();
                process->waitForFinished(3000);
            }
            process->deleteLater();
        }
        processes.clear();
    }

private:
    QList<QProcess*> processes;
};
结合Boost.Interprocess实现进程间通信

在多进程应用中,进程间通信是关键。结合QProcess和Boost.Interprocess,可以实现高效的通信机制。以下示例展示了主进程与子进程通过共享内存进行通信。

下面例子如何在Qt应用中使用QProcess启动子进程,并通过Boost.Interprocess的共享内存和互斥锁实现通信与同步。

主进程代码:

复制代码
#include <QCoreApplication>
#include <QProcess>
#include <boost/interprocess/shared_memory_object.hpp>
#include <boost/interprocess/mapped_region.hpp>
#include <boost/interprocess/sync/named_mutex.hpp>
#include <cstring>
#include <iostream>

using namespace boost::interprocess;

int main(int argc, char *argv[]) {
    QCoreApplication a(argc, argv);

    // 清理之前的资源
    shared_memory_object::remove("SharedMemory");
    named_mutex::remove("MyMutex");

    // 创建共享内存
    shared_memory_object shm(create_only, "SharedMemory", read_write);
    shm.truncate(1024);
    mapped_region region(shm, read_write);

    // 创建命名互斥锁
    named_mutex mutex(create_only, "MyMutex");

    // 启动子进程
    QProcess child;
    child.start("child_process_executable"); // 替换为实际子进程可执行文件路径

    if (!child.waitForStarted()) {
        qDebug() << "Failed to start child process.";
        return -1;
    }

    // 锁定互斥锁并写入数据
    {
        scoped_lock<named_mutex> lock(mutex);
        const char *message = "Hello from parent!";
        std::memcpy(region.get_address(), message, std::strlen(message) + 1);
    } // 自动解锁

    // 等待子进程完成
    child.waitForFinished();

    // 读取子进程的响应
    {
        scoped_lock<named_mutex> lock(mutex);
        const char *response = static_cast<const char*>(region.get_address());
        std::cout << "Response from child: " << response << std::endl;
    }

    // 清理资源
    shared_memory_object::remove("SharedMemory");
    named_mutex::remove("MyMutex");

    return a.exec();
}

说明

  1. 资源清理 :在创建共享内存和互斥锁之前,先移除之前可能残留的资源,避免冲突。
  2. 创建共享内存 :通过 shared_memory_object****创建一个名为 SharedMemory****的共享内存,并映射到地址空间。
  3. 创建命名互斥锁 :通过 named_mutex****创建一个名为 MyMutex****的互斥锁,用于同步访问共享内存。
  4. 启动子进程 :使用 QProcess****启动子进程 child_process_executable**。**
  5. 写入数据 :在锁定互斥锁后,将消息写入共享内存。
  6. 读取响应:等待子进程完成后,读取子进程在共享内存中写入的响应。

子进程代码:

复制代码
#include <boost/interprocess/shared_memory_object.hpp>
#include <boost/interprocess/mapped_region.hpp>
#include <boost/interprocess/sync/named_mutex.hpp>
#include <cstring>
#include <iostream>

using namespace boost::interprocess;

int main() {
    try {
        // 打开已存在的共享内存
        shared_memory_object shm(open_only, "SharedMemory", read_write);
        mapped_region region(shm, read_write);

        // 打开已存在的命名互斥锁
        named_mutex mutex(open_only, "MyMutex");

        // 读取主进程的数据
        {
            scoped_lock<named_mutex> lock(mutex);
            const char *message = static_cast<const char*>(region.get_address());
            std::cout << "Received from parent: " << message << std::endl;

            // 写入响应到共享内存
            const char *response = "Hello from child!";
            std::memcpy(region.get_address(), response, std::strlen(response) + 1);
        } // 自动解锁
    }
    catch(interprocess_exception &ex){
        std::cerr << ex.what() << std::endl;
        return 1;
    }

    return 0;
}

说明

  1. 打开共享内存和互斥锁 :子进程通过 open_only****模式打开主进程创建的共享内存和互斥锁。
  2. 读取数据 :在锁定互斥锁后,读取共享内存中的数据。
  3. 写入响应 :将响应消息写入共享内存。

注意:请确保子进程的可执行文件路径正确,并且子进程能够访问Boost.Interprocess库。

相关推荐
ccino .7 小时前
Docker 容器删除卡在 Removal In Progress / Dead 解决
服务器·安全·web安全·网络安全
江湖人称菠萝包7 小时前
【Qt】《Qt 5.9 C++开发指南》笔记-Chapter4-常用界面设计组件
笔记·qt·qt5
微学AI7 小时前
飞牛 NAS 部署 prompts.chat:自建提示词库、导入社区内容,再配置固定公网访问
数据库·内网穿透
羑悻的小杀马特7 小时前
从写 YAML 到集群自愈:KES-Operator 把 KES 集群接进 Kubernetes 原生体系
运维·数据库·容器·kubernetes
bksczm7 小时前
MySQL基础篇之事务
linux·数据库·sql·mysql
李白客7 小时前
数据库管理工具怎么选?Navicat、DataGrip、DBeaver 六维横向对比与选型建议
运维·数据库
马立杰7 小时前
mysql中Illegal mix of collations for operation “UNION”错误的解决方法
数据库·sql·网络安全
Ivanqhz7 小时前
BPE(Byte Pair Encoding) 算法
java·服务器·网络·深度学习·神经网络
蓝速科技7 小时前
蓝速 K10 三防平板在工业恶劣工况下的选型与应用指南
大数据·运维·数据库·人工智能·科技
bksczm7 小时前
MySQL基础篇之视图与用户管理
linux·数据库·sql·mysql