使用管道从 STDIN 读取分叉进程时出现问题
problem getting forked process to read from STDIN using pipes
我正在尝试创建一个辅助类来执行系统命令并通过管道支持获得响应。对于我只需要获得响应(命令没有 STDIN 可以使用(的情况,它按预期工作,对于管道支持,我得到乱码 STDIN,我无法找出根本原因。
处理此机制的主要函数是(请忽略小错误检查问题(
最小工作示例
#include <vector>
#include <string>
#include <iostream>
#include <sstream>
#include <unistd.h>
#include <sys/prctl.h>
#include <sys/types.h>
#include <signal.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <stdarg.h>
struct exec_cmd_t {
exec_cmd_t(std::vector<std::string> args) : args(args), has_executed(false), cpid(-1) { }
exec_cmd_t(const exec_cmd_t &) = delete;
exec_cmd_t(exec_cmd_t &&) = delete;
exec_cmd_t & operator=(const exec_cmd_t &) = delete;
exec_cmd_t & operator=(exec_cmd_t &&) = delete;
std::string operator()();
std::string pipe_cmd(const std::string & input);
std::string pipe_cmd();
~exec_cmd_t();
private:
std::vector<std::string> args;
bool has_executed;
int cpid;
std::stringstream in_stream;
std::stringstream out_stream;
friend std::string operator | (exec_cmd_t & first, exec_cmd_t & second);
friend std::string operator | (exec_cmd_t && first, exec_cmd_t && second);
friend std::string operator | (std::string, exec_cmd_t & second);
friend std::string operator | (std::string, exec_cmd_t && second);
};
std::string exec_cmd_t::pipe_cmd(const std::string & input) {
this->has_executed = true;
const int read_end = 0;
const int write_end = 1;
int read_pipe[2];
int write_pipe[2];
if (pipe(read_pipe) < 0 || pipe(write_pipe) < 0) {
this->has_executed = false;
return std::string{};
}
this->in_stream << input;
std::string line;
while(getline(this->in_stream, line)) {
if (line.size() == 0) {
continue;
}
int wr_sz = write(write_pipe[write_end], line.c_str(), line.size());
if (wr_sz <= 0) {
break;
}
write(write_pipe[write_end], "n", 1);
}
close(write_pipe[write_end]);
this->cpid = fork();
if (this->cpid == 0) {
dup2(write_pipe[read_end], STDIN_FILENO);
dup2(read_pipe[write_end], STDOUT_FILENO);
close(read_pipe[read_end]);
close(write_pipe[write_end]);
close(read_pipe[write_end]);
close(write_pipe[read_end]);
prctl(PR_SET_PDEATHSIG, SIGTERM);
char * params[args.size()];
const char * image_path = args[0].c_str();
for(int i = 1; i < args.size(); i++) {
params[i-1] = const_cast<char *>(args[i].c_str());
}
params[args.size()] = nullptr;
execv(image_path, params);
exit(1);
}
close(read_pipe[write_end]);
close(write_pipe[read_end]);
char buff[256];
int rd_sz = -1;
int flags = fcntl(read_pipe[0], F_GETFL, 0);
fcntl(read_pipe[read_end], F_SETFL, flags | O_NONBLOCK);
int status = 0;
waitpid(this->cpid, &status, 0);
this->has_executed = false;
int error_code = 0;
while((rd_sz = read(read_pipe[read_end], buff, sizeof(buff))) > 0) {
buff[rd_sz] = ' ';
this->out_stream << std::string{buff};
}
close(read_pipe[read_end]);
return this->out_stream.str();
}
std::string exec_cmd_t::pipe_cmd() {
static std::string empty_str{};
return pipe_cmd(empty_str);
}
std::string exec_cmd_t::operator()() {
return pipe_cmd();
}
exec_cmd_t::~exec_cmd_t() {
if (this->has_executed) {
int status;
waitpid(this->cpid, &status, WNOHANG);
if (!WIFEXITED(status)) {
kill(this->cpid, SIGKILL);
waitpid(this->cpid, &status, 0);
}
}
}
std::string operator | (exec_cmd_t & first, exec_cmd_t & second) {
return second.pipe_cmd(first());
}
std::string operator | (exec_cmd_t && first, exec_cmd_t && second) {
return second.pipe_cmd(first());
}
std::string operator | (std::string output, exec_cmd_t & second) {
return second.pipe_cmd(output);
}
std::string operator | (std::string output, exec_cmd_t && second) {
return second.pipe_cmd(output);
}
int main() {
auto str = exec_cmd_t{ {"/bin/echo", "echo", "hellonworldnornnot"} } | exec_cmd_t{ {"/bin/grep", "grep", "world", "-"} };
std::cout << str << std::endl;
return 0;
}
给我
grep: =V: No such file or directory
(standard input):world
似乎 grep 正在执行两次,一次失败,没有这样的文件或目录,另一次成功。任何建议都会非常有帮助:-(。 提前谢谢。
你有一个原因导致未定义的行为,这可能会导致你的程序做它所做的事情。声明并使用超出范围的 VLA,如下所示:
char* params[args.size()];
...
params[args.size()] = nullptr;
execv(image_path, params);
这使params
中的终止char*
未初始化,因此它可以指向任何位置。grep
认为它指向文件名,尝试打开它并失败。
由于 VLA:s 不在 C++ 标准中,请考虑将其更改为:
std::vector<char*> params(args.size());
...
params[args.size() - 1] = nullptr;
execv(image_path, params.data());
另一个令人担忧的原因是,您在应该使用ssize_t
的地方使用了int
,即使您阅读或写入的内容极不可能超过int
可以处理的内容。
在我进行这些更改后,它开始工作并打印了预期的world
。我什至添加了第三个命令来检查它可以处理它。建议的更改:
14,15c14,15
< exec_cmd_t(std::vector<std::string> args) :
< args(args), has_executed(false), cpid(-1) {}
---
> exec_cmd_t(std::vector<std::string> Args) :
> args(Args), has_executed(false), cpid(-1), in_stream{}, out_stream{} {}
59c59
< int wr_sz = write(write_pipe[write_end], line.c_str(), line.size());
---
> ssize_t wr_sz = write(write_pipe[write_end], line.c_str(), line.size());
76c76
< char* params[args.size()];
---
> std::vector<char*> params(args.size());
78c78
< for(int i = 1; i < args.size(); i++) {
---
> for(decltype(args.size()) i = 1; i < args.size(); i++) {
81,82c81,82
< params[args.size()] = nullptr;
< execv(image_path, params);
---
> params[args.size() - 1] = nullptr;
> execv(image_path, params.data());
90c90
< int rd_sz = -1;
---
> ssize_t rd_sz = -1;
96c96
< int error_code = 0;
---
> // int error_code = 0; // unused
106,107c106
< static std::string empty_str{};
< return pipe_cmd(empty_str);
---
> return pipe_cmd({});
143c142,143
< exec_cmd_t{{"/bin/grep", "grep", "world", "-"}};
---
> exec_cmd_t{{"/bin/grep", "grep", "-A1", "hello"}} |
> exec_cmd_t{{"/bin/grep", "grep", "world"}};
我还意识到您的程序就像管道命令之间的代理,从一个命令读取所有内容并将其写入下一个命令。
您可以同时启动所有程序,并一次性设置已启动程序之间的管道。对于三个命令,需要三个管道:
cmd1 cmd2 cmd3
| w--r w--r |
stdin read output into program
or fed by your program
如果您决定运行具有大量输出的命令,这将使性能和内存消耗成为更少的问题。在内部,您只需要通过读取最后一个命令的输出来存储要存储的内容。我对这种方法做了一个小测试,它就像一个魅力。
相关文章:
- 警告处理为错误这里有什么问题
- 最小硬币更换问题(自上而下方法)
- 在进程中对同一管道进行读取和写入时C++管道出现问题
- 父进程意外被子 cmd 杀死.exe运行 java jar 的进程 - WINDOWS 7 问题 - C++
- 使用管道从 STDIN 读取分叉进程时出现问题
- 在 Linux 中从单独的单线程进程生成多线程进程时出现问题
- 在 c++ 中等待一个子进程与另一个子进程时遇到问题
- 使用boost进程间库的phpexec共享内存和Cloudfoundry容器问题
- 从导致问题的java进程附加到外部c++进程
- 使用SetParent将窗口嵌入外部进程的各种问题
- 读取进程内存问题未更新
- C++创建进程 - 系统错误#2找不到文件 - 我的文件路径有什么问题?
- 从另一个进程访问STA对象时出现问题
- COM进程中DLL中的DllMain出现问题
- 打开带有DEBUG标志的进程的问题
- 创建读取面向互联网的套接字的守护进程有什么安全问题?
- Q执行带参数的exe时出现进程问题
- 枚举进程句柄,奇怪的问题
- Q进程::执行(“清除”)问题
- 在Qt中使用windows进程时的问题