为了保证软件运行的鲁棒性,常常在软件设计中,启用看门狗软件。对于长时间运行的软件,处于各种不可预知的错误,例如:软件的bug、IO异常等等,容易造成软件崩溃。软件崩溃后,就需要看门狗软件将其拉起来。
如果要开发一个看门狗软件,需要看长时间运行的软件是窗体软件,还是无窗体的软件。对于有窗体和无窗体的软件,看门狗软件的实现方式是不同的。
1.Ubuntu上的看门狗软件的开发
Ubuntu上启动应用程序,需要先使用fork启动子进程,然后,在子进程中启动应用程序,开门狗大致代码如下:
/*
* 检查被开门狗监视的软件的进程是否不存在了,如果不存在了,则要重新拉起来
*/
auto ret = ::fork();
if(ret == -1)
{
break; //出错了,程序退出
}
else if(ret == 0) //子进程
{
::execlp(exePath, exeName, params); //启动被开门狗监视的程序,
//exePath是要拉起的程序的完整路径
//exeName是要拉起的程序名称
//params是启动程序的命令行参数
}
else //父进程
{
//...
}
1.1 无窗体
如果开门狗程序要拉起的是无窗体的程序,则需要进行如下设置:
假设开门狗程序位于/home/myWatchdog,要让开门狗程序随系统启动而启动,则需要在/etc/systemd/system/目录下放置一个.service文件。
这里创建文件/etc/systemd/system/myWatchdog.service,文件内容如下:
[Unit]
Description=description of the service
[Service]
Type=simple
ExecStart=/home/myWatchdog
ExecStop=
[Install]
WantedBy=multi-user.target
创建完/etc/systemd/system/myWatchdog.service文件之后,需要使用命令将服务开启:
sudo systemctl enable myWatchdog.service
最后,重启系统。
1.2 有窗体
如果开门狗程序要拉起的是有窗体的程序,则需要进行如下设置:
假设开门狗程序位于/home/myWatchdog,要让开门狗程序随系统启动而启动,则需要在/etc/xdg/autostart/目录下放置一个.desktop文件。
这里创建文件/etc/xdg/autostart/myWatchdog.desktop,文件内容如下:
[Desktop Entry]
Version=1.0
Name=myWatchdog.desktop
Exec=/home/myWatchdog
StartupNotify=false
NoDisplay=true
Type=Application
Categories=Utility;
建完/etc/xdg/autostart/myWatchdog.desktop文件之后,重启系统。
2.Windows上的看门狗软件的开发
Windows需要将开门狗软件做成windows服务程序,在服务程序里面启动应用程序即可,开门狗大致代码如下:
#include <Windows.h>
char gServiceName[] = "myWatchdog"; //服务名称
void WINAPI ServiceMain();
int APIENTRY main(int argn, const char ** argv)
{
//...
SERVICE_TABLE_ENTRY serviceTable[2] =
{
{ gServiceName, (LPSERVICE_MAIN_FUNCTION)ServiceMain },
{ NULL, NULL }
};
StartServiceCtrlDispatcher(serviceTable);
return 0;
}
SERVICE_STATUS_HANDLE gServiceStatus;
SERVICE_STATUS gStatus;
void ControlHandler(DWORD request)
{
switch (request)
{
case SERVICE_CONTROL_STOP:
gStatus.dwCurrentState = SERVICE_STOPPED;
gStatus.dwWin32ExitCode = 0;
::SetServiceStatus(gServiceStatus, &gStatus);
break;
case SERVICE_CONTROL_SHUTDOWN:
gStatus.dwCurrentState = SERVICE_STOPPED;
gStatus.dwWin32ExitCode = 0;
::SetServiceStatus(gServiceStatus, &gStatus);
break;
default:
::SetServiceStatus(gServiceStatus, &gStatus);
break;
}
return;
}
void WINAPI ServiceMain()
{
gStatus.dwServiceType = SERVICE_WIN32;
gStatus.dwCurrentState = SERVICE_START_PENDING;
gStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN;
gStatus.dwWin32ExitCode = 0;
gStatus.dwServiceSpecificExitCode = 0;
gStatus.dwCheckPoint = 0;
gStatus.dwWaitHint = 0;
gServiceStatus = ::RegisterServiceCtrlHandler(gServiceName, (LPHANDLER_FUNCTION)ControlHandler);
if (gServiceStatus == (SERVICE_STATUS_HANDLE)0)
{
return;
}
gStatus.dwCurrentState = SERVICE_RUNNING;
::SetServiceStatus(gServiceStatus, &gStatus);
while (true)
{
/*
* 检查被开门狗监视的软件的进程是否不存在了,如果不存在了,则要重新拉起来
*/
//拉起程序
lauchProgress();
}
}
2.1 无窗体
如果开门狗程序要拉起的是无窗体的程序,则拉起程序的函数lauchProgress()这样写:
void lauchProgress()
{
STARTUPINFO si;
::ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si);
PROCESS_INFORMATION pi;
::ZeroMemory(&pi, sizeof(pi));
BOOL bRet = ::CreateProcess(NULL, exePath, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi); //exePath是要拉起的程序的完整路径
if (bRet) //拉起程序成功
{
::CloseHandle(pi.hProcess);
}
else //拉起程序失败
{
//...
}
}
2.2 有窗体
如果开门狗程序要拉起的是有窗体的程序,则拉起程序的函数lauchProgress()这样写:
void lauchProgress()
{
HANDLE hUserToken;
DWORD id = ::WTSGetActiveConsoleSessionId();
if (!::WTSQueryUserToken(id, &hUserToken))
{
return; //拉起失败
}
STARTUPINFO si;
::ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si);
PROCESS_INFORMATION pi;
::ZeroMemory(&pi, sizeof(pi));
BOOL bRet = ::CreateProcessAsUser(hUserToken, NULL, exePath, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi); //exePath是要拉起的程序的完整路径
if (bRet) //拉起程序成功
{
::CloseHandle(pi.hProcess);
}
else //拉起程序失败
{
//...
}
}
看门狗程序编译成myWatchdog.exe程序之后,假设程序保存在d:\myWatchdog.exe,则需要使用命令安装服务:
sc create myWatchdog binPath="D:\myWatchdog.exe"
最后,进入“控制面板”->“系统和安全”->"Windows工具"->“服务”,在列表中找到"myWatchdog",设置为“自动启动”,即可。