Boost.Process提供了一個靈活的C++ 進程管理框架。它容許C++ developer能夠像Java和.Net程序developer那樣管理進程。它還提供了管理當前執行進程上下文、建立子進程、用C++ 流和異步I/O進行通訊的能力。
該庫以徹底透明的方式將全部進程管理的抽象細節呈現給使用者,且該庫是跨平臺的。ios
Boost.Process的長期目標是提供一個抽象於操做系統之上的,能夠管理任何運行的進程的框架。因爲提供這樣的API比較困難,因此如今只專一於管理。Boost.Process的最重要的特徵就是啓動一個外部應用、控制它們並與它們交互。傳統上講,對於C和C++ 來講,就比較困難了,由於它們要啓動新進程、執行外部程序、創建匿名管道來交互、等待進程結束、檢查進程退出碼等。更糟糕的是不一樣操做系統,相關的進程模塊和API是不一樣的。因此,Boost.Process的出現就提供了便利條件。shell
通常來講一個應用啓動了子進程,它們可能會經過傳輸數據來交流。這種進程間通訊是文件句柄層面的,一般涉及stdin、stdout、stderr。若是操做系統支持,那麼就須要可重定向的流。不過這對C++ 來講是很容易的。session
支持同步、異步、分離框架
這樣就能夠實現一個進程的輸出能夠做爲另外一個進程的輸入。異步
最重要的類就是Context和Process。Context提供了進程運行的上下文。pistream和postream是爲了交互。父進程還能夠等待子進程退出,並檢查進程退出碼。若是有例如包含管道的shell命令要執行,那麼pipeline_entry就應運而生了,它能夠實現前一個子進程的輸出是下一個子進程的輸入。async
一、建立上下文Context
二、建立子進程,得到子進程對象
三、若是有重定向,能夠訪問到stdin、stdout、stderr
四、進程結束,檢查進程退出碼ide
一個最簡單的例子函數
#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;
}
|
下面再看一個異步的例子post
#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》ui
對於流的描述,可分爲六種類型
序號 | 流描述 | 含義 |
---|---|---|
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;
}
}
|
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>,用以保存環境變量。
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類實際上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庫能夠在這裏得到點這裏。