如何检查文件描述符是否被关闭?

12
在 POSIX 环境中,当使用系统调用来操作文本文件(如 open()close()read()write())时,使用 close(file_descriptor) 关闭文件描述符后,是否有一种方法可以检查我是否成功关闭了它?

示例

int main(int argc, char **argv)
{
    int input_file; // file descriptor for input file
    int output_file; // file descriptor for output file
    input_file = open(argv[1], O_RDONLY));
    ouput_file = open(argv[2], ...file properties & permissions and crap.....);

    // blah blah blah...

    close(input_file);
    close(output_file);

    // what can I code here to check if the file descriptor was actually closed?
}
3个回答

8
最简单的方法可能是只需检查您的第一个close的返回值。
if (close(input_file)) {
    perror("close");
} else {
    // all good
}

这是唯一线程安全的方法。在多线程程序中,另一个线程可能会获得一个新的文件描述符,该描述符重新使用了刚关闭的fd号码。
在单线程代码中,没有任何东西可以在关闭和检查之间回收fd,如果您希望在此之后检查文件描述符是否有效,则可以使用任何消耗文件描述符并检查其错误代码的函数。最不干扰且轻量级的可能是fcntl/F_GETFL。
if (fcntl(fd, F_GETFL) < 0 && errno == EBADF) {
    // file descriptor is invalid or closed
}

您也可以再次调用close,但这可能会导致EBADF失败。

if (close(fd) && errno == EBADF) {
    // file descriptor is invalid or closed
} else {
    // we successfully closed fd *now* (e.g. first close failed with EINTR)
    // or there was a different error
}

close在成功时不会修改errno,但只有在close返回非零值时才读取errno,这意味着它已被设置。


1
@SteveJobs:在多线程环境中,这些方法都是危险的,因为所涉及的文件描述符自上次关闭以来可能已被另一个线程回收。 - alk
1
邪恶的赋值。应该是 errno == EBADF - Isaac
3
多线程编程要小心。文件描述符会被重复使用,因此关闭一个文件描述符可能会导致在 close() 之后立即打开另一个具有相同值的文件描述符 - 如果另一个线程很快地进入并打开另一个文件。实现线程安全的唯一方法是检查返回值。 - Philip Couling
Yoda条件是我推荐的一种方法,如果你要检查全局变量是否等于某个值。你必须保护自己免受邪恶赋值的侵害。EBADF == errno - Chris Watts
1
这个答案不能保证有效。如果close()成功,它不会修改errno。在调用close()之前,需要将errno设置为0。之后检查文件描述符是否有效也无法在多线程程序或任何其他可以重复使用文件描述符的情况下工作。 - Andrew Henle
显示剩余2条评论

3
您可以根据 close() 的返回值和相应的错误编号进行检查。
来自于http://pubs.opengroup.org/onlinepubs/009695399/functions/close.html 成功完成后,将返回0; 否则,将返回-1,并设置errno以指示错误。 [...]
如果出现以下情况,则close()函数将失败: [EBADF] fildes参数不是有效的文件描述符。

0

检查文件描述符是否无效的唯一方法是在定义时将其明确设置为(例如)-1,并且每次成功关闭后都要这样做。

可以使用宏来完成:

#define FD_INVALID (-1)

#define FD_CLOSE_AND_INVALIDATE(fd) \
do { \
  if (-1 == close(fd)) \
  { \
    perror("close failed"); \
  } \
  else \
  { \
    (fd) = FD_INVALID; \
  } \
} while (0)

int main(int argc, char ** argv)
{
  int input_file = FD_INVALID; /* Properly initialise file descriptor for input file */
  int output_file = FD_INVALID; /*  Properly initialise file descriptor for output file */

  input_file = open(argv[1], O_RDONLY));
  ouput_file = open(argv[2], ...);

  /* Do something. */

  FD_CLOSE_AND_INVALIDATE(input_file);
  FD_CLOSE_AND_INVALIDATE(output_file);

  /* To check if the file descriptor was actually closed just test for FD_INVALID. */
}

如果系统有许多文件描述符,请维护一个文件描述符的哈希映射表,并在每次关闭时将它们设置为-1。 - Helping Bean

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接