zoukankan      html  css  js  c++  java
  • boost:进程管理

    概述

    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库可以在这里获得点这里

     
  • 相关阅读:
    10分钟教你用VS2017将代码上传到GitHub
    【算法】C++用链表实现一个箱子排序附源代码详解
    【智能算法】粒子群算法(Particle Swarm Optimization)超详细解析+入门代码实例讲解
    【C/C++】10分钟教你用C++写一个贪吃蛇附带AI功能(附源代码详解和下载)
    【python】10分钟教你用python如何正确把妹
    【python】10分钟教你用python一行代码搞点大新闻
    【python】10分钟教你用python下载和拼接微信好友头像图片
    3. powerdesigner 生成mysql脚本,要求字段、表名有注释
    5. 回填表格复选框
    14. js字符串截取substring用法
  • 原文地址:https://www.cnblogs.com/lidabo/p/10248716.html
Copyright © 2011-2022 走看看