概述

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

特点

进程管理

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

输入输出重定向

一般来说一个应用启动了子进程,它们可能会通过传输数据来交流。这种进程间通信是文件句柄层面的,通常涉及stdin、stdout、stderr。如果操作系统支持,那么就需要可重定向的流。不过这对C++ 来说是很容易的。

不同操作模式

支持同步、异步、分离

管道管理

这样就可以实现一个进程的输出可以作为另一个进程的输入。

库的设计图

最重要的类就是Context和Process。Context提供了进程运行的上下文。pistream和postream是为了交互。父进程还可以等待子进程退出,并检查进程退出码。如果有例如包含管道的shell命令要执行,那么pipeline_entry就应运而生了,它可以实现前一个子进程的输出是下一个子进程的输入。

使用步骤

1、创建上下文Context
2、创建子进程,获得子进程对象
3、如果有重定向,可以访问到stdin、stdout、stderr
4、进程结束,检查进程退出码

教程

一个最简单的例子

#include <boost/filesystem.hpp>
#include <boost/process.hpp>
#include <string>
#include <vector>
 
namespace bp = ::boost::process;
 
bp::child start_child()
{
    std::string exec = "bjam";
    std::vector<std::string> args;
    args.push_back("bjam");
    args.push_back("--version");
 
    bp::context ctx;
    ctx.stdout_behavior = bp::capture_stream();
 
    return bp::launch(exec, args, ctx);
}
 
int main()
{
    bp::child c = start_child();
 
    bp::pistream &is = c.get_stdout();
    std::string line;
    while (std::getline(is, line))
        std::cout << line << std::endl;
    bp::status s = c.wait();
 
    return s.exited() ? s.exit_status() : EXIT_FAILURE;
}

下面再看一个异步的例子

#include <boost/filesystem.hpp>
#include <boost/asio.hpp>
#include <boost/process.hpp>
#include <boost/array.hpp>
#include <boost/bind.hpp>
#include <string>
#include <vector>
#include <iostream>
 
namespace bp = ::boost::process;
namespace ba = ::boost::asio;
 
ba::io_service io_service;
boost::array<char, 4096> buffer;
 
ba::posix::stream_descriptor in(io_service);
 
bp::child start_child()
{
    std::string exec = "bjam";
 
    std::vector<std::string> args;
    args.push_back("bjam");
    args.push_back("--version");
 
    bp::context ctx;
    ctx.stdout_behavior = bp::capture_stream();
    ctx.environment = bp::self::get_environment();
 
    return bp::launch(exec, args, ctx);
}
 
void end_read(const boost::system::error_code &ec, std::size_t bytes_transferred);
 
void begin_read()
{
    in.async_read_some(boost::asio::buffer(buffer),
        boost::bind(&end_read, ba::placeholders::error, ba::placeholders::bytes_transferred));
}
 
void end_read(const boost::system::error_code &ec, std::size_t bytes_transferred)
{
    if (!ec)
    {
        std::cout << std::string(buffer.data(), bytes_transferred) << std::flush;
        begin_read();
    }
}
 
int main()
{
    bp::child c = start_child();
 
    bp::pistream &is = c.get_stdout();
    in.assign(is.handle().release());
 
    begin_read();
    io_service.run();
 
    c.wait();
}

这个例子中用到了asio库,涉及到许多回调函数。关于异步(asio)暂时不做讲解,写这个例子是为了展示该库的异步功能。对异步感兴趣的同学可以看一下《Boost.Asio C++ Network Programming

部分文件和类

stream_behaviour.hpp文件

对于流的描述,可分为六种类型

序号 流描述 含义
1 capture  父子进程之间通过无名管道相互接收数据 
2 close  启动时关闭 
3 inherit  父子进程共用一个,也即继承 
4 redirect_to_stdout  主要用在stderr时,重定向到stdout 
5 silence  输出重定向到/dev/null 
6 posix_redirect  将输出重定向到指定的文件描符,是对redirect_to_stdout的扩展 

以下是等价的
boost::process::child::get_stdin() <==> boost::process::posix_child::get_input(STDIN_FILENO)
boost::process::child::get_stdout() <==> boost::process::posix_child::get_output(STDOUT_FILENO)
boost::process::child::get_stderr() <==> boost::process::posix_child::get_output(STDERR_FILENO)

重定向的例子
#include <boost/process.hpp>
#include <boost/filesystem.hpp>
#include <string>
#include <vector>
#include <iostream>
#include <cstdlib>
#include <unistd.h>
 
namespace bp = ::boost::process;
 
