【问题标题】:Bad file descriptor when running cat command in a shell在 shell 中运行 cat 命令时文件描述符错误
【发布时间】:2015-12-10 19:48:51
【问题描述】:

我正在尝试在为类 Unix 操作系统 (xV6) 编写的 shell 中实现 I/O 重定向。在我正在阅读的操作系统手册中,我发现以下代码将在 shell 中运行以执行 cat 命令:

char *argv[2];
argv[0] = "cat";
argv[1] = 0;
if(fork() == 0) {
    close(0);
    open("input.txt", O_RDONLY);
    exec("cat", argv);
}

我修改了代码以在我的 shell 中运行,该 shell 的 argv 数组位于另一个函数中,但它保留了该功能。出于某种原因,当我运行 cat < input.txt 时,shell 输出:

cat: -: Bad file descriptor
cat: closing standard input: Bad file descriptor

我还是操作系统编程的新手,所以我并不完全清楚 I/O 重定向的所有功能,但我认为我拥有的代码应该可以工作。什么可能导致问题。我有下面的 I/O 重定向代码:

case '<':
    ecmd = (struct execcmd*)cmd;
    rcmd = (struct redircmd*)cmd;

    if(fork() == 0){
      close(0);
      open("input", O_RDONLY);
      execvp(ecmd->argv[0], ecmd->argv );
    }
    runcmd(rcmd->cmd);
    break;

编辑

我做了strace -e open ls 并得到:

open("/etc/ld.so.cache",    O_RDONLY|O_CLOEXEC) = 3
open("/lib/x86_64-linux-gnu/libselinux.so.1", O_RDONLY|O_CLOEXEC) = 3
open("/lib/x86_64-linux-gnu/libacl.so.1", O_RDONLY|O_CLOEXEC) = 3
open("/lib/x86_64-linux-gnu/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
open("/lib/x86_64-linux-gnu/libpcre.so.3", O_RDONLY|O_CLOEXEC) = 3
open("/lib/x86_64-linux-gnu/libdl.so.2", O_RDONLY|O_CLOEXEC) = 3
open("/lib/x86_64-linux-gnu/libattr.so.1", O_RDONLY|O_CLOEXEC) = 3
open("/proc/filesystems", O_RDONLY)     = 3
open("/usr/lib/locale/locale-archive", O_RDONLY|O_CLOEXEC) = 3
a.out  sh.c  test
+++ exited with 0 +++

编辑 2

出于某种原因,此案例的代码有效,但我不确定为什么:

case '<':
    rcmd = (struct redircmd*)cmd;
    close(rcmd->fd);
    if(open(rcmd->file, rcmd->mode) < 0){
      printf(2, "Cannot open file %s\n", rcmd->file);
      perror(rcmd->file);
      exit(1);
    }
    runcmd(rcmd->cmd);
    break;

【问题讨论】:

  • 你能打印出open()在你的代码中返回的值吗?
  • @MarkPlotnick 如何检查 open() 返回的内容?在 shell 中,它只输出错误的描述符消息。
  • 您也可以尝试在strace 下运行您的程序,以跟踪它发出的所有系统调用:strace -f -o /some/output/file/name program [args]。见linux.die.net/man/1/strace
  • 您可以将open 的结果分配给一个变量,并使用printf 打印该变量的值。在您的书中查找 fork 示例,了解如何进行变量赋值和对 printf 的调用。
  • 书中的代码实现了一个非常具体的I/O重定向实例:从一个名为input.txt的文件中读取。如果该文件不存在,open 将返回特殊值-1,它不是有效的文件描述符,用于指示错误。第二个代码 sn -p 更好;它似乎使用用户提供的文件名,并检查从open 返回的值。

标签: c linux operating-system xv6


【解决方案1】:

您不能只关闭标准输入,然后再打开另一个文件。它不会自动成为您的新标准输入。你想要的是使用系统调用 dup2 可以将一个文件描述符“重定向”到另一个。

int fd = open("input.txt", O_RDONLY);
dup2(fd, 0); // stdin now points to fd
close(fd);

有关详细信息,请参阅man 2 dup2。请注意,open 和 dup2 都可能会失败,因此您应该检查它们的返回值,如果这是一个问题。

编辑:这实际上有时会起作用,因为POSIX 保证内核将始终分配最低的空闲文件描述符。但它不是线程安全的,一般来说只是不好的风格。我建议始终执行 dup2,即使在某些情况下您可以不使用它。

【讨论】:

  • 我刚刚用似乎可以工作的代码编辑了帖子。你知道为什么这段代码有效吗?它似乎并没有比关闭以前的 fd 做更多的事情。它甚至不需要复制。
  • 当你打开一个文件时,内核通常会选择下一个空闲的文件描述符。因此,您打开的第一个文件将是 fd 3,然后是 fd 4,以此类推。当您关闭 fd 0 时,它变为空闲文件,然后内核将分配它。然而,内核不需要以任何特定的顺序分配 FD,这样的编码是非常糟糕的风格。另外,如果您发现我的回答有帮助,请考虑“接受”它。编辑:显然内核 需要始终选择最低的可用 FD。不过,我仍然会考虑这种糟糕的风格。 pubs.opengroup.org/onlinepubs/009695399/functions/open.html
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-09-08
  • 2013-05-12
相关资源
最近更新 更多