一.boost/process库介绍

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

例程:

#include <boost/process.hpp>

#include <string>
#include <iostream>

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();
}

进程管理:
  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");

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

//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_);

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

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

2.1 主进程程序

2.1.1 main.cpp

#include <thread>
#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;
}

2.1.2 processhelper.hpp

#include <iostream>
#include <vector>
#include <string>
#include <tuple>

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);
}

2.1.3 processhelper.cpp

#include <boost/process.hpp>

#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

结合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 <iostream>  
#include <thread>    
#include <boost/process.hpp>
#include <signal.h>

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;
}

编译:

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 <thread>
#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;
}

2.2.2 subprocess.cpp

#include <iostream>  
#include <thread>    
#include <boost/process.hpp>
#include <signal.h>

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;
}

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掉主进程,更加丰富的功能可基于该库进行拓展。

五. 感谢支持

    完结撒花!希望看到这里的小伙伴能点个关注,我后续会持续更新更多关于操作系统的原理和实现,也欢迎大家广泛交流。
    码字实属不易,如果本文对你有10分帮助,就赏个10分把,感谢各位大佬支持!

在这里插入图片描述

Logo

更多推荐