bp::posix_child start_child()
{
    std::string exec = bp::find_executable_in_path("dbus-daemon");
 
    std::vector<std::string> args;
    args.push_back("dbus-daemon");
    args.push_back("--fork");
    args.push_back("--session");
    args.push_back("--print-address=3");
    args.push_back("--print-pid=4");
 
    bp::posix_context ctx;
    ctx.output_behavior.insert(bp::behavior_map::value_type(STDOUT_FILENO, bp::inherit_stream()));
    ctx.output_behavior.insert(bp::behavior_map::value_type(STDERR_FILENO, bp::inherit_stream()));
    ctx.output_behavior.insert(bp::behavior_map::value_type(3, bp::capture_stream()));
    ctx.output_behavior.insert(bp::behavior_map::value_type(4, bp::capture_stream()));
 
    return bp::posix_launch(exec, args, ctx);
}
 
int main()
{
    try
    {
        bp::posix_child c = start_child();
 
        std::string address;
        pid_t pid;
        c.get_output(3) >> address;
        c.get_output(4) >> pid;
 
        bp::status s = c.wait();
        if (s.exited())
        {
            if (s.exit_status() == EXIT_SUCCESS)
            {
                std::cout << "D-BUS daemon's address is: " << address << std::endl;
                std::cout << "D-BUS daemon's PID is: " << pid << std::endl;
            }
            else
                std::cout << "D-BUS daemon returned error condition: " << s.exit_status() << std::endl;
        }
        else
            std::cout << "D-BUS daemon terminated abnormally" << std::endl;
 
        return s.exited() ? s.exit_status() : EXIT_FAILURE;
    }
    catch (boost::filesystem::filesystem_error &ex)
    {
        std::cout << ex.what() << std::endl;
        return EXIT_FAILURE;
    }
}
boost::process::context类
template <class Path>
class basic_context : public basic_work_directory_context<Path>, public environment_context
{
public:
    /**
     * Child's stdin behavior.
     */
    stream_behavior stdin_behavior;
 
    /**
     * Child's stdout behavior.
     */
    stream_behavior stdout_behavior;
 
    /**
     * Child's stderr behavior.
     */
    stream_behavior stderr_behavior;
};
 
typedef basic_context<std::string> context;

而basic_work_directory_context是用来设置工作目录的;environment_context实质上是个包装了boost::process::environment的类,boost::process::environment是一个map<string, string>,用以保存环境变量。

boost::process::posix_context类
typedef std::map<int, stream_behavior> behavior_map;
 
template <class Path>
class posix_basic_context : public basic_work_directory_context<Path>, public environment_context
{
public:
    /**
     * Constructs a new POSIX-specific context.
     *
     * Constructs a new context. It is configured as follows:
     * * All communcation channels with the child process are closed.
     * * There are no channel mergings.
     * * The initial work directory of the child processes is set to the
     *   current working directory.
     * * The environment variables table is empty.
     * * The credentials are the same as those of the current process.
     */
    posix_basic_context()
        : uid(::getuid()),
        euid(::geteuid()),
        gid(::getgid()),
        egid(::getegid())
    {
    }
 
    /**
     * List of input streams that will be redirected.
     */
    behavior_map input_behavior;
 
    /**
     * List of output streams that will be redirected.
     */
    behavior_map output_behavior;
 
    /**
     * The user credentials.
     *
     * UID that specifies the user credentials to use to run the %child
     * process. Defaults to the current UID.
     */
    uid_t uid;
 
    /**
     * The effective user credentials.
     *
     * EUID that specifies the effective user credentials to use to run
     * the %child process. Defaults to the current EUID.
     */
    uid_t euid;
 
    /**
     * The group credentials.
     *
     * GID that specifies the group credentials to use to run the %child
     * process. Defaults to the current GID.
     */
    gid_t gid;
 
    /**
     * The effective group credentials.
     *
     * EGID that specifies the effective group credentials to use to run
     * the %child process. Defaults to the current EGID.
     */
    gid_t egid;
 
    /**
     * The chroot directory, if any.
     *
     * Specifies the directory in which the %child process is chrooted
     * before execution. Empty if this feature is not desired.
     */
    Path chroot;
};
 
/**
 * Default instantiation of posix_basic_context.
 */
typedef posix_basic_context<std::string> posix_context;

函数boost::process::self::get_environment()可以得到当前进程的环境变量。
我们可以对环境变量进行修改,如
boost::process::environment_context env;
env.insert(boost::process::environment::valuetype(“A”, “a”));

进程结束码类信息
class status
{
    friend class child;
 
public:
    /**
     * 进程是否正常退出
     */
    bool exited() const;
 
