gpt4 book ai didi

c++ - posix_spawnp 和管道子输出到一个字符串

转载 作者:可可西里 更新时间:2023-11-01 15:04:21 29 4
gpt4 key购买 nike

我正在努力创建进程并将子进程的输出通过管道传输到父进程的字符串中。我让它在 Windows 上运行(使用 CreatePipe 和 CreateProcess 和 ReadFile),但似乎无法在 Unix 上运行精确的模拟。这是我的代码:

#include <spawn.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/wait.h>
#include <iostream>
#include <string>
#include <vector>
using namespace std;

int main()
{
int exit_code;
int cout_pipe[2];
int cerr_pipe[2];
posix_spawn_file_actions_t action;

if(pipe(cout_pipe) || pipe(cerr_pipe))
cout << "pipe returned an error.\n";

posix_spawn_file_actions_init(&action);
posix_spawn_file_actions_addclose(&action, cout_pipe[0]);
posix_spawn_file_actions_addclose(&action, cerr_pipe[0]);
posix_spawn_file_actions_adddup2(&action, cout_pipe[1], 1);
posix_spawn_file_actions_adddup2(&action, cerr_pipe[1], 2);

posix_spawn_file_actions_addclose(&action, cout_pipe[1]);
posix_spawn_file_actions_addclose(&action, cerr_pipe[1]);

vector<string> argmem = {"bla"};
vector<char*> args = {&argmem[0][0], nullptr}; // I don't want to call new.

pid_t pid;
if(posix_spawnp(&pid, "echo", &action, NULL, &args[0], NULL) != 0)
cout << "posix_spawnp failed with error: " << strerror(errno) << "\n";
//close(cout_pipe[0]);
//close(cerr_pipe[0]);

close(cout_pipe[1]);
close(cerr_pipe[1]);

waitpid(pid,&exit_code,0);
cout << "exit code: " << exit_code << "\n";

// Read from pipes
const size_t buffer_size = 1024;
string buffer;
buffer.resize(buffer_size);
ssize_t bytes_read = read(cout_pipe[0], &buffer[0], buffer_size);
while ((bytes_read = read(cout_pipe[0], &buffer[0], buffer_size)) > 0)
{
cout << "read " << bytes_read << " bytes from stdout.\n";
cout << buffer.substr(0, static_cast<size_t>(bytes_read)+1) << "\n";
bytes_read = read(cout_pipe[0], &buffer[0], buffer_size);
}
if(bytes_read == -1)
cout << "Failure reading from stdout pipe.\n";
while ((bytes_read = read(cerr_pipe[0], &buffer[0], buffer_size)) > 0)
{
cout << "read " << bytes_read << " bytes from stderr.\n";
cout << buffer.substr(0, static_cast<size_t>(bytes_read)+1) << "\n";
bytes_read = read(cout_pipe[0], &buffer[0], buffer_size);
}
if(bytes_read == -1)
cout << "Failure reading from stderr pipe.\n";

posix_spawn_file_actions_destroy(&action);
}

输出是:

exit code: 0

所以我想除了实际管道外一切正常。这里有什么问题?我还想知道是否有办法在 waitpid 循环中读取管道字节,但是当我尝试这样做时,父进程无限挂起。

最佳答案

posix_spawn 很有趣也很有用,这使得这个问题值得研究——即使它不再与 OP 相关。

发布的代码中存在一些重大错误。我怀疑其中一些是无奈之下黑客攻击的结果,但我不知道哪个是原始错误:

  1. args 数组不包含代表可执行文件名称的 argv[0]。这导致 echo 程序永远看不到预期的 argv[1](“bla”)。
  2. read() 函数是从不同地方以一种完全没有意义的方式调用的。执行此操作的正确方法是仅调用 read 作为 while 循环的控制表达式的一部分。
  3. waitpid() 在从管道读取之前被调用。这会阻止 I/O 完成(至少在重要的情况下)。
  4. 此代码的一个更微妙的问题是,在从 stderr 中读取任何内容之前,它会尝试读取 child 的所有 stdout。原则上,这可能会导致 child 在尝试写入 stderr 时阻塞,从而阻止程序完成。为此创建一个有效的解决方案更加复杂,因为它要求您可以从任何具有可用数据的管道中读取。我用了poll()为了这。另一种方法是使用多线程。

此外,我还使用了 sh(命令 shell,即 bash)作为子进程。这提供了很大的额外灵 active ,例如运行管道而不是单个可执行文件。不过,特别是使用 sh 提供了一种简单的便利,即不必管理命令行的解析。

/*BINFMTCXX: -std=c++11 -Wall -Werror
*/

#include <spawn.h> // see manpages-posix-dev
#include <poll.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/wait.h>
#include <iostream>
#include <string>
#include <vector>
using namespace std;

int main()
{
int exit_code;
int cout_pipe[2];
int cerr_pipe[2];
posix_spawn_file_actions_t action;

if(pipe(cout_pipe) || pipe(cerr_pipe))
cout << "pipe returned an error.\n";

posix_spawn_file_actions_init(&action);
posix_spawn_file_actions_addclose(&action, cout_pipe[0]);
posix_spawn_file_actions_addclose(&action, cerr_pipe[0]);
posix_spawn_file_actions_adddup2(&action, cout_pipe[1], 1);
posix_spawn_file_actions_adddup2(&action, cerr_pipe[1], 2);

posix_spawn_file_actions_addclose(&action, cout_pipe[1]);
posix_spawn_file_actions_addclose(&action, cerr_pipe[1]);

//string command = "echo bla"; // example #1
string command = "pgmcrater -width 64 -height 9 |pgmtopbm |pnmtoplainpnm";
string argsmem[] = {"sh","-c"}; // allows non-const access to literals
char * args[] = {&argsmem[0][0],&argsmem[1][0],&command[0],nullptr};

pid_t pid;
if(posix_spawnp(&pid, args[0], &action, NULL, &args[0], NULL) != 0)
cout << "posix_spawnp failed with error: " << strerror(errno) << "\n";

close(cout_pipe[1]), close(cerr_pipe[1]); // close child-side of pipes

// Read from pipes
string buffer(1024,' ');
std::vector<pollfd> plist = { {cout_pipe[0],POLLIN}, {cerr_pipe[0],POLLIN} };
for ( int rval; (rval=poll(&plist[0],plist.size(),/*timeout*/-1))>0; ) {
if ( plist[0].revents&POLLIN) {
int bytes_read = read(cout_pipe[0], &buffer[0], buffer.length());
cout << "read " << bytes_read << " bytes from stdout.\n";
cout << buffer.substr(0, static_cast<size_t>(bytes_read)) << "\n";
}
else if ( plist[1].revents&POLLIN ) {
int bytes_read = read(cerr_pipe[0], &buffer[0], buffer.length());
cout << "read " << bytes_read << " bytes from stderr.\n";
cout << buffer.substr(0, static_cast<size_t>(bytes_read)) << "\n";
}
else break; // nothing left to read
}

waitpid(pid,&exit_code,0);
cout << "exit code: " << exit_code << "\n";

posix_spawn_file_actions_destroy(&action);
}

关于c++ - posix_spawnp 和管道子输出到一个字符串,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13893085/

29 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com