linux下基于boost/process库实现多进程管理,基于c++开发
本文介绍了基于boost/process库实现子线程的创建、kill等管理操作,基于linux c++开发。
文章目录
一.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分把,感谢各位大佬支持!
更多推荐
所有评论(0)