    /**
     * 进程返回值
     */
    int exit_status() const;
 
protected:
    status(int flags);
 
    ...
};
 
class posix_status : public status
{
public:
    posix_status(const status &s);
 
    /**
     * 进程是否因为信号终止
     */
    bool signaled() const;
 
    /**
     * 如果因为信号终止,那么是因为哪个信号终止的
     */
    int term_signal() const;
    /**
     * 是否core dump了
     */
    bool dumped_core() const;
 
    /**
     * 进程是否因为收到信号停止
     */
    bool stopped() const;
 
    /**
     * 如果进程因为收到信号停止,那么信号是哪个
     */
    int stop_signal() const;
}
进程类对象信息
class process
{
public:
    typedef pid_t id_type;
    process(id_type id);
 
    /**
     * Returns the process' identifier.
     */
    id_type get_id() const;
 
    /**
     * 强制终止一个进程,force为真则用SIGKILL杀死,否则用SIGTERM杀死
     */
    void terminate(bool force = false) const ;
private:
    ...
};
 
class child : public process
{
public:
    /**
     * 获得标准输出
     */
    postream &get_stdin() const;
 
    /**
     * 获得标准输入
     */
    pistream &get_stdout() const;
 
    /**
     * 获得标准错误输入
     */
    pistream &get_stderr() const;
 
    /**
     * 阻塞等待进程退出,返回状态码对象
     */
    status wait();
 
    /**
     * 创建一个子进程对象
     */
    child(id_type id, detail::file_handle fhstdin, detail::file_handle fhstdout, detail::file_handle fhstderr, detail::file_handle fhprocess = detail::file_handle());
 
private:
    ...
};
 
class posix_child : public child
{
public:
    /**
     * 从指定描述符获得一个输出流
     */
    postream &get_input(int desc) const;
 
    /**
     * 从指定描述符获得一个输入流
     */
    pistream &get_output(int desc) const;
 
    /**
     *构造函数
     */
    posix_child(id_type id, detail::info_map &infoin, detail::info_map &infoout);
 
private:
    ...
};
children类

children类实际上std::vector<child>。children的启动方式是一个输出流被链接到下一个子进程的输入流上。

#include <boost/process.hpp>
#include <string>
#include <vector>
#include <iostream>
#include <fstream>
#include <cstdlib>
 
namespace bp = ::boost::process;
 
bp::children start_children()
{
    bp::context ctxin;
    ctxin.stdin_behavior = bp::capture_stream();
 
    bp::context ctxout;
    ctxout.stdout_behavior = bp::inherit_stream();
    ctxout.stderr_behavior = bp::redirect_stream_to_stdout();
 
    std::string exec1 = bp::find_executable_in_path("cut");
    std::vector<std::string> args1;
    args1.push_back("cut");
    args1.push_back("-d ");
    args1.push_back("-f2-5");
 
    std::string exec2 = bp::find_executable_in_path("sed");
    std::vector<std::string> args2;
    args2.push_back("sed");
    args2.push_back("s,^,line: >>>,");
 
    std::string exec3 = bp::find_executable_in_path("sed");
    std::vector<std::string> args3;
    args3.push_back("sed");
    args3.push_back("s,$,<<<,");
 
    std::vector<bp::pipeline_entry> entries;
    entries.push_back(bp::pipeline_entry(exec1, args1, ctxin));
    entries.push_back(bp::pipeline_entry(exec2, args2, ctxout));
    entries.push_back(bp::pipeline_entry(exec3, args3, ctxout));
 
    return bp::launch_pipeline(entries);
}
 
int main(int argc, char *argv[])
{
    try
    {
        if (argc < 2)
        {
            std::cerr << "Please specify a file name" << std::endl;
            return EXIT_FAILURE;
        }
 
        std::ifstream file(argv[1]);
        if (!file)
        {
            std::cerr << "Cannot open file" << std::endl;
            return EXIT_FAILURE;
        }
 
        bp::children cs = start_children();
 
        bp::postream &os = cs.front().get_stdin();
        std::string line;
        while (std::getline(file, line))
            os << line << std::endl;
        os.close();
 
        bp::status s = bp::wait_children(cs);
 
        return s.exited() ? s.exit_status() : EXIT_FAILURE;
    }
    catch (boost::filesystem::filesystem_error &ex)
    {
        std::cout << ex.what() << std::endl;
        return EXIT_FAILURE;
    }
}

需要注意的是,wait_children出错时,返回第一个子进程的退出码,所有子进程都正常退出时,返回最后一个子进程的退出码。

