【问题标题】:lseek/write suddenly returns -1 with errno = 9 (Bad file descriptor)lseek/write 突然返回 -1 且 errno = 9(错误的文件描述符)
【发布时间】:2010-03-30 12:01:12
【问题描述】:

我的应用程序使用lseek() 来寻找写入数据所需的位置。 该文件已使用open() 成功打开,我的应用程序能够多次使用lseek()write()

在给定时间,对于某些不容易重现的用户,lseek() 返回 -1,errno 为 9。在此之前文件未关闭,文件句柄 (int) 未重置。

之后,创建另一个文件; open() 再次正常,lseek()write() 再次正常工作。

更糟糕的是,该用户再次尝试了完整的序列,一切都很好。

所以我的问题是,操作系统可以出于某种原因为我关闭文件句柄吗? 什么可能导致这种情况?某种文件索引器或文件扫描器?

解决这个问题的最佳方法是什么?这个伪代码是最好的解决方案吗? (别管代码布局,会为它创建函数)

int fd=open(...);
if (fd>-1) {
  long result = lseek(fd,....);
  if (result == -1 && errno==9) {
      close(fd..); //make sure we try to close nicely
      fd=open(...);

      result = lseek(fd,....);
  }
}

有人有类似的经历吗?

总结:对于给定的 fd,文件查找和写入工作正常,但突然无故返回 errno=9。

【问题讨论】:

  • 如果我尝试打开我的代码将无法编译(...);奇数。
  • 如果 lseek() 使用 EBADF 失败,您可以很确定同一个文件描述符上的 close() 也会因 EBADF 失败。而且由于您不检查您的重新打开或重新搜索电话,任何事情都可能发生。
  • @Ger Teunis:如果您发布的是伪代码,请将其标记为伪代码,这样人们就不会抱怨(就像蒂姆所做的那样)或只是告诉您“为我工作”。尽可能避免发布伪代码——寻找尽可能小的代码来重现问题的过程是调试艺术的重要组成部分。很多时候问题出在你认为的地方,你发布的伪代码甚至不包含问题的核心(在这种情况下,文件驻留在网络挂载上)。
  • 我错过了伪代码部分。对此我深表歉意,我的错,以及我在没有仔细检查的情况下使用强语言的错。
  • 支持明智的驴友投票

标签: c file-io handle seek


【解决方案1】:

所以我的问题是,操作系统可以出于某种原因为我关闭文件句柄吗?什么可能导致>这个?某种文件索引器或文件扫描器?

不,这不会发生。

解决这个问题的最佳方法是什么?是 这个伪代码最好的解决方案? (不要介意代码布局,将 为它创建函数)

不,最好的方法是找到错误并修复它。

有人有类似的经历吗?

我曾多次看到 fds 搞砸了,在某些情况下会导致 EBADF, 并在其他人中大放异彩,这是:

  • 缓冲区溢出 - 溢出一些东西并将一个无意义的值写入“int fd;”变量。
  • 由于某些极端情况而发生的愚蠢错误 if(fd = foo[i].fd) 当他们的意思是 if(fd == foo[i].fd)
  • 线程之间的竞争条件,某些线程关闭了其他线程想要使用的错误文件描述符。

如果您能找到重现此问题的方法,请在“strace”下运行您的程序,这样您就可以看到发生了什么。

【讨论】:

  • 我同意找到这个错误。我会做更多的代码审查,但我会检查如果网络驱动器断开连接会发生什么。也许这会导致错误的文件描述符?
  • 经过仔细检查,fd 根本没有改变,只有一个线程在做磁盘 IO(所有文件处理)。我可以假设如果我不关闭 fd,fd 保持不变我不应该得到 errno 和 -1 结果,对吗?确实如此。
  • 虽然某些特定的操作系统,利用某些特定的文件系统/网络 IO 可能会出现一些极端情况下的错误,导致这种情况发生是完全可能的,但这只是猜测。不过,当这种情况发生时,对所有线程进行 strace 会非常有帮助
  • 经过更多的记录和调试后,我的代码中没有错误。 fh 永远不会改变,也不会关闭它们。文件句柄是在我的程序范围之外控制的(打开和写入),所以这也不是内存问题。你就在这里。所有文件句柄都可以。不关闭文件句柄。我的代码没有问题,只是 osx 将 fh 标记为无效。重新操作文件也返回了相同的 fd id。有趣的东西。
