用 Qt 实现几种最常用的进程间通信方式。
1. 匿名管道 —— 用 QProcess 父子通信
原理:父进程启动子进程,通过标准输入/输出管道传递数据。Qt 的QProcess封装了这一切。
父进程(发送数据)
// parent/main.cpp #include <QCoreApplication> #include <QProcess> #include <QDebug> int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); QProcess child; child.start("./child"); // 启动编译好的子进程 child.waitForStarted(); child.write("Hello from parent\n"); // 写入子进程的标准输入 child.closeWriteChannel(); // 关闭输入,让子进程知道结束 child.waitForFinished(); qDebug() << "Child output:" << child.readAllStandardOutput(); return 0; }子进程(接收数据)
// child/main.cpp #include <QCoreApplication> #include <QTextStream> int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); QTextStream in(stdin); QString line = in.readLine(); QTextStream out(stdout); out << "I received: " << line << Qt::endl; return 0; }编译运行:两个工程都编译后,在同一目录下运行 parent,会看到子进程把收到的消息打印回来。
2. 命名管道(FIFO) —— 用 QFile 读写
原理:Linux 下创建特殊文件(管道文件),无亲缘关系的进程可以像操作普通文件一样用QFile打开它,但数据不会落盘。
公共准备:创建管道文件(只需一次)
在终端执行:
mkfifo /tmp/myfifo
写入进程
// writer/main.cpp #include <QCoreApplication> #include <QFile> #include <QDebug> int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); QFile fifo("/tmp/myfifo"); if (!fifo.open(QIODevice::WriteOnly)) { qDebug() << "Cannot open FIFO for writing"; return 1; } fifo.write("Hello via FIFO\n"); fifo.close(); return 0; }读取进程
// reader/main.cpp #include <QCoreApplication> #include <QFile> #include <QDebug> int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); QFile fifo("/tmp/myfifo"); if (!fifo.open(QIODevice::ReadOnly)) { qDebug() << "Cannot open FIFO for reading"; return 1; } qDebug() << "Received:" << fifo.readAll(); fifo.close(); return 0; }运行:先启动 reader(它会阻塞等待),再启动 writer,reader 会立即打印收到的消息。
3. 本地套接字 —— QLocalServer / QLocalSocket
原理:同机进程间类似网络通信,但不需要 IP,使用文件名作为地址。
服务端
// server/main.cpp #include <QCoreApplication> #include <QLocalServer> #include <QLocalSocket> #include <QDebug> int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); const QString name = "qt_local_socket"; QLocalServer::removeServer(name); QLocalServer server; server.listen(name); QObject::connect(&server, &QLocalServer::newConnection, [&]() { QLocalSocket *client = server.nextPendingConnection(); QObject::connect(client, &QLocalSocket::readyRead, [client]() { QByteArray data = client->readAll(); qDebug() << "Server got:" << data; client->write("Echo: " + data); client->disconnectFromServer(); }); }); qDebug() << "Server listening..."; return a.exec(); }客户端
// client/main.cpp #include <QCoreApplication> #include <QLocalSocket> #include <QDebug> int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); QLocalSocket socket; socket.connectToServer("qt_local_socket"); if (!socket.waitForConnected(3000)) { qDebug() << "Connection failed"; return 1; } socket.write("Hello socket!"); socket.waitForBytesWritten(); if (socket.waitForReadyRead(3000)) { qDebug() << "Reply:" << socket.readAll(); } return 0; }注意:.pro文件中要加QT += network。
4. 共享内存 + 信号量 —— QSharedMemory + QSystemSemaphore
原理:Qt 封装了跨平台的共享内存和进程间信号量,适合快速交换数据并保证互斥。
任意进程(写入/读取同一个计数器)
// shared_counter/main.cpp #include <QCoreApplication> #include <QSharedMemory> #include <QSystemSemaphore> #include <QDebug> int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); QSystemSemaphore sem("counter_sem", 1, QSystemSemaphore::OpenOrCreate); QSharedMemory shm("counter_shm"); if (!shm.create(sizeof(int))) { // 已经存在,附加 if (!shm.attach()) { qDebug() << "Attach failed"; return 1; } } sem.acquire(); // 加锁 int *counter = static_cast<int*>(shm.data()); *counter += 1; qDebug() << "Current counter:" << *counter; sem.release(); // 解锁 // 清理(最后一个退出的进程执行) // shm.detach(); // 这里为了演示不销毁 return 0; }运行:编译后多次启动这个程序,每次都会看到计数器递增,并且不会混乱。
5. 信号(Signal) —— QProcess 发送/接收
原理:Unix 信号是一种异步通知机制。Qt 中可用QProcess向进程发送信号,但接收信号需要配合低层 API,这里只演示发送。
发送方
// signal_sender/main.cpp #include <QCoreApplication> #include <QProcess> #include <QDebug> #include <signal.h> int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); QProcess process; process.start("./receiver"); // 启动一个长期运行的程序 process.waitForStarted(); qDebug() << "Sending SIGUSR1 to" << process.processId(); // 方式1:使用 kill 系统调用 kill(process.processId(), SIGUSR1); // 方式2:使用 QProcess::terminate() 发送 SIGTERM // process.terminate(); process.waitForFinished(3000); return 0; }接收方(需处理信号)
// receiver/main.cpp #include <QCoreApplication> #include <QDebug> #include <signal.h> #include <unistd.h> // 信号处理函数(尽量简单) void handleSigUsr1(int) { qDebug() << "SIGUSR1 received!"; } int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); signal(SIGUSR1, handleSigUsr1); qDebug() << "Receiver PID:" << getpid() << " waiting..."; // 让程序保持运行,等待信号 QTimer::singleShot(5000, &a, &QCoreApplication::quit); return a.exec(); }注意:接收方需要<signal.h>和 Unix 环境。
小结
| IPC 方式 | Qt 实现类 | 适用场景 |
|---|---|---|
| 匿名管道 | QProcess(标准输入/输出) | 父子进程单向数据流 |
| 命名管道 | QFile(操作 FIFO 文件) | 无关进程间的流式通信 |
| 本地套接字 | QLocalServer / QLocalSocket | 多对多、结构化的命令交换 |
| 共享内存+信号量 | QSharedMemory + QSystemSemaphore | 大数据量、高频访问 |
| 信号 | kill() / QProcess::terminate() | 简单事件通知、进程控制 |
希望对你的 Qt 进程间通信学习有帮助。