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庫可以在這里獲得點這里


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM