【问题标题】:Why read() on file descriptor fails if lseek() is used?如果使用 lseek(),为什么文件描述符上的 read() 会失败?
【发布时间】:2016-10-12 03:54:34
【问题描述】:

在下面的示例中,我们关闭默认 stderr 并通过 fdopen() 在临时文件上重新打开它,使用描述符 2,这是从临时文件描述符 dup()'ed。然后我们直接write()这个描述符2。我们可以安全地执行此操作,因为这是对文件的第一次写入操作,因此它具有空缓冲区。在此之后,我们fprintf() 到新的stderr。然后我们关闭stderr(因此,它的关联描述符2被自动关闭)。原始描述符fd 仍然有效。通过它,我们转到临时文件的开头,读取其内容并将它们打印到标准输出。但是输出是乱码:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>

int main(void)
{
  int fd;
  char buf[200];
  int n;
  char fname[] = "/tmp/tst-perror.XXXXXX";
  fd = mkstemp (fname);
  fclose (stderr);
  dup2 (fd, 2);
  stderr = fdopen (2, "w");
  fd = fileno(stderr);
  char *s = "this is a test\n";
  n = write(fd, s, strlen(s));
  fprintf(stderr, "multibyte string\n");
  fclose (stderr);
//  close(fd);
//  fd = open(fname, O_RDONLY);
  lseek (fd, 0, SEEK_SET);
  n = read (fd, buf, sizeof (buf));
  printf("%.*s", (int) n, buf);
  close (fd);
  return 0;
}

输出是:

$ ./a.out
����

如果我们取消注释“close”和“open”行并注释“lseek”行,输出如预期:

$ ./a.out
this is a test
multibyte string

write() 没有缓冲区,stderr 在关闭时被注销,所以 如果我们在读取文件之前不关闭文件,为什么输出会出现乱码?

【问题讨论】:

  • 另外,请注意lseek 在哪个流上。请参阅man 2 lseek 中的注释,“某些设备无法搜索,POSIX 未指定哪些设备必须支持 lseek()。”.

标签: c file-io io posix


【解决方案1】:

没有检查函数的返回值。如果它在那里,您就会发现错误。

不管怎样,问题是:

  fd = fileno(stderr);     // getting the fd from current stderr
  fclose (stderr);         // closing stderr
  ...
  lseek (fd, 0, SEEK_SET); // seeking on fd which was already closed

在最后一次调用和后续调用中,fd 实际上是未定义的(或者更确切地说,它引用了关闭的文件描述符)。因此对fd 的任何操作都会失败EBADF(不是有效的文件描述符)。

显然,如果您再次包含fd = open(...)fd 将变为有效并且代码将起作用。

【讨论】:

  • 确实,我的真正意图是在“fileno”和“write”行中使用“fd2”。现在一切正常。
猜你喜欢
  • 2019-06-11
  • 2012-11-18
  • 1970-01-01
  • 2013-03-01
  • 2014-04-24
  • 1970-01-01
  • 2023-03-06
相关资源
最近更新 更多