ACE_Process进程管理

1 控制台程序启动时传递给main的参数

控制台程序不管启动的时候是否有窗口,其参数都只有一个,那就是exe的全路径加exe的名字

//运行时不显示控制台窗口
#pragma comment(linker,"/subsystem:\"Windows\" /entry:\"mainCRTStartup\"")

#include <fstream>
using namespace std;


int main(int argc, char** argv)
{
    ofstream fout("data.txt");
    for (int i = 0;i<argc;++i)
    {
        fout<<"argc="<<i<<",argv["<<i<<"]="<<argv[i]<<endl;
    }
    return 0;
}

这里写图片描述

进程可以理解成一个exe
控制台程序在启动运行的时候默认会调用

2 ACE_Process类计算阶乘n!

代表着一个进程的代理,可以通过ACE_Process_Options设置进程启动的参数,可以启动一个exe,可以获取进程退出的返回值,可以启动其他的子进程(由当前进程启动的进程就就当前进程的子进程)

下面的程序会启动一个进程,这个进程再递归启动子进程,从而实现计算10的阶乘,最终退出的进程将结果写入文件中。很有代表意义:

#ifdef _DEBUG
#pragma comment (lib,"ACEd.lib")
#else
#pragma comment (lib,"ACE.lib")
#endif

#include "ace/OS.h"
#include "ace/Log_Msg.h"
#include "ace/Process.h"
#include <fstream>
using namespace std;


int main(int argc, char** argv)
{
    //可移植的
    ACE_Process_Options options;

    int n = 10;

    //最高层的exe,程序第一次启动的那个exe
    if (argc == 1)
    {
        //调用子进程需要的命令行参数,第一个参数是当前exe名字说明还会再启动当前exe的副本,第二个参数是n-1
        options.command_line("%s %d",argv[0],n-1);
    }
    //到最后一个被启动的进程时,直接返回1
    else if (atoi(argv[1]) == 1)
    {
        return 1;//从这里就可以看出来main的返回值是干嘛的了吧
    } 
    else
    {
        //计算当前进程所属的n
        n = atoi(argv[1]);
        options.command_line("%s %d",argv[0],n-1);
    }

    ACE_Process child;
    //再启动一个当前exe的新副本,但是参数不同
    child.spawn(options);
    //child会等待新exe的退出之后再执行wait()之后的代码,wait()是可移植的
    child.wait();
    //当前进程所属的n乘以child创建的进程的返回值,作为当前进程的返回值
    ofstream fout("data.txt");
    int factorial = n* child.exit_code();
    fout<<factorial;
    return factorial;
}

这里写图片描述

下面的程序只是对上面的程序稍加改进,将每个进程的ID和进程计算的的阶乘打印到文件中

#ifdef _DEBUG
#pragma comment (lib,"ACEd.lib")
#else
#pragma comment (lib,"ACE.lib")
#endif

#include "ace/OS.h"
#include "ace/Log_Msg.h"
#include "ace/Process.h"
#include <fstream>
using namespace std;


int main(int argc, char** argv)
{
    //可移植的
    ACE_Process_Options options;

    int n = 10;

    //最高层的exe,程序第一次启动的那个exe
    if (argc == 1)
    {
        //调用子进程需要的命令行参数,第一个参数是当前exe名字说明还会再启动当前exe的副本,第二个参数是n-1
        options.command_line("%s %d",argv[0],n-1);
    }
    //到最后一个被启动的进程时,直接返回1
    else if (atoi(argv[1]) == 1)
    {
        return 1;//从这里就可以看出来main的返回值是干嘛的了吧
    } 
    else
    {
        //计算当前进程所属的n
        n = atoi(argv[1]);
        options.command_line("%s %d",argv[0],n-1);
    }

    ACE_Process child;
    //再启动一个当前exe的新副本,但是参数不同
    child.spawn(options);
    //child会等待新exe的退出之后再执行wait()之后的代码,wait()是可移植的
    child.wait();
    //各个子进程都向同一个文件中输出自己计算出的阶乘值
    ofstream fout("data.txt",ios::app);
    //当前进程所属的n乘以child创建的进程的返回值,作为当前进程的返回值
    int factorial = n* child.exit_code();
    fout<<"进程ID["<<ACE_OS::getpid()<<"] "<<n<<"!="<<factorial<<endl;
    return factorial;
}

这里写图片描述

3 利用ACE_Process实现看门狗(守护进程)

主要就原理:被守护进程B由看门进程A启动(通过B的进程名字B.exe),在启动之后注册进程的退出事件,当B退出时,A捕获退出事件,调用回调函数F,F会再次启动一次B。

