我正在尝试围绕交互式程序创建一个包装器。为此,我使用了pipe
,dup2
和poll
的组合。一切似乎都顺利,直到孩子终止。在此步骤中,父进程似乎丢失了stdin
,这是我似乎无法理解的原因。
以下是代码:
#include <unistd.h>
#include <sys/types.h>
#include <stdlib.h>
#include <stdio.h>
#include <poll.h>
#include <fcntl.h>
#include <signal.h>
#include <vector>
#include <string>
#include <iostream>
struct SystemFunctionFailure
{
std::string what;
SystemFunctionFailure(std::string const& what) : what(what) {}
};
template<typename T,size_t N> constexpr size_t countof(const T(&)[N]) { return N; }
void readAndPrint(std::string const& what, int fd)
{
std::cerr << "Reading "+what+"\n";
std::vector<char> buffer(1024);
const auto bytesRead=read(fd,buffer.data(),buffer.size());
if(bytesRead==-1)
{
if(errno!=EAGAIN)
throw SystemFunctionFailure("read "+what);
}
else if(bytesRead==0)
{
std::cerr << "EOF reached on "+what+"\n";
exit(0);
}
else
std::cerr << "CONTENTS OF "+what+": "+std::string(buffer.data(),buffer.size())+"\n";
}
int main()
{
try
{
int pipeChildOut[2];
if(pipe(pipeChildOut)==-1) throw SystemFunctionFailure("pipe for child stdout");
int pipeChildErr[2];
if(pipe(pipeChildErr)==-1) throw SystemFunctionFailure("pipe for child stderr");
int pipeChildIn[2];
if(pipe(pipeChildIn)==-1) throw SystemFunctionFailure("pipe for child stdin");
const auto child=fork();
if(child==-1) throw SystemFunctionFailure("fork");
if(child)
{
dup2(pipeChildOut[1],STDOUT_FILENO);
close(pipeChildOut[0]);
dup2(pipeChildErr[1],STDERR_FILENO);
close(pipeChildErr[0]);
dup2(pipeChildIn[0],STDIN_FILENO);
close(pipeChildIn[1]);
execlp("sh","sh","-c","sleep 1; echo Test ; sleep 1; echo Child is exiting... >&2",nullptr);
throw SystemFunctionFailure("execlp returned");
}
else
{
const int childStdErr=pipeChildErr[0];
const int childStdOut=pipeChildOut[0];
dup2(pipeChildIn[1],STDOUT_FILENO);
fcntl(childStdErr,F_SETFL,O_NONBLOCK);
fcntl(childStdOut,F_SETFL,O_NONBLOCK);
fcntl(STDIN_FILENO,F_SETFL,O_NONBLOCK);
while(true)
{
std::cerr << "New iteration of IO loop\n";
pollfd pollfds[]={ // making the indices coincide with .._FILENO
{STDIN_FILENO,POLLIN},
{childStdOut,POLLIN},
{childStdErr,POLLIN},
};
if(poll(pollfds,countof(pollfds),{-1})==-1)
throw SystemFunctionFailure("poll");
std::cerr << "poll returned\n";
for(unsigned i=0;i<countof(pollfds);++i)
std::cerr <<" pollfds["<<i<<"].revents: " << pollfds[i].revents << "\n";
if(pollfds[ STDIN_FILENO].revents&POLLIN) readAndPrint("stdin" ,pollfds[ STDIN_FILENO].fd);
if(pollfds[STDOUT_FILENO].revents&POLLIN) readAndPrint("stdout",pollfds[STDOUT_FILENO].fd);
if(pollfds[STDERR_FILENO].revents&POLLIN) readAndPrint("stderr",pollfds[STDERR_FILENO].fd);
}
}
}
catch(SystemFunctionFailure& ex)
{
perror(ex.what.c_str());
exit(EXIT_FAILURE);
}
}
此处,子项通过stdin
隐式关闭其原始dup2
,因此它似乎不会影响父级对控制台输入的任何访问。但出于某种原因,我得到的是输出:
$ g++ test.cpp -o test -std=c++14 && ./test
New iteration of IO loop
poll returned
pollfds[0].revents: 0
pollfds[1].revents: 1
pollfds[2].revents: 0
Reading stdout
CONTENTS OF stdout: Test
New iteration of IO loop
poll returned
pollfds[0].revents: 0
pollfds[1].revents: 0
pollfds[2].revents: 1
Reading stderr
CONTENTS OF stderr: Child is exiting...
New iteration of IO loop
$
即。我得到shell提示符,因此父级不再位于前台。在此之后,如果我等待几秒钟并输入一个字母,我会得到这个输出:
poll returned
pollfds[0].revents: 1
pollfds[1].revents: 0
pollfds[2].revents: 0
Reading stdin
read stdin: Input/output error
我想至少让父进程在子进程死后保留对其控制台输入的访问权限。阅读an answer to another question之后,我认为我的问题是相关的,但答案并没有回答我的问题:“如何做到这一点?”。
答案 0 :(得分:1)
我认为if(child)
应为if(child == 0)
。
来自https://linux.die.net/man/2/fork
成功时,子进程的PID在父进程中返回, 并在孩子中返回 0。失败时,返回-1 parent,没有创建子进程,并且正确设置了errno。