目录

摘要

一、进程间保活的基本原理

 二、具体步骤及代码示例

三、常见问题与优化

四、总体方案


摘要


        在一些需要长时间运行的应用程序中,确保进程在意外退出时能够自动重启是一项非常重要的任务。尤其是在嵌入式开发、后台服务以及需要高可用性的场景下,进程保活机制至关重要。在Qt中,我们可以使用一些技巧来实现进程间通信(IPC)和进程自动重启的功能,从而保证应用的稳定性和可靠性。本文将介绍一种在Qt中实现进程间保活的工具和方法,并提供具体的代码实例。

一、进程间保活的基本原理

        进程间保活的基本思路是通过进程间通信(IPC)或者定时器等方式监控目标进程的状态。一旦发现进程崩溃或异常退出,就会启动一个新的进程来替代原有的进程。这样,确保了应用的持续运行。

Qt中可使用以下方法来实现进程间保活:
1. QProcess:用于启动和监控外部进程。
2. 定时器:定期检查目标进程是否正常运行。
3. QTimer:用于设置定时检查机制。

 二、具体步骤及代码示例

        我们将通过一个简单的实例来演示如何实现进程保活。假设我们有一个主进程,负责启动一个子进程。如果子进程崩溃或退出,主进程将自动重启该子进程。

