优秀的编程知识分享平台

网站首页 > 技术文章 正文

Ubuntu和Windows的看门狗软件开发

nanyue 2024-08-25 10:18:19 技术文章 6 ℃

为了保证软件运行的鲁棒性,常常在软件设计中,启用看门狗软件。对于长时间运行的软件,处于各种不可预知的错误,例如:软件的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",设置为“自动启动”,即可。

Tags:

最近发表
标签列表