Process_Manager_Class.h

#pragma once
//CProcessManager.h
#include <stdio.h>
#include <stdlib.h>
#include <vector>
#include <string>
#include <map>
using namespace std;

#include "ace/OS.h"
#include "ace/streams.h"
#include "ace/Log_Msg.h"
#include "ace/Date_Time.h"
#include "ace/Event_Handler.h"
#include "ace/Reactor.h"
#include "ace/Process_Manager.h"

// 进程监控,进程崩溃后,自动重新启动进程
class CProcessManager
{
public:
    CProcessManager()
    {
        ACE_DEBUG((LM_DEBUG,"CProcessManager()\n"));
    };
    virtual ~CProcessManager()
    {
        ACE_DEBUG((LM_DEBUG,"~CProcessManager()\n"));
    };
    // 从配置文件读取进程列表信息
    virtual int ReadConfigProcessInfo(char *pFileName);
    // 启动进程,并进行进程监控,进程崩溃后,自动重新启动进程
    virtual int ProcessMonitor();

private:
    vector<string> m_vConfigProcessInfo;       // 进程列表信息
};

// 回调事件,程序退出后,执行handle_exit函数
class CProcessExitHandler: public ACE_Event_Handler
{
public:
    CProcessExitHandler()
    {
        ACE_DEBUG((LM_DEBUG,"CProcessExitHandler(%d)\n",this->id_ = ++ increase_));
    };
    virtual ~CProcessExitHandler()
    {
        ACE_DEBUG((LM_DEBUG,"~CProcessExitHandler(%d)\n",this->id_));
    };
    // 程序退出后,执行该函数
    virtual int handle_exit(ACE_Process* process);
private:
    static int increase_;
    int id_;
};

Process_Manager_Class.cpp

//CProcessManager.cpp
#include "Process_Manager_Class.h"
#include <fstream>
using namespace std;
ACE_Process_Manager *g_pPM;             // 进程管理
map<string, pid_t> g_mapProcessInfo;    // 进程ID管理列表

int
CProcessExitHandler::increase_ = 0;
// 读取进程列表配置文件
int CProcessManager::ReadConfigProcessInfo(char *pFileName)
{
    if(NULL == pFileName) 
    {
        ACE_DEBUG((LM_ERROR, ACE_TEXT(": Parameter FileName is null.\n")));
        return -1;
    }
    // 打开进程列表配置文件
    ifstream fin(pFileName);
    if(!fin)
    {
        ACE_DEBUG((LM_ERROR, ACE_TEXT(": file open error.[%s]\n"), pFileName));
        return -2;
    }
    // 循环读取进程列表配置文件
    while(!fin.eof())
    {
        string program;
        getline(fin,program);
        if(0 == program.size() || program[0] == '#')// 去掉回车换行和空白符,跳过注释
        {
            continue;
        }
        // 把读取的进程信息放到[进程列表信息]全局变量
        m_vConfigProcessInfo.push_back(program);
    }
    // [进程列表信息]不应该为0
    if(m_vConfigProcessInfo.size() == 0)
    {
        ACE_DEBUG((LM_ERROR, ACE_TEXT(": Process list is null.[%s]\n"), pFileName));
        return -3;
    }
    return 0;
}

// 进程监控
// 根据配置文件启动进程,并注册进程退出时回调函数
int CProcessManager::ProcessMonitor()
{
    int nRet = 0;
    vector<string>::iterator itrConfig;
    string strStartProcess;

    m_vConfigProcessInfo.clear();

    // 读取进程列表配置文件
    nRet = ReadConfigProcessInfo("ProcessManager.conf");
    if(nRet != 0)
    {
        ACE_DEBUG((LM_ERROR, 
            ACE_TEXT(": ReadConfigProcessInfo[%s] error.\n"), "ProcessManager.conf"));
        return -1;
    }

    // Instantiate a process manager with space for 100 processes.
    g_pPM = new ACE_Process_Manager(ACE_Process_Manager::DEFAULT_SIZE, ACE_Reactor::instance());
    CProcessExitHandler procExitHandler;
    // 循环配置进程列表,启动进程
    for(itrConfig = m_vConfigProcessInfo.begin(); itrConfig != m_vConfigProcessInfo.end(); ++itrConfig)
    {
        strStartProcess = *itrConfig;
        // 启动进程
        ACE_Process_Options options;
        options.command_line(strStartProcess.c_str());
        pid_t pid = g_pPM->spawn(options);
        // 启动进程失败
        if (pid == ACE_INVALID_PID)
        {
            ACE_DEBUG((LM_ERROR, 
                ACE_TEXT(": start a child process failed(%d)%s\n"), 
                pid, strStartProcess.c_str()));
            return -1;
        }
        ACE_DEBUG((LM_INFO, 
            ACE_TEXT(": start a child process success(%d)%s\n"), 
            pid, strStartProcess.c_str()));
        // 注册回调(进程退出时,调用该回调)
        g_pPM->register_handler(&procExitHandler, pid);
        // 添加启动成功的进程到进程ID管理列表
        g_mapProcessInfo[strStartProcess] = pid;
        ACE_OS::sleep(1);
    }

    // Run the reactor event loop waiting for events to occur.
    ACE_Reactor::instance()->run_reactor_event_loop();
    //ACE_Reactor::instance()->end_reactor_event_loop();
    return 0;
}