master3中大量用到进程管理这个库。这个Boost.Process库可以在这里获得点这里

boost:进程管理的更多相关文章

  1. C++ Windows进程管理

    功能: 1.各个进程启动.挂起.恢复.停止等 2.监听进程的运行状态,进程退出(正常.非正常)时,通知用户 3.异步队列 4.线程安全 进程管理器类: #ifndef __ProcessManager ...

  2. 《Linux内核设计与实现》读书笔记 第三章 进程管理

    第三章进程管理 进程是Unix操作系统抽象概念中最基本的一种.我们拥有操作系统就是为了运行用户程序,因此,进程管理就是所有操作系统的心脏所在. 3.1进程 概念: 进程:处于执行期的程序.但不仅局限于 ...

  3. 进程管理三大扩展工具htop

    三大进程管理监控工具 HTOP 介绍: Htop是一款运行于Linux系统监控与进程管理软件,htop提供所有进程的列表,并且使用彩色标识出处理器.swap和内存状态.用户一般可以在top无法提供详尽 ...

  4. Linux进程管理子系统分析【转】

    本文转载自:http://blog.csdn.net/coding__madman/article/details/51298732 Linux进程管理: 进程与程序: 程序:存放在磁盘上的一系列代码 ...

  5. Linux下取代top的进程管理工具 htop

    一.htop 简介 This is htop, an interactive process viewer for Linux. It is a text-mode application (for ...

  6. Linux进程管理

    一.进程管理简介 进程是正在执行的程序或命令,每一个进程都是一个运行实体,都有自己的地址空间,并占用一定的系统资源. 进程管理的作用: 1.判断服务器的健康状态 2.查看系统中的所有进程 3.杀死进程 ...

  7. 12个Linux进程管理命令介绍(转)

    12个Linux进程管理命令介绍 [日期:2015-06-02] 来源:Linux中国  作者:Linux [字体:大 中 小]   执行中的程序在称作进程.当程序以可执行文件存放在存储中,并且运行的 ...

  8. 理解Docker容器的进程管理

    摘要: Docker在进程管理上有一些特殊之处,如果不注意这些细节中的魔鬼就会带来一些隐患.另外Docker鼓励"一个容器一个进程(one process per container)&qu ...

  9. Android内存进程管理机制

    参考文章: http://www.apkbus.com/android-104940-1-1.htmlhttp://blog.sina.com.cn/s/blog_3e3fcadd0100yjo2.h ...

随机推荐

  1. 里德九步审讯法 z

    在现实生活中,警方审讯靠的不仅仅是自信和创造力(尽管这两点对审讯工作确有帮助)——审讯者还要在交际影响的心理战术方面接受过高水平训练.       让一个人认罪可不是件容易事,而警察有时能让无辜者承认 ...

  2. [Other] 自定义MIME类型支持FLV的相关设置

    刚测试知道为何服务器无法播放flv的原因,特此记录而已. 网络空间支持FLV的相关设置,就是自定义一个MIME类型,一般虚拟主机管理里面都有这个选项 自定义MIME类型 扩展名: .flv MIME类 ...

  3. GridControl 继承写法修改自己的GridControl

    namespace GridControlDemo { class MyGridControl : GridControl { protected override BaseView CreateDe ...

  4. poj 1789 Truck History 最小生成树

    点击打开链接 Truck History Time Limit: 2000MS   Memory Limit: 65536K Total Submissions: 15235   Accepted:  ...

  5. SQL Server 2012 Enterprise Core Edition和SQL Server 2012 Enterprise Edition的区别

    core没有图形界面,只有power shell界面,给没有图形界面的windows用的.

  6. 部门子部门表结构,递归指定部门的所有子部门SQL函数

  7. gcc编译, gdb调试, makefile写法

    //test.c: #include <stdio.h> int main(void) { printf("hello world!"); return 0; } == ...

  8. python学习笔记(win32print API介绍)

    最近博主在研究用python控制打印机 这里整理下win32print的API介绍,官网地址http://timgolden.me.uk/pywin32-docs/win32print.html Op ...

  9. Gradle用户指南(章8:依赖关系管理基础)

    章8:依赖关系管理基础 本章将介绍一些gradle依赖关系管理的基础 什么是依赖关系管理? 简略的说,依赖管理是由两部分组成的.首先,gradle需要知道你要构建或者运行的项目,以便找到它们.我们将这 ...

  10. memcached搭建缓存系统

    Memcached是danga.com(运营LiveJournal的技术团队)开发的一套分布式内存对象缓存系统,用于在动态系统中减少数据库负载,提升性能. 二.适用场合 1.分布式应用.由于memca ...