如何防止父进程在孩子死后丢失控制台输入?
How to prevent parent process from losing console input after the child dies?
我正在尝试围绕交互式程序制作包装器。为此,我使用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 loopn";
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 returnedn";
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);
}
}
在这里,子项通过 dup2
隐式关闭其原始stdin
,因此似乎不会影响父级对控制台输入的任何访问。但出于某种原因,这是我得到的输出:
$ 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
我想至少让父进程在孩子死后保留对其控制台输入的访问权限。在阅读了另一个问题的答案后,我认为我的问题与我的问题有关,但该答案并没有回答我的问题:"如何正确做?
我认为if(child)
应该if(child == 0)
.
从 https://linux.die.net/man/2/fork
成功后,子进程的 PID 将在父进程中返回, 在子项中返回 0。失败时,在 父进程,则不创建子进程,并正确设置 errno。
相关文章:
- C++:将控制台输出存储在宏中更好吗
- 在while循环中输入带有std::cin的字符串后,控制台会输出大量胡言乱语
- SSH通过/sbin/SSH无法读取RSA密钥文件(从控制台运行)
- C++控制台应用程序阻止退出
- 如何修复此iFile以将.txt输出到控制台
- 为什么控制台要求输入,即使代码中没有输入
- 在Qt Creator中,如何在连接到正在运行的进程后查看控制台输出
- 控制台输出在 Qt5 中未正确显示
- C++出现控制台错误.我无法识别源代码的问题
- C++程序不会从 Windows 控制台运行
- 有人可以向我解释为什么控制台输出 0 吗?
- Visual Studio 2017 停止工作,并在打开后显示许多控制台窗口
- 如何从 Win32 C++ 应用程序输出到父控制台窗口?
- Win32 API 控制台光标在 WriteConsole 后不移动
- 如何获取控制台窗口的宽度?
- 如何在 Win32 C++控制台应用程序中调用 UWP 类库
- 如何从Windows控制台调用.exe(C++)以在不同的目录(或任何目录)中创建文件夹?
- 来自Visual Studio中控制台项目的exe文件?
- 为什么即使使用 for 循环遍历我的向量,它也没有输出到控制台?(C++)
- 使用 std::string_view 的子字符串控制台输出