我发布我的代码只是为了让问题更加清晰明了。我并不是在寻求你的帮助来修复它,我更想理解dup2系统调用,但我从手册和其他stackoverflow问题中并没有完全掌握它。
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;
}
为了让您了解上下文,这段代码代表基本Linux shell的执行步骤。命令对象包含命令参数、IO“名称”和IO描述符(我想我可能会将文件描述符作为字段删除)。
我最难理解的是何时关闭哪些文件描述符。我猜我会问一些问题,试图提高对概念的理解。
1)使用用于处理管道的文件描述符数组时,父进程拥有所有这些描述符的副本。父进程何时关闭描述符?更重要的是,关闭哪些描述符?是全部吗?所有未被执行命令使用的描述符?
2)在子进程中处理管道时,哪些进程会保留哪些描述符?比如如果我执行命令:ls -l | grep "[username]",哪些描述符应该保留给ls进程?只有管道的写端吗?如果是这样,那么何时?同样的问题也适用于grep命令。
3)当我处理IO重定向到文件时,必须打开一个新文件并将其复制到STDOUT(我不支持输入重定向)。这个描述符什么时候关闭?我在示例中看到它在调用dup2后立即关闭,但是如果文件已经关闭,任何东西怎么写入文件呢?
提前感谢您。我已经卡在这个问题上好几天了,我真的很想完成这个项目。
编辑:我已经更新了此处的修改代码和示例输出,供有兴趣为我的问题提供具体帮助的人参考。首先,我有一个完整的for循环来处理执行。它已经更新了我的关闭调用,涉及各种文件描述符。
while(currCommand != NULL) {
command = currCommand->getData();
infile = command->getInFileName();
outfile = command->getOutFileName();
arguments = command->getArgList();
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;
if (dup2(outfd, STDOUT_FILENO) == -1)
return false;
close(STDOUT_FILENO);
}
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;
close(STDOUT_FILENO);
}
else {
if (dup2(pipefd[pipeIndex + 1], STDOUT_FILENO) == -1)
return false;
close(pipefd[pipeIndex]);
}
}
pipeIndex++;
if(strcmp("STDIN", infile)) {
if(dup2(pipefd[pipeIndex - 1], STDIN_FILENO) == -1)
return false;
close(pipefd[pipeIndex]);
pipeIndex++;
}
if (execvp(arguments[0], arguments) == -1) {
std::cerr << "Error!" << std::endl;
_Exit(0);
}
}
else if(pid == -1) {
return false;
}
currCommand = currCommand->getNext();
}
for(int i = 0; i < numPipes * 2; i++)
close(pipefd[i]);
for(int i = 0; i < commands->size();i++) {
if(wait(status) == -1)
return false;
}
执行此代码时,我收到以下输出
ᕕ( ᐛ )ᕗ 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"
ᕕ( ᐛ )ᕗ
ls -l > output.txt
的输出意味着我关闭了错误的描述符,而关闭其他相关描述符时,虽然没有错误,但是也没有将输出写入文件。如ls -l
和grep "cook"
所示,应该在控制台上生成输出。