class ProcessMonitor : public QObject
{Q_OBJECTpublic:ProcessMonitor(const QString &processPath, QObject *parent = nullptr): QObject(parent), m_processPath(processPath){// 定时器每隔5秒检查一次子进程m_timer = new QTimer(this);connect(m_timer, &QTimer::timeout, this, &ProcessMonitor::checkProcess);m_timer->start(5000); // 每5秒检查一次// 启动子进程startProcess();}private slots:void checkProcess(){// 如果子进程已经退出,重新启动它if (m_process.state() == QProcess::NotRunning) {qDebug() << "子进程已经退出,重启中...";startProcess();} else {qDebug() << "子进程正在运行...";}}void startProcess(){m_process.start(m_processPath);if (m_process.waitForStarted()) {qDebug() << "子进程启动成功!";} else {qDebug() << "子进程启动失败!";}}private:QProcess m_process;QTimer *m_timer;QString m_processPath;
};int main(int argc, char *argv[])
{QCoreApplication a(argc, argv);// 设定子进程路径QString processPath = "/path/to/your/child_process";  // 替换为子进程的实际路径// 创建进程监控器ProcessMonitor monitor(processPath);return a.exec();
}

在主进程中,我们将使用QProcess来启动子进程,并通过定时器定期检查子进程是否仍在运行。

说明:

QProcess:用于启动子进程`m_process.start(m_processPath)启动指定路径的程序。
QTimer:定时器每隔5秒检查一次子进程是否还在运行。如果子进程已经退出,则会调用startProcess()重新启动子进程。
checkProcess():检查子进程的状态,如果发现子进程已经停止运行,则会重新启动它。

三、常见问题与优化

1. 如何判断子进程是否异常退出?
       可以通过QProcess::errorOccurred()和QProcess::exitCode()来进一步判断子进程是否是正常退出,还是由于错误退出。如果是异常退出,可以通过记录错误日志来提高系统的可靠性。

2. 如何控制重启次数?
   可以在checkProcess()方法中加入计数器,限制子进程重启的次数,防止子进程频繁崩溃后导致死循环。

3. 如何处理进程间的通信?
   如果需要进程间的通信,可以通过QProcess::setProcessChannelMode()方法指定通信模式,使用QProcess::write()和QProcess::read()进行数据交换。

四、总体方案

1.使用任务列表监控并重启一个程序

#include <QCoreApplication>
#include <QProcess>
#include <QDir>
#include <QDebug>
#include <QList>
#include <QProcess>
#include <QRegExp>bool isProcessRunning(const QString& processName)
{// 使用 tasklist 命令获取当前正在运行的进程QProcess process;process.start("tasklist");process.waitForFinished();// 获取进程列表输出QString output = process.readAllStandardOutput();// 使用正则表达式检查进程是否在列表中QRegExp regex(processName);return regex.indexIn(output) != -1;
}void startProcess(const QString& processPath)
{QProcess::startDetached(processPath);
}int main(int argc, char *argv[])
{QCoreApplication a(argc, argv);QString processName = "Lourker.exe";  // 要检查的进程名QString processPath = "C:\\path\\to\\Lourker.exe"; // 进程的路径// 检查进程是否已经在运行if (!isProcessRunning(processName)) {qDebug() << processName << "is not running, starting it now...";startProcess(processPath);} else {qDebug() << processName << "is already running.";}return a.exec();
}

2.使用进程间通信重启一个程序

    //  进程间通信m_timerKeepLive = new QTimer(this);connect(m_timerKeepLive, SIGNAL(timeout()), this, SLOT(slotSendKeepLiveData()));if (NULL == m_server){m_server = new QLocalServer(this);}for (int i = 0; i < 100; i++){QString strServerName = QString("myserver_%1").arg(i);if( m_server->listen(strServerName) ) //监听{connect(m_server, SIGNAL(newConnection()), this, SLOT(slotNewConnection()));m_formDebug->slotShowAppend(SHOW_RECV, QString("进程间通信 %1 监听成功").arg(strServerName));m_timerKeepLive->start(10*1000); //每10s发送1次LockerUI的保活心跳m_unLossConnect = QDateTime::currentDateTime().toTime_t();ok = false;break;}else{m_formDebug->slotShowAppend(SHOW_ERR, QString("进程间通信 %1 监听失败").arg(strServerName));}}

//  进程间通信新连接
void Widget::slotNewConnection()
{m_formDebug->slotShowAppend(SHOW_RECV, "进程间通信 发现新连接!!");QLocalSocket *newsocket = m_server->nextPendingConnection();  //获取连接上的客户端句柄m_client = newsocket;connect(newsocket, SIGNAL(readyRead()), this, SLOT(slotReadData())); //关联数据接收槽函数}//  进程间通信接收数据
void Widget::slotReadData()
{// 取得是哪个 localsocket 可以读数据了QLocalSocket *local = static_cast<QLocalSocket *>(sender());if (!local)return;QByteArray rcv_data = local->readAll();//qDebug() << "77 rcv_data:" << rcv_data;//m_formDebug->slotShowAppend(SHOW_RECV, QString("进程间通信 接收数据:%1").arg(QString(rcv_data)));// 检查收到的数据是否以 "hello" 结尾if (rcv_data.endsWith("hello")){ok = true;m_unLossConnect = QDateTime::currentDateTime().toTime_t();// 提取路径信息,假设 hello 前面是路径QString pathData = QString(rcv_data).chopped(5); // 去掉 "hello"if(pathData != App::targetKeepLiveAppPath){App::targetKeepLiveAppPath = pathData; // 存储路径信息App::writeConfig(); // 写入配置文件}// 回复心跳QString exePath = qApp->applicationFilePath();QByteArray replyData = QString("%1 OK").arg(exePath).toLatin1();local->write(replyData);//m_formDebug->slotShowAppend(SHOW_SEND, QString("进程间通信 发送数据:%1").arg(QString(replyData)));//qDebug() << "已更新 App::TargetAppPath:" << App::targetKeepLiveAppPath; // 打印更新后的路径}
}
//  发送保活信息
void Widget::slotSendKeepLiveData()
{QString exePath = qApp->applicationFilePath();// 检查 m_client 是否有效if (NULL != m_client){QString message = QString("%1 OK").arg(exePath);                    // 当前exe路径m_client->write(message.toLatin1());//m_formDebug->slotShowAppend(SHOW_SEND, QString("进程间通信 主动发送数据:%1").arg(message));//qDebug()<< "7777" << message;}if(ok){//记录当前回复时间m_unLossConnect = QDateTime::currentDateTime().toTime_t();ok=false;}int nLossInt = QDateTime::currentDateTime().toTime_t() - m_unLossConnect;//超时几秒qDebug()<< "66666"<< nLossInt;if (nLossInt >= 2 * 10)  //表示超时的总秒数20s未回复{m_timerKeepLive->stop();m_unLossConnect = QDateTime::currentDateTime().toTime_t();// 检查目标应用程序是否在运行if (isAppRunning(App::targetKeepLiveAppPath)){killApp(App::targetKeepLiveAppPath); // 强制关闭应用程序qDebug() << "成功退出应用程序:" << App::targetKeepLiveAppPath;}else{qDebug() << "应用程序未运行,准备重启:" << App::targetKeepLiveAppPath;}// 延迟启动应用程序QTimer::singleShot(1000, this, [this] { startApp(App::targetKeepLiveAppPath); }); // 延迟启动qDebug() << "计划在1秒后重启应用程序:" << App::targetKeepLiveAppPath;//QTimer::singleShot(1000, this, SLOT(startApp(App::targetKeepLiveAppPath))); // 延迟启动}
}
void Widget::killApp(const QString &appPath)
{QString appName = QFileInfo(appPath).fileName(); // 获取应用程序名称QProcess::execute(QString("taskkill /F /IM %1").arg(appName)); // 强制关闭进程qDebug() << "已强制关闭应用程序:" << appName;
}bool Widget::isAppRunning(const QString &appPath)
{QProcess process;process.start(QString("tasklist /FI \"imagename eq %1\"").arg(QFileInfo(appPath).fileName())); // 获取进程名称process.waitForFinished(5000); // 阻塞 5 秒等待 tasklist 执行完成QString outputStr = QString::fromLocal8Bit(process.readAllStandardOutput()); // 获取进程信息return outputStr.contains(QFileInfo(appPath).fileName()); // 返回是否找到进程
}
void Widget::startApp(const QString &appPath)
{// 启动应用程序并检查返回值if (QProcess::startDetached(appPath)){qDebug() << "应用程序启动成功:" << appPath;}else{qDebug() << "启动应用程序失败:" << appPath;}writeLog(QString("已重启保活软件1次,重启时间: %1").arg(QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm:ss")),false);m_unLossConnect = QDateTime::currentDateTime().toTime_t();m_timerKeepLive->start(10*1000);}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/bicheng/87375.shtml
繁体地址,请注明出处:http://hk.pswp.cn/bicheng/87375.shtml
英文地址,请注明出处:http://en.pswp.cn/bicheng/87375.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Python-内置数据结构-list-tuple-bubble-字符串-bytes-bytesarray-切片-学习笔记

欠4年前自己的一份笔记&#xff0c;献给今后的自己。 分类 数值型 int、float、complex、bool 序列对象 字符串 str 列表 list tuple 键值对 集合set 字典dict 数值型 int、float、complex、bool都是class&#x…

利用事务钩子函数解决业务异步发送问题

利用事务钩子函数解决业务异步发送问题 一、问题背景二、实现方案1、生产者代码2、消费者代码 三、测试与验证1、未开启事务场景2、开启事务场景 四、项目结构及源码 一、问题背景 在某项业务中&#xff0c;需要在事务完成后&#xff0c;写入日志到某数据库中。需要要么都成功&…

uniapp选择相册

概述 一款针对Android平台下的图片选择器&#xff0c;支持从相册获取图片、视频、音频&拍照&#xff0c;支持裁剪(单图or多图裁剪)、压缩、主题自定义配置等功能&#xff0c;支持动态获取权限&适配Android 5.0系统的开源图片选择框架。 支持Uniapp和Uniapp X下的Vue2、…

MAC 多应用切换技巧,单应用切换技巧

在 Mac 上&#xff0c;有几种快捷键可以帮助你快速切换应用程序窗口&#xff1a; 1. Command (⌘) Tab - 这是最常用的快捷键&#xff0c;用于在打开的应用程序之间进行循环切换。按住 Command 键不放&#xff0c;然后反复按下 Tab 键可以选择下一个应用程序。当你松开 Comm…

SpringBoot+本地部署大模型实现知识库功能

SpringBoot本地部署大模型实现RAG知识库功能 1、Linux系统部署本地大模型1.1 安装ollama1.2 启动ollama1.3 下载deepseek模型 2、Springboot代码调用本地模型实现基础问答功能3、集成向量数据库4、知识库数据喂取5、最终实现RAG知识库功能 1、Linux系统部署本地大模型 1.1 安装…

嵌入式原理与应用篇---ARM

ARM 架构的 STM32 系列微控制器广泛应用于嵌入式系统开发&#xff0c;理解其汇编语言指令对于优化性能、访问硬件底层非常重要。下面详细解释常见的 ARM 汇编指令及其使用实例。 数据处理指令 1. MOV&#xff08;移动&#xff09; 功能&#xff1a;将立即数或寄存器值复制到…

【RHCSA-Linux考试题目笔记(自用)】servera的题目

一、开始 1、启动rhcsa环境 2、点击题目&#xff0c;看题 3、通过控制器来启动所有虚拟机 控制器 打开后点start&#xff0c;然后ok 之后进入一个有classroom、servera、serverb&#xff08;考试不一定叫这些名&#xff0c;但大差不差&#xff09;什么之类的界面&#xff0c;…

SpringBoot项目使用arthas-tunnel-server

参考官网Arthas Spring Boot Starter | arthas Spring Boot系列之使用Arthas Tunnel Server 进行远程调试实践-腾讯云开发者社区-腾讯云 springBoot项目, 增加maven依赖 <dependency><groupId>com.taobao.arthas</groupId><artifactId>arthas-sprin…

Modbus TCP 进阶:基于以太网的远程设备控制(二)

基于 Modbus TCP 的远程设备控制实战 &#xff08;一&#xff09;硬件与网络搭建实操 1. 设备选型与连接 在工业现场&#xff0c;根据远程控制需求进行设备选型至关重要 。对于传感器&#xff0c;若要监测温度&#xff0c;可选择高精度的热电偶传感器&#xff0c;如 K 型热电…

分库分表之实战-sharding-JDBC

大家好&#xff0c;我是工藤学编程 &#x1f989;一个正在努力学习的小博主&#xff0c;期待你的关注实战代码系列最新文章&#x1f609;C实现图书管理系统&#xff08;Qt C GUI界面版&#xff09;SpringBoot实战系列&#x1f437;【SpringBoot实战系列】Sharding-Jdbc实现分库…

httpcore-nio引起的线程、fd泄露问题

依赖来源&#xff1a;httpasyncclient-4.1.4.jar 现象 程序报错too many open files 线程数飙升、句柄数飙升 thread dump显示大量 "I/O dispatcher 7215" #9102 prio5 os_prio0 tid0x00002b7ba036a800 nid0x6f24 runnable [0x00002b7d98d41000]java.lang.Thread.…

多线程生产者消费者模型实战案例

多线程生产者消费者模型实战案例 前言业务场景术前准备无锁无事务有事务 synchronized事务在锁外事务在锁内 数据库行锁什么是数据库行锁有事务没有事务 乐观锁ReentrantLock分布式锁 前言 曾经一直有一个疑惑&#xff0c;就是关于多线程生产者消费者模型的学习过程中&#xf…

青少年编程与数学 02-022 专业应用软件简介 03 三维建模及动画软件:Autodesk Maya

青少年编程与数学 02-022 专业应用软件简介 03 三维建模及动画软件&#xff1a;Autodesk Maya 一、什么是三维建模二、什么是计算机动画三、三维建模及动画设计软件的发展历程&#xff08;一&#xff09;早期探索阶段&#xff08;20世纪60年代 - 80年代&#xff09;&#xff08…

获得 OCM 大师证书学习历练

当我站在山城重庆的洪崖洞前&#xff0c;看着璀璨的夜景倒映在嘉陵江上&#xff0c;手中紧握着 OCM 大师证书&#xff0c;那一刻&#xff0c;备考时的艰辛与考试时的紧张都化作了满满的成就感。这段在重庆获得 OCM 大师证书的经历&#xff0c;就像一场充满挑战与惊喜的冒险&…

srs-gb28181 与 SRS 5.0 对 GB28181 国标支持

srs-gb28181 是基于 SRS 4.0/5.0 的国标&#xff08;GB28181&#xff09;扩展分支&#xff0c;而 SRS 5.0 官方版本也逐步增强了对 GB28181 的支持。以下是两者的主要区别&#xff1a; 1. 功能支持对比 功能srs-gb28181&#xff08;扩展分支&#xff09;SRS 5.0&#xff08;官…

算法第18天|继续二叉树:修剪二叉搜索树、将有序数组转化为二叉搜索树、把二叉搜索树转换为累加树

今日总结&#xff1a; 1、修剪二叉搜索树&#xff08;重点思考如何修剪&#xff09; &#xff08;1&#xff09;递归的返回值是什么&#xff1f;&#xff08;与插入、删除一样&#xff09; &#xff08;2&#xff09;递归的单层逻辑一定要缕清&#xff08;3中情况讨论&#xff…

C# 多线程(三)线程池

目录 1.通过TPL使用线程池 2.不使用TPL进入线程池的办法 异步委托 3.线程池优化技术 最小线程数的工作原理 每当启动一个新线程时&#xff0c;系统都需要花费数百微秒来分配资源&#xff0c;例如创建独立的局部变量栈空间。默认情况下&#xff0c;每个线程还会占用约1…

学习笔记(29):训练集与测试集划分详解:train_test_split 函数深度解析

学习笔记(29):训练集与测试集划分详解&#xff1a;train_test_split 函数深度解析 一、为什么需要划分训练集和测试集&#xff1f; 在机器学习中&#xff0c;模型需要经历两个核心阶段&#xff1a; 训练阶段&#xff1a;用训练集数据学习特征与目标值的映射关系&#xff08;…

【全网唯一】自动化编辑器 Windows版纯本地离线文字识别插件

目的 自动化编辑器超轻量级RPA工具&#xff0c;零代码制作RPA自动化任务&#xff0c;解放双手&#xff0c;释放双眼&#xff0c;轻松玩游戏&#xff0c;刷任务。本篇文章主要讲解下自动化编辑器的TomatoOCR纯本地离线文字识别Windows版插件如何使用和集成。 准备工作 1、下载自…

GitHub 2FA绑定

GitHub 2FA绑定 作为全球最大的代码托管平台&#xff0c;GitHub对账号安全的重视程度不断提升——自2023年3月起&#xff0c;GitHub已要求所有在GitHub.com上贡献代码的用户必须启用双因素身份验证&#xff08;2FA&#xff09;。如果你是符合条件的用户&#xff0c;会收到一封…