// 进程退出回调函数
// 程序退出后,执行该函数,重新启动进程
int CProcessExitHandler::handle_exit(ACE_Process* process)
{
    //等待一段时间再重新启动新进程
    ACE_OS::sleep(3);
    map<string, pid_t>::iterator itrProcess;

    ACE_DEBUG((LM_INFO,
        ACE_TEXT(": Process %d exited with exit code %d\n"),
        process->getpid (), process->return_value()));

    // 循环进程ID管理列表,根据进程ID找到退出的进程,重新启动进程
    for(itrProcess = g_mapProcessInfo.begin(); itrProcess != g_mapProcessInfo.end(); ++itrProcess)
    {
        // 根据进程ID,查找进程名
        if(itrProcess->second == process->getpid())
        {
            // 重新启动进程
            ACE_Process_Options options;
            options.command_line(itrProcess->first.c_str());
            pid_t pid = g_pPM->spawn(options);
            // 重新启动进程失败
            if (pid == ACE_INVALID_PID)
            {
                ACE_DEBUG((LM_ERROR, 
                    ACE_TEXT(": restart a child process error(%d)%s\n"), 
                    pid, itrProcess->first.c_str()));
                return -1;
            }
            ACE_DEBUG((LM_INFO, 
                ACE_TEXT(": restart a child process success(%d)%s\n"), 
                pid, itrProcess->first.c_str()));
            // 注册回调(进程退出时,调用该回调),当前对象没有代码负责释放,
            //对象自始至终只有一个副本,不会创建更多的CProcessExitHandler
            g_pPM->register_handler(this, pid);
            // 添加启动成功的进程到进程ID管理列表
            itrProcess->second = pid;
            break;
        }
    }
    return 0;
}

Process_Manager_Class_main.cpp

#ifdef _DEBUG
#pragma comment (lib,"ACEd.lib")
#else
#pragma comment (lib,"ACE.lib")
#endif


#pragma comment( linker, "/subsystem:\"windows\" /entry:\"mainCRTStartup\"")


#include "Process_Manager_Class.h"
#include <string>
int main(int argc, char *argv[])
{
    // Running as a child for test.
    if (argc > 1)
    {
        ACE_OS::sleep(10);
        return 0;
    }
    // set output log file
    ACE_OS::setprogname("ProcessManager");
    ACE_Date_Time tvTime(ACE_OS::gettimeofday());
    string szLogFileName;
    szLogFileName =  "log.log";

    ACE_OSTREAM_TYPE *pLogOutput = new ofstream(szLogFileName.c_str());
    ACE_LOG_MSG->msg_ostream(pLogOutput, true);
    ACE_LOG_MSG->set_flags(ACE_Log_Msg::OSTREAM);
    // Prepends timestamp and message priority to each message
    //ACE_LOG_MSG->set_flags(ACE_Log_Msg::VERBOSE_LITE);
    //ACE_LOG_MSG->clr_flags(ACE_Log_Msg::STDERR);

    ACE_DEBUG((LM_INFO, ACE_TEXT(": ---ProcessManager START---.\n")));
    int nRet = 0;
    CProcessManager *pProcMng = new CProcessManager();
    // 启动进程监控
    nRet = pProcMng->ProcessMonitor();
    if(nRet != 0)
    {
        ACE_DEBUG((LM_ERROR, ACE_TEXT(": ---ProcessMonitor Error---\n")));
    }
    delete pProcMng;
    ACE_DEBUG((LM_INFO, ACE_TEXT(": ---ProcessManager STOP---.\n")));
    return 0;
}

配置文件:
ProcessManager.conf
内容:B

参考:http://blog.csdn.net/colder2008/article/details/5865142

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

C++程序员Carea

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值