boost::process
позволяет перенаправлять вывод программы в канал переносимым способом, поэтому нет необходимости использовать условия препроцессора и вызывать API, специфичный для ОС, для запуска процесса, создания и чтения каналов.
boost::process
поддерживает синхронный и асинхронный ввод-вывод. Синхронный создает высокий риск тупика; Исходя из моего опыта, если приложение сложное и запускает много дочерних процессов с разным выводом (что-то похожее на реализацию старого доброго протокола CGI
, например, где мы запускаем неизвестные и, возможно, сторонние, глючные дочерние процессы), это гарантирует, что boost::process
синхронный ввод-вывод рано или поздно приведет к тупику.
Итак, мы должны использовать асинхронный ввод-вывод на основе boost::asio
. Это помогает нам избежать тупиков, однако задает больше вопросов, на которые нет ответов.
Обычно мы хотим прочитать все данные, сгенерированные дочерним процессом. Мы также обычно не знаем, сколько данных сгенерирует дочерний процесс, сгенерирует ли он вообще какие-либо данные, произойдет ли сбой, каков ожидаемый формат данных и т. Д. Мы вообще ничего не знаем, и мы все еще хотим прочитать все данные. Даже если код ошибки процесса не равен нулю, он все равно может вывести любое полезное сообщение об ошибке, которое мы также хотим прочитать.
Единственный способ увидеть, как это сделать, - читать до появления ошибки. Когда мы используем boost::asio
с сокетами TCP, он закрывает ошибку конца файла, когда сокет закрыт, поэтому мы можем знать, что это конец данных, и этот сокет закрыт, поэтому больше никаких данных не появится.
С async_pipe
, однако, не задокументировано, что код ошибки должен означать end of pipe
или pipe is closed by other side
.
На практике мы можем использовать следующий код для «чтения до ошибки»:
std::vector<char> exec(const std::string & command)
{
using boost::asio::async_read;
using boost::asio::buffer;
using boost::asio::io_context;
using boost::system::error_code;
using boost::system::system_error;
using namespace std;
namespace bp = boost::process;
io_context async_io_context;
bp::async_pipe command_stdout(async_io_context);
bp::child command_process(command, bp::std_in.close(), bp::std_out > command_stdout, bp::std_err > command_stdout, bp::shell);
static constexpr const size_t kStdOutBufferSize = 65535;
array<char, kStdOutBufferSize> command_stdout_buffer;
vector<char> command_output;
function<void(const boost::system::error_code &ec, std::size_t size)> async_read_handler_function;
error_code io_error_code;
async_read_handler_function = [&](const error_code &ec, std::size_t size)
{
// Write data
if(size)
command_output.insert(command_output.end(), command_stdout_buffer.data(), command_stdout_buffer.data() + size);
// Parse error code
if (ec)
{
io_error_code = ec;
async_io_context.stop();
}
else
async_read(command_stdout, buffer(command_stdout_buffer.data(), command_stdout_buffer.size()), async_read_handler_function);
};
async_read(command_stdout, buffer(command_stdout_buffer.data(), command_stdout_buffer.size()), async_read_handler_function);
async_io_context.run();
if (io_error_code)
{
// TODO: so how to make us sure that we really read all data and other side of pipe is gracefully closed by child process?
}
return command_output;
}
В Windows, когда все работает правильно, этот код действительно читает все выходные данные и возвращает код ошибки с целочисленными значениями system_category
и 109
; в boost
enum коды ошибок, 109
означает boost::system::errc::destination_address_required
. Мои вопросы:
- Гарантируется ли, что если удаленный конец канала закрыт дочерним процессом и мы прочитали все ожидающие данные, код ошибки будет
boost::system::errc::destination_address_required
?
- Является ли этот код ошибки переносимым, поэтому любая ОС (Windows, Linux, Android, iOS, MacOS) выдаст нам тот же код ошибки?
Если код не переносимый и ошибки в других ОС будут другими, как сделать код переносимым? boost
имеет смысл, если создает только переносимый код, в противном случае специфичные для системы API лучше, потому что они дадут лучшую производительность и более глубокое использование специфических функций системы.