• linux下基于boost/process库实现多进程管理,基于c++开发


    一.boost/process库介绍

      什么是boost::process:一个进程管理模块,可以启动等待终止子进程,和子进程通信
      Boost.Process提供了一个灵活的C++ 进程管理框架。它允许C++ developer可以像Java和.Net程序developer那样管理进程。它还提供了管理当前执行进程上下文、创建子进程、用C++ 流和异步I/O进行通信的能力。该库以完全透明的方式将所有进程管理的抽象细节呈现给使用者,且该库是跨平台的。

    例程:

    #include 
    
    #include 
    #include 
    
    using namespace boost::process;
    
    int main()
    {
        ipstream pipe_stream; // 创建管道流
        // 执行gcc --version,并将该子进程的stdout重定向到前面创建的管道
        // std_out是boost::process定义的一个标签,使用形如 std_out>ipstream的语句表示重定向子进程stdout
        // boost::process是平台无关的,因此这只是一个标签,真正的重定向流程发生在底层逻辑
        child c("gcc --version", std_out > pipe_stream); 
        std::string line;
        // 不断地从管道中读取子进程的输出,并通过父进程(也就是自己)的stderr输出
        // ~~又是个不用花括号的败家子~~
        while (pipe_stream && std::getline(pipe_stream, line) && !line.empty())
            std::cerr << line << std::endl;
        // 等待子进程退出,类似std::thread::join()
        c.wait();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    进程管理:
      Boost.Process的长期目标是提供一个抽象于操作系统之上的,可以管理任何运行的进程的框架。由于提供这样的API比较困难,所以现在只专注于管理。
      Boost.Process的最重要的特征就是启动一个外部应用、控制它们并与它们交互。传统上讲,对于C和C++ 来说,就比较困难了,因为它们要启动新进程、执行外部程序、建立匿名管道来交互、等待进程结束、检查进程退出码等。更糟糕的是不同操作系统,相关的进程模块和API是不同的。所以,Boost.Process的出现就提供了便利条件。

    boost::process::system:
    (以下使用bp指代boost::process,例如bp::system指代boost::process::system)
      最简单的调用方法,启动子进程,执行结束后返回。阻塞式。和Windows下的system()是一样的。使用一个const char*传递执行的命令,函数阻塞到子进程执行完毕后返回exit_code。
      不同的是,bp::system除了传统的传参方式(比如gcc -v)以外,还可以单独传参,即bp::system(“gcc”, “-v”)。
      bp::system可以使用boost提供的error code。传递一个system_error对象即可。
      bp::system可以使用同步IO。在参数中追加形如“<标签> <操作符> <目标>”的形式可以将子进程的流重定向到其他流(包括文件流)。标签包括bp::std_in、bp::std_out、bp::std_err。操作符为尖括号(语言层面上,应该是重载了标签的运算符从而实现的trick)。目标可以是boost的管道、标准输入输出流、文件流等各种stream,也可以是文件名、boost定义的其他标签。
      以下流操作都是合法的,每个流操作作为一个函数参数传入bp::system即可:

    /* 重定向到标准输入输出 */
    bp::std_out > stdout
    bp::std_err > stderr
    bp::std_in < stdin
    /* 重定向到boost提供的流 */
    bp::std_out > bp::null
    /* 重定向到文件,使用文件名即可 */
    bp::std_out > "gcc_out.log"
    完整的调用例如:
    bp::system("g++ main.cpp", bp::std_out > "gcc_out.log");
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    环境变量:
      使用boost::this_process::environment();获取当前进程的环境变量。可以像使用std::map一样使用该对象:

    //get a handle to the current environment
    auto env = boost::this_process::environment();
    //add a variable to the current environment
    env["VALUE_1"] = "foo";
    
    //copy it into an environment separate to the one of this process
    bp::environment env_ = env;
    //append two values to a variable in the new env
    env_["VALUE_2"] += {"bar1", "bar2"};
    
    //launch a process with `env_`
    bp::system("stuff", env_);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    二.实现子进程的创建及管理

      下面基于boost/process库实现了一个子进程的创建,argv参数传递,kill等操作;下面结合源代码实现给出分析:

    2.1 主进程程序

    2.1.1 main.cpp

    #include 
    #include "processhelper.hpp"
    using namespace std;
    using namespace ProcessHelper;
    
    int main()
    {
        // 传递给子进程的两个参数
        std::string processArgv = "argv1 argv2";  
        // 要调用的
        std::string processName = "subprocess";
        // 启动子进程
        auto newtup = ProcessHelper::startProcess(processName, processArgv);
        auto pid = std::get<0>(newtup);      // 获取子进程的pid号
        auto err_msg = std::get<1>(newtup);  // 获取创建子进程过程中产生的错误信息
    
        // 查询进程是否在运行
        if (0 != ProcessHelper::isRunning(pid))
        {
            std::cerr << "subprocess " << pid << " is not running!" << std::endl;
            std::cerr << "error massage: " << err_msg << std::endl;
            return -1;
        }
        else {
            std::cout << "subprocess " << pid << " is running!" << std::endl;
        }
    
    	// 主进程中延时10s,让子进程执行,10s后回收子进程
        this_thread::sleep_for(10s);
    
        // 根据进程名查询所有的pids
        auto tup = ProcessHelper::getAllProcessPidsByName(processName);
        auto pids = std::get<0>(tup);
        // 杀掉所有的同名进程
        for (auto pid : pids) 
        {
            std::cout << "killing " << pid << std::endl;
            ProcessHelper::killProcess(pid, true);
            if (0 != ProcessHelper::isRunning(pid))
                std::cout << "process " << pid << " killed!" << std::endl;
        }
    
        while(1)
        {
            this_thread::sleep_for(1s);
            std::cout<<"主进程运行中...."<<std::endl;
            std::cout<<"pid = "<<getCurrentProcessId()<<std::endl;
            std::cout<<"ppid = "<<getParentProcessId()<<std::endl;
            std::cout<<"主进程运行中...."<<std::endl;
        }
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52

    2.1.2 processhelper.hpp

    #include 
    #include 
    #include 
    #include 
    
    using namespace std;
    
    namespace ProcessHelper
    {
        // 获取时间戳
        uint64_t getTimeStamp();        
        // 获取当前进程PID      
        uint32_t getCurrentProcessId();
        // 获取父进程ID      
        uint32_t getParentProcessId();
        // 获取进程运行时间   
        std::string getProcessRunningTime(uint64_t startTimeStamp);
        // 判断当前进程的运行状态
        int isRunning(int pid);
        // 根据进程id杀子进程
        std::tuple<bool, std::string> killChildProcess(int pid);
        // 根据进程id杀子进程
        std::tuple<bool, std::string> killProcess(int pid, bool isChild = true);
        // 根据进程名获取所有进程
        std::tuple<std::vector<int>, std::string> getAllProcessPidsByName(const std::string &processName);
        // 根据进程名启动进程
        std::tuple<int, std::string> startProcess(const std::string &processName, const std::string &processArgv);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    2.1.3 processhelper.cpp

    #include 
    
    #include "processhelper.hpp"
    
    namespace ProcessHelper
    {
        // 获取时间戳
        uint64_t getTimeStamp()
        {
            std::chrono::milliseconds ms = std::chrono::duration_cast<std::chrono::milliseconds>(
                std::chrono::system_clock::now().time_since_epoch());
            return ms.count();
        }
    
        // 获取当前进程PID      
        uint32_t getCurrentProcessId()
        {
            return ::getpid();
        }
    
        // 获取父进程PID      
        uint32_t getParentProcessId()
        {
            return ::getppid();
        }
    
        // 获取进程运行时间   
        std::string getProcessRunningTime(uint64_t startTimeStamp) // return minutes
        {
            auto now = getTimeStamp();
            auto diff = now - startTimeStamp;
            auto min = double(diff) / (1000 * 60);
            return std::to_string(min);
        }
    
        // 0 : running, -1 : exit, -2 : zombie(僵尸进程)
        int isRunning(int pid)
        {
            if (0 == kill(pid, 0))
            {
                std::string path = std::string("/proc/") + std::to_string(pid) + "/status";
                std::ifstream fs;
                fs.open(path);
                if (!fs)
                    return -1;
    
                std::string line;
                while (getline(fs, line))
                {
                    std::size_t found = line.find("State:");
                    if (found == std::string::npos)
                        continue;
                    else
                    {
                        found = line.find("zombie");
                        if (found == std::string::npos)
                            return 0;
                        else
                            return -2; // zombie
                    }
                }
            }
            else
                return -1;
        }
    
        // 根据进程id杀子进程
        std::tuple<bool, std::string> killChildProcess(int pid)
        {
            std::string err;
            bool ret = false;
            try
            {
                auto id = boost::process::pid_t(pid);
                boost::process::child pros(id);
                std::error_code ec;
                pros.terminate(ec);
    
                if (ec.value() == 0)
                    return std::make_tuple(true, err);
            }
            catch (boost::process::process_error &exc)
            {
                err = exc.what();
            }
            return std::make_tuple(false, err);
        }
    
        // 根据进程id杀子进程
        std::tuple<bool, std::string> killProcess(int pid, bool isChild)
        {
            std::string err;
            bool ret = false;
            if (isChild)
                return killChildProcess(pid);
            else    // if not a child process,will not kill the process correctly
            {
                std::string cmd("kill -9 ");
                cmd += std::to_string(pid);
                auto ret = boost::process::system(cmd);
                if (ret == 0)
                    return std::make_tuple(true, err);
            }
            return std::make_tuple(false, err);
        }
    
        // 根据进程名获取所有进程
        std::tuple<std::vector<int>, std::string> getAllProcessPidsByName(const std::string &processName)
        {
            std::vector<int> pids;
            std::string err;
            try
            {
                boost::filesystem::path path = "/proc";
                boost::filesystem::directory_iterator end;
    
                for (boost::filesystem::directory_iterator iter(path); iter != end; iter++)
                {
                    boost::filesystem::path p = *iter;
                    std::ifstream statusFile;
                    statusFile.open(p.string() + std::string("/status"));
                    if (!statusFile)
                        continue;
    
                    std::string statusContent;
                    getline(statusFile, statusContent);
                    std::vector<std::string> a;
                    boost::algorithm::split(a, statusContent, boost::algorithm::is_any_of(":"), boost::algorithm::token_compress_on);
    
                    if (boost::algorithm::trim_copy(a[1]) == processName)
                    {
                        pids.push_back(std::stoi(p.leaf().string()));
                    }
                    statusFile.close();
                    statusFile.clear();
                }
            }
            catch (boost::process::process_error &exc)
            {
                err = exc.what();
            }
            return std::make_tuple(std::move(pids), err);
        }
    
        // 根据进程名启动进程
        std::tuple<int, std::string> startProcess(const std::string &processName, const std::string &processArgv)
        {
            int pid = -1;
            std::string err;
    
            try
            {
                auto p = processName;
                p = boost::filesystem::current_path().string() + "/" + p;
                // 首先判断进程可执行程序是否存在
                if (!boost::filesystem::exists(p))
                {
                    err = "subprocess not exist";
                    return std::make_tuple(pid, err);
                }
                p = p + " " + processArgv;
                boost::process::child c(p);  // 启动一个子进程
                pid = c.id();
                // detach as a single process
                c.detach();
            }
            catch (boost::process::process_error &exc)
            {
                err = exc.what();
                pid = -1;
            }
            return std::make_tuple(pid, err);
        }
    } // namespace ProcessHelper
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174

    结合main.cpp分析,主要做了以下几件事情:

    • 根据processName启动子进程(ProcessHelper::startProcess),返回子进程的pid值和错误信息;
    • 查询子进程的状态(ProcessHelper::isRunning);
    • 主进程中延时等待10s(sleep_for(10s)),等待子进程执行后kill;
    • 根据processName查询所有子进程的pid,然后kill(ProcessHelper::killProcess);
    • 主进程执行…

    2.1.4 编译

    g++ main.cpp processhelper.cpp -lboost_system -lpthread -lboost_filesystem

    2.2 子进程程序

    subprocess.cpp

    #include   
    #include     
    #include 
    #include 
    
    using namespace std;
    
    int main(int argc, char** argv)
    {
        while(1)
        {
            std::cout<<"子进程运行中...."<<std::endl;
            printf("pid = %d\n",::getpid());
            printf("ppid = %d\n",::getppid());
            std::cout<<"argc = "<<argc<<std::endl;
            for(int i=1;i<argc;i++)
                std::cout<<"argv["<<i<<"] = "<<argv[i]<<std::endl;
            std::cout<<"子进程运行中...."<<std::endl;
            std::this_thread::sleep_for(1s);
        }
        return 0;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    编译:

    g++ subprocess.cpp -o subprocess -lboost_system -lpthread

    2.3 文件目录

      经过编译后,主进程生成a.out作为主进程执行文件,生成的subprocess作为子进程的执行文件,文件目录如下:
    在这里插入图片描述

    2.3 执行

    确保子进程程序已经存在当前目录下,并按照processName命名,执行./a.out输出结果如下:
    在这里插入图片描述
    在这里插入图片描述

    三.在子进程中kill父进程

      在一些情况下,在子进程中定时判断某些条件的状态,当发送某些状态时在子进程中kill父进程,下面给出了一个基本的demo,在子进程中sleep(3s),3s后调用系统命令将父进程kill掉。只需要更改上面的mian.cpp和subprocess.cpp文件,并分别编译生成a.out和subprocess执行文件。

    2.2.1 mian.cpp

    #include 
    #include "processhelper.hpp"
    using namespace std;
    using namespace ProcessHelper;
    
    int main()
    {
        // 传递给子进程的两个参数
        std::string processArgv = "argv1 argv2";  
        // 要调用的
        std::string processName = "subprocess";
        // 启动子进程
        auto newtup = ProcessHelper::startProcess(processName, processArgv);
        auto pid = std::get<0>(newtup);      // 获取子进程的pid号
        auto err_msg = std::get<1>(newtup);  // 获取创建子进程过程中产生的错误信息
    
        // 查询进程是否在运行
        if (0 != ProcessHelper::isRunning(pid))
        {
            std::cerr << "subprocess " << pid << " is not running!" << std::endl;
            std::cerr << "error massage: " << err_msg << std::endl;
            return -1;
        }
        else {
            std::cout << "subprocess " << pid << " is running!" << std::endl;
        }
    
        while(1)
        {
            this_thread::sleep_for(1s);
            std::cout<<"主进程运行中...."<<std::endl;
            std::cout<<"pid = "<<getCurrentProcessId()<<std::endl;
            std::cout<<"ppid = "<<getParentProcessId()<<std::endl;
            std::cout<<"主进程运行中...."<<std::endl;
        }
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37

    2.2.2 subprocess.cpp

    #include   
    #include     
    #include 
    #include 
    
    using namespace std;
    
    int main(int argc, char** argv)
    {
        std::cout<<"子进程运行中...."<<std::endl;
        printf("pid = %d\n",::getpid());
        printf("ppid = %d\n",::getppid());
        std::cout<<"argc = "<<argc<<std::endl;
        for(int i=1;i<argc;i++)
            std::cout<<"argv["<<i<<"] = "<<argv[i]<<std::endl;
        std::cout<<"子进程运行中...."<<std::endl;
        std::this_thread::sleep_for(3s);
        
        std::string cmd("kill -9 ");
        cmd += std::to_string(::getppid());
        auto ret = boost::process::system(cmd);
        if (ret == 0)
            std::cout<<"父进程kill成功!!!"<<std::endl;
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    2.2.3 编译 & 执行:

    g++ main.cpp processhelper.cpp -lboost_system -lpthread -lboost_filesystem
    g++ subprocess.cpp -o subprocess -lboost_system -lpthread
    ./a.out

    执行结果:
    在这里插入图片描述

    四.总结

      本文基于boost/process库实现了对子进程的创建、管理;在主进程中可kill掉子进程;在子进程中可kill掉主进程,更加丰富的功能可基于该库进行拓展。

  • 相关阅读:
    30分钟部署一个kubernetes集群【1.17】
    googleTest V1.12.1的基础用法
    vue编写分页组件
    蓝桥杯 2240. 买钢笔和铅笔的方案数c++解法
    高并发下的缓存击穿、雪崩、穿透和分布式锁(三)
    OKR助理源代码说明
    时序数据库 TimescaleDB 安装与使用
    Guava Cache 异步刷新技巧,你值得拥有!
    C语言系统化精讲(四):C语言变量和数据类型-下篇
    codeforces刷题二
  • 原文地址:https://blog.csdn.net/weixin_42700740/article/details/126584599