C++ 了解dup2和关闭文件描述符
我发布代码只是为了我的问题的上下文。我并没有明确地寻找你来帮助修复它,我更希望了解dup2系统调用,我只是没有从手册页和其他许多stackoverflow问题中学习C++ 了解dup2和关闭文件描述符,c++,linux,dup2,C++,Linux,Dup2,我发布代码只是为了我的问题的上下文。我并没有明确地寻找你来帮助修复它,我更希望了解dup2系统调用,我只是没有从手册页和其他许多stackoverflow问题中学习 pid = fork(); if(pid == 0) { if(strcmp("STDOUT", outfile)) { if (command->getOutputFD() == REDIRECT) { if ((outfd = ope
pid = fork();
if(pid == 0) {
if(strcmp("STDOUT", outfile)) {
if (command->getOutputFD() == REDIRECT) {
if ((outfd = open(outfile, O_CREAT | O_WRONLY | O_TRUNC)) == -1)
return false;
command->setOutputFD(outfd);
if (dup2(command->getOutputFD(), STDOUT_FILENO) == -1)
return false;
pipeIndex++;
}
else if (command->getOutputFD() == REDIRECTAPPEND) {
if ((outfd = open(outfile, O_CREAT | O_WRONLY | O_APPEND)) == -1)
return false;
command->setOutputFD(outfd);
if (dup2(command->getOutputFD(), STDOUT_FILENO) == -1)
return false;
pipeIndex++;
}
else {
if (dup2(pipefd[++pipeIndex], STDOUT_FILENO) == -1)
return false;
command->setOutputFD(pipefd[pipeIndex]);
}
}
if(strcmp("STDIN", infile)) {
if(dup2(pipefd[pipeIndex - 1], STDIN_FILENO) == -1)
return false;
command->setOutputFD(pipefd[pipeIndex - 1]);
pipeIndex++;
}
if (execvp(arguments[0], arguments) == -1) {
std::cerr << "Error!" << std::endl;
_Exit(0);
}
}
else if(pid == -1) {
return false;
}
ls-l>output.txt的输出意味着我关闭了错误的描述符,但是关闭了其他相关的描述符,在没有呈现错误的情况下,不会向文件提供任何输出。如ls-l
所示,grep“cook”
应向控制台生成输出
使用用于处理管道的文件描述符数组
有所有这些描述符的副本。描述词是什么时候掌握的
家长关门了?更重要的是,哪些描述符?这是全部吗
他们?执行命令时未使用的所有命令
文件描述符可以通过以下三种方式之一关闭:
您可以显式地对其调用close()
进程终止,操作系统会自动关闭所有仍然打开的文件描述符
当进程调用七个exec()
函数之一并且文件描述符具有O_CLOEXEC
标志时
如您所见,大多数情况下,文件描述符将保持打开状态,直到您手动关闭它们。代码中也会发生这种情况-因为您没有指定O\u CLOEXEC
,所以当子进程调用execvp()
时,文件描述符不会关闭。在子对象中,它们在子对象终止后关闭。父母也是如此。如果您希望在终止之前的任何时间发生这种情况,则必须手动调用close()
在子系统中处理管道时,会留下哪些描述符
由哪些进程打开?假设我执行命令:ls-l | grep
“[username]”,哪些描述符应为ls保留打开状态
过程就在管道的末端?如果是,什么时候?相同的
问题适用于grep命令
这里有一个(粗略的)想法,当您键入ls-l | grep“username”
时,shell会做什么:
shell调用pipe()
来创建新管道。管道文件描述符将在下一步由子级继承
shell分叉两次,让我们调用这些进程c1
和c2
。我们假设c1
将运行ls
,c2
将运行grep
在c1
中,管道的读取通道用close()
关闭,然后它用管道写入通道和STDOUT\u FILENO
调用dup2()
,从而使写入STDOUT
等同于写入管道。然后,调用七个exec()
函数中的一个开始执行ls
ls
写入stdout
,但由于我们将stdout
复制到管道的写入通道,ls
将写入管道
在c2
中,情况正好相反:管道的写入通道关闭,然后调用dup2()
使stdin
指向管道的读取通道。然后,调用七个exec()
函数中的一个开始执行grep
grep
从stdin
读取,但由于我们dup2()
将标准输入到管道的读取通道,grep
将从管道读取
当我处理IO重定向到文件时,必须打开一个新文件
并复制到标准输出(我不支持输入重定向)。什么时候
这个描述符关闭了吗?我在例子中看到它被关闭了
在调用dup2之后,但是接下来会发生什么呢
如果文件已关闭,是否写入文件
因此,当您调用dup2(a,b)
时,以下任一项均为真:
a==b
。在这种情况下,不会发生任何事情,dup2()
过早返回。没有关闭任何文件描述符
a!=b
。在这种情况下,必要时关闭b
,然后使b
引用与a
相同的文件表条目。文件表条目是包含当前文件偏移量和文件状态标志的结构;多个文件描述符可以指向同一个文件表条目,这正是复制文件描述符时发生的情况。因此,dup2(a,b)
具有使a
和b
共享相同文件表条目的效果。因此,写入a
或b
将最终写入同一文件。因此,关闭的文件是b
,而不是a
。如果dup2(a,STDOUT\u FILENO)
,则关闭STDOUT
,并使STDOUT
的文件描述符指向与a
相同的文件表条目。任何写入stdout
的程序都将改为写入文件,因为stdout
的文件描述符指向您复制的文件
更新:
因此,对于您的具体问题,在简要查看代码之后,我要说:
您不应该在此处调用close(STDOUT\u FILENO)
:
if (command->getOutputFD() == REDIRECT) {
if ((outfd = open(outfile, O_CREAT | O_WRONLY | O_TRUNC)) == -1)
return false;
if (dup2(outfd, STDOUT_FILENO) == -1)
return false;
close(STDOUT_FILENO);
}
如果关闭stdout
,将来尝试写入stdout
时将出现错误。这就是为什么会出现ls:write error:Bad file descriptor
。毕竟,ls
正在写入stdout
,但您关闭了它。哎呀
您正在反向操作:您想关闭outpd
。您打开了outpd
,以便可以将STDOUT\u FILENO
重定向到outpd
,一旦重定向完成,您就不再需要outpd
,您可以关闭它。但是您肯定不想关闭stdout
,因为这样做的目的是让stdout
ᕕ( ᐛ )ᕗ ls -l
total 68
-rwxrwxrwx 1 cook cook 242 May 31 18:31 CMakeLists.txt
-rwxrwxrwx 1 cook cook 617 Jun 1 22:40 Command.cpp
-rwxrwxrwx 1 cook cook 9430 Jun 8 18:02 ExecuteExternalCommand.cpp
-rwxrwxrwx 1 cook cook 682 May 31 18:35 ExecuteInternalCommand.cpp
drwxrwxrwx 2 cook cook 4096 Jun 8 17:16 headers
drwxrwxrwx 2 cook cook 4096 May 31 18:32 implementation files
-rwxr-xr-x 1 cook cook 25772 Jun 8 18:12 LeShell
-rwxrwxrwx 1 cook cook 243 Jun 5 13:02 Makefile
-rwxrwxrwx 1 cook cook 831 Jun 3 12:10 Shell.cpp
ᕕ( ᐛ )ᕗ ls -l > output.txt
ls: write error: Bad file descriptor
ᕕ( ᐛ )ᕗ ls -l | grep "cook"
ᕕ( ᐛ )ᕗ
if (command->getOutputFD() == REDIRECT) {
if ((outfd = open(outfile, O_CREAT | O_WRONLY | O_TRUNC)) == -1)
return false;
if (dup2(outfd, STDOUT_FILENO) == -1)
return false;
close(STDOUT_FILENO);
}
if (command->getOutputFD() == REDIRECT) {
if ((outfd = open(outfile, O_CREAT | O_WRONLY | O_TRUNC)) == -1)
return false;
if (dup2(outfd, STDOUT_FILENO) == -1)
return false;
if (outfd != STDOUT_FILENO)
close(outfd);
}
else if (command->getOutputFD() == REDIRECTAPPEND) {
if ((outfd = open(outfile, O_CREAT | O_WRONLY | O_APPEND)) == -1)
return false;
if (dup2(outfd, STDOUT_FILENO) == -1)
return false;
if (outfd != STDOUT_FILENO)
close(STDOUT_FILENO);
}