C++ 将posix pipe()和dup()与C++;重定向I/O问题
为了处理I/O重定向,我不得不修改我为之前的家庭作业编写的一个简单shell,而我在让管道正常工作方面遇到了麻烦。似乎在复制了单独进程中的文件描述符之后,当我写入和读取stdout和stdin时,管道工作,但是如果我使用printf、fprintf、get、fgets等来尝试查看输出是否显示在管道中,即使stdin和stdout的文件描述符显然是管道的副本,它也会转到控制台(我不知道这是否是正确的说法,但我认为重点很清楚) 我有99.9%的把握,至少在普通C语言中,我在做所有应该做的事情——比如在dup()之后适当地关闭所有文件描述符——并且文件I/O工作正常,所以这似乎是一个我不知道也找不到任何信息的细节问题。我花了一天的大部分时间在尝试不同的事情,在过去的几个小时里,我在谷歌上搜索,试图找出是否可以将cin和cout重定向到管道上,看看这是否能解决问题,但在这一点上,这似乎比它的价值更麻烦 既然cin和cout应该与stdio同步,那么仅仅通过重定向stdin和stdout就可以了吗?我认为应该这样做,特别是因为这些命令可能是用C编写的,所以它们会使用stdio。但是,如果我尝试“cat[file1][file2]| sort”这样的命令,它会将cat[file1][file2]的结果打印到命令行,而sort不会获得任何输入,因此没有输出。很明显,cout和cin也不受dup()的影响,所以我把二和二放在一起,得出了这个结论 这是我代码的一个略为缩短的版本,减去了所有的错误检查和诸如此类的事情,我相信我处理得很好。我可以发布完整的代码,如果它来了,但它是很多,所以我将从这个开始 我重写了函数,以便父进程为每个命令派生一个子进程,并根据需要将它们与管道连接,然后等待子进程死亡。同样,文件描述符0和1上的写入和读取工作(即写入管道和从管道读取),文件指针stdin和stdout上的stdio不工作(不写入管道) 非常感谢,这让我很难受 更新:我没有为每个不同的命令更改字符串cmd,所以它似乎不起作用,因为管道只是转到同一个命令,所以最终输出是相同的。。。对不起,我的沉默,但谢谢你,因为我发现了斯特拉斯的问题C++ 将posix pipe()和dup()与C++;重定向I/O问题,c++,shell,posix,pipe,C++,Shell,Posix,Pipe,为了处理I/O重定向,我不得不修改我为之前的家庭作业编写的一个简单shell,而我在让管道正常工作方面遇到了麻烦。似乎在复制了单独进程中的文件描述符之后,当我写入和读取stdout和stdin时,管道工作,但是如果我使用printf、fprintf、get、fgets等来尝试查看输出是否显示在管道中,即使stdin和stdout的文件描述符显然是管道的副本,它也会转到控制台(我不知道这是否是正确的说法,但我认为重点很清楚) 我有99.9%的把握,至少在普通C语言中,我在做所有应该做的事情——比如
int call_execv( string cmd, vector<string> &argv, int argc,
vector<int> &redirect)
{
int result = 0, pid, /* some other declarations */;
bool file_in, file_out, pipe_in, pipe_out;
queue<int*> pipes; // never has more than 2 pipes
// parse, fork, exec, & loop if there's a pipe until no more pipes
do
{
/* some declarations for variables used in parsing */
file_in = file_out = pipe_in = pipe_out = false;
// parse the next command and set some flags
while( /* there's more redirection */ )
{
string symbol = /* next redirection symbol */
if( symbol == ">" )
{
/* set flags, get filename, etc */
}
else if( symbol == "<" )
{
/* set flags, get filename, etc */
}
else if( pipe_out = (symbol == "|") )
{
/* set flags, and... */
int tempPipes[2];
pipes.push( pipe(tempPipes) );
break;
}
}
/* ... set some more flags ... */
// fork child
pid = fork();
if( pid == 0 ) // child
{
/* if pipe_in and pipe_out set, there are two pipes in queue.
the old pipes read is dup'd to stdin, and the new pipes
write is dup'd to stdout, other two FD's are closed */
/* if only pipe_in or pipe_out, there is one pipe in queue.
the unused end is closed in whichever if statement evaluates */
/* if neither pipe_in or pipe_out is set, no pipe in queue */
// redirect stdout
if( pipe_out ){
// close newest pipes read end
close( pipes.back()[P_READ] );
// dup the newest pipes write end
dup2( pipes.back()[P_WRITE], STDOUT_FILENO );
// close newest pipes write end
close( pipes.back()[P_WRITE] );
}
else if( file_out )
freopen(outfile.c_str(), "w", stdout);
// redirect stdin
if( pipe_in ){
close( pipes.front()[P_WRITE] );
dup2( pipes.front()[P_READ], STDIN_FILENO );
close( pipes.front()[P_READ] );
}
else if ( file_in )
freopen(infile.c_str(), "r", stdin);
// create argument list and exec
char **arglist = make_arglist( argv, start, end );
execv( cmd.c_str(), arglist );
cout << "Execution failed." << endl;
exit(-1); // this only executes is execv fails
} // end child
/* close the newest pipes write end because child is writing to it.
the older pipes write end is closed already */
if( pipe_out )
close( pipes.back()[P_WRITE] );
// remove pipes that have been read from front of queue
if( init_count > 0 )
{
close( pipes.front()[P_READ] ); // close FD first
pipes.pop(); // pop from queue
}
} while ( pipe_out );
// wait for each child process to die
return result;
}
int-call\u-execv(字符串cmd、向量和argv、int-argc、,
向量和重定向)
{
int result=0,pid,/*一些其他声明*/;
输入文件,输出文件,输入文件,输出文件;
队列管道;//从来没有超过2个管道
//如果有管道,则执行parse、fork、exec和loop,直到没有更多管道为止
做
{
/*解析中使用的变量的一些声明*/
文件输入=文件输出=管道输入=管道输出=错误;
//解析下一个命令并设置一些标志
而(/*还有更多重定向*/)
{
字符串符号=/*下一个重定向符号*/
如果(符号==“>”)
{
/*设置标志、获取文件名等*/
}
否则,如果(symbol==”无论出现什么问题,您都不会检查任何返回值。您如何知道pipe()或dup2()是
命令成功?您是否验证了stdout
和stdin
是否确实指向execv
前面的管道?execv
是否保留您提供的文件描述符?不确定,以下是execve
文档中的相应段落:
- 默认情况下,文件描述符在execve()中保持打开状态。在exec上标记为关闭的文件描述符将关闭;请参阅fcntl(2)中对FD_CLOEXEC的描述。(如果
文件描述符已关闭,这将导致释放通过此进程在基础文件上获得的所有记录锁。有关详细信息,请参阅fcntl(2)
如果文件描述符0、1和2在成功执行execve()后关闭,则进程将获得特权,因为每个
在执行的文件上设置任务位,然后系统可能会为每个文件描述符打开一个未指定的文件。作为一般原则,没有可移植程序,
无论是否具有特权,都可以假定这三个文件描述符将在execve()中保持关闭状态
您应该添加更多的调试输出,看看真正发生了什么。您是否在程序中使用了strace-f
(跟踪子项)?以下内容:
queue<int*> pipes; // never has more than 2 pipes
// ...
int tempPipes[2];
pipes.push( pipe(tempPipes) );
我在最初的帖子中说,“这是我的代码的一个略为缩短的版本,减去所有的错误检查和类似的事情,我相信我处理得很好。”我检查管道上的返回值,fork(即使是-1),所有的DUP和FRE都打开了。我正在尝试strace并注意到一些潜在的问题,因此感谢您的建议,我以前不知道。此外,我相当确定execv保留了文件描述符,因为文件I/O工作正常,我尝试设置FD_CLOEXEC标志,以查看它做了什么,并且在关闭时它会抱怨,这是正确的不正常。哦,还有一些事情。我尝试使用一个文件(使用tmpnam()生成一个唯一的文件名)让子进程以管道的形式写入,然后关闭,然后从管道中读取。我相当确定它的逻辑(就像为单独的“管道”使用单独的文件一样)很好,但我想知道刷新和其他东西是否会有问题,这可能解释了为什么不起作用?另外,要明确的是,文件I/O与freopen一起工作,管道到文件与freopen不起作用,这与管道和dup.Ano不起作用
struct PipeFds
{
int fds[2];
};
std::queue<PipeFds> pipes;
PipeFds p;
pipe(p.fds); // check the return value
pipes.push(p);