问题描述
与另一篇博客相同:
比如ros2 topic pub就是一个可能不会自动结束的shell指令,在C++进行并发设计时,往往可能导致这些进程变成野进程
因此我们需要主动管理 / 退出他们
我实现了一个goes_popen()
接口,用于更加简洁地管理这类问题。
接口效果
- 提供非阻塞的终端指令反馈方法。(不同于python的subprocess库的checkout()接口)
- 提供主动退出/关闭指令的接口。 (真正意义的terminate())
- 同时支持终端反馈存入变量 和 终端打印。
使用demo
#include "new_popen.hpp"
std::string command = R"(
ros2 topic pub /initialpose geometry_msgs/PoseWithCovarianceStamped "\
header:
stamp:
sec: 0,
nanosec: 0
frame_id: 'map'
pose:
pose:
position:
x: -2.0,
y: -0.5,
z: 0.0
orientation:
x: 0.0,
y: 0.0,
z: 0.01,
w: 1.0
covariance: [0.25, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.25, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.06853891909122467]
" -1
)";
std::string command_1 = "ros2 topic echo /scan --full-length --once";
std::string command_2 = "ros2 topic list";
inline void a_gap(){
// it's just a gap
for (int i = 0; i < 3; ++i) {
std::cout << "Main thread is doing task " << i << std::endl;
// 在这里添加其他任务代码
std::this_thread::sleep_for(std::chrono::seconds(1)); // 模拟执行任务的耗时
}
}
int main() {
// Introduction to API:
// 1. create a commander:
// 执行命令
CommandProcess::SharedPtr proc_1 = goes_popen(
command,false,false
);
CommandProcess::SharedPtr proc_2 = goes_popen(
command_1,false,true
); // drop the stderr
CommandProcess::SharedPtr proc_3 = goes_popen(
command_2,true,false
); // drop the stdout
a_gap();
// 2. terminate commander
proc_1->terminate();
// 3. block the main-proc
proc_1->join();
// 4. positive check if_finished()
if(proc_1->if_finished()) std::cout << "proc_1 has terminate" << std::endl;
if(proc_2->if_finished()) std::cout << "proc_2 has terminate" << std::endl;
else std::cout << "proc_2 is still working" << std::endl;
// 5. get the output from the command
std::string result = proc_3->get_output(); // it will block the main-proc, until command finished
std::cout << "read from proc_3: \n" << result << std::endl;
// 6. how to get the result with no block?
// OR how to terminate during the result reading?
// use structure like following:
// Firstly, you should provide 3 states:
bool exit_flag_ = false;
std::string result_2 = "";
std::thread t;
// Secondly, get_output_noblock
get_output_noblock(
&t, // you should provide a thread
exit_flag_, // the shutdown flag you could control
proc_2, // CommandProcess you need to get_ouput_noblock
result_2 // the result would be written into it.
);
// Thirdly, early shutdown module
// you should read result after ->if_finished() is true
while(!proc_2->if_finished()){
a_gap();
// change exit_flag_ = true when you need to pre-shutdown proc_1
{
// exit_flag_=true; // it will cancel your command
}
if(exit_flag_==true) break;
}
// [important!]: before read result
proc_2->join();
t.join();
std::cout << "read from proc_2: " << result_2 << std::endl;
// [notice]: you can only get the output after finished
// if you early shutdown the command, you would read nothing.
proc_1.reset();
proc_2.reset();
proc_3.reset();
return 0;
}
实现原理概述
- 一个指令两次fork进程。一个子进程用于管道通信,获取终端指令的反馈;另一个子进程用于执行exec()进行进程变身,执行shell指令。
- 管道通信。 将终端反馈重定向到两个子进程的管道中,从而实现终端反馈的获取。
- 共享内存。将终端反馈存入共享内存,从而实现更方便的读取操作。
仓库链接
https://github.com/GoesM/goes_popen