【解决方案2】:

操作系统不会随机关闭文件句柄(我假设是类 Unix 系统)。如果您的文件句柄已关闭,那么您的代码可能有问题,很可能是在其他地方(感谢 C 语言和 Unix API,这实际上可以在代码中的任何位置,并且可能是由于例如轻微的缓冲区在某些看起来不相关的代码中溢出)。

您的伪代码是最糟糕的解决方案,因为它会给您一种已经解决问题的印象,而错误仍然潜伏着。

我建议您在打开和关闭文件或套接字的任何位置添加调试打印(即printf() 调用)。另外,试试Valgrind

(我昨天刚刚发生了一个令人毛骨悚然的 off-by-1 缓冲区溢出,它损坏了编译器为保存 CPU 寄存器而生成的临时槽的最低有效字节;间接影响是另一个函数中的结构似乎移动了几个字节。我花了很长时间才理解发生了什么,包括彻底阅读 Mips 汇编代码)。

【讨论】:

  • 期待这个反应,这当然是最明显的一个。不自大,但我几乎可以肯定没有内存泄漏。文件句柄 id 保持不变(选中),并且 lseek 和 write 在它正常工作之前。写操作之间没有任何动态(内存方面)发生。正在记录所有文件 IO 代码(NSlog)并且一切正常,直到在某些情况下(第二次不可重现)突然出现 -1 且 errno=9。您必须同意,只要 fd 没有改变, errno=9 就不太可能出现对吗?出于某种原因,它确实如此。
  • @Thomas:+1 我完全同意。错误应该被修复,而不是隐藏。 @Ger,你怎么能确定你没有堆栈或堆损坏?这听起来像是一个需要修复的险恶错误
  • 我当然同意,最好找到原因。但乍一看,原因似乎超出了我的范围。看起来仍然是这样,因为当前 fd 不可能被另一个值覆盖。
  • 有人建议如果(网络)驱动器突然断开连接会发生什么。将对此进行调查。
【解决方案3】:

我不知道你有什么类型的设置,但是下面的场景,我认为可以产生这样的效果(或者类似的效果)。我尚未对此进行测试以验证,因此请谨慎对待。

如果您打开的文件/设备实现为服务器应用程序(例如 NFS),请考虑如果服务器应用程序出现故障/重新启动/重新启动会发生什么情况。文件描述符虽然最初在客户端有效,但可能不再映射到服务器端的有效文件句柄。这可能会导致一系列事件,其中客户端将获得 EBADF。

希望这会有所帮助。

【讨论】:

  • 你就在这里。所有文件句柄都可以。不关闭文件句柄。我的代码没有问题,只是 osx 将 fh 标记为无效。重新操作文件也会返回 fd id。有趣的东西。
【解决方案4】:

不,操作系统不应该像那样关闭文件句柄,并且其他应用程序(文件扫描仪等)不应该能够这样做。

不要解决问题,找到它的来源。如果您不知道问题的原因是什么,您将永远不会知道您的解决方法是否真的确实有效。

  1. 检查您的假设。 通话前errno 是否设置为 0? fd 在通话时真的有效吗? (我知道你说的是,但你检查了吗?)
  2. puts( strerror( 9 ) ); 在您的平台上的输出是什么?

【讨论】:

  • errno 之前为 0,lseek 结果突然为 -1。 fd 确实是打开的,因为在同一迭代之前写入确实有效。
  • 那么,继续检查你的假设。你比我更了解你的代码。在每次调用之前打印 fd 的值将是我的下一步行动。
  • 好主意,也是一个建议,当(网络)驱动器由于某种原因断开连接时会发生什么。
猜你喜欢
  • 1970-01-01
  • 2020-05-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-09-07
  • 1970-01-01
  • 2015-05-20
  • 2021-04-29
相关资源
最近更新 更多