Qt進程間保活方案:詳解如何實現進程間通信與自動保活機制

目錄

摘要

一、進程間保活的基本原理

?二、具體步驟及代碼示例

三、常見問題與優化

四、總體方案


摘要


????????在一些需要長時間運行的應用程序中,確保進程在意外退出時能夠自動重啟是一項非常重要的任務。尤其是在嵌入式開發、后臺服務以及需要高可用性的場景下,進程保活機制至關重要。在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;會收到一封…