经过多年的经验,我觉得与Posix和平相处.
然后我从Linus Torvalds 读到这条消息,大约2002年:
Run Code Online (Sandbox Code Playgroud)int ret; do { ret = close(fd); } while(ret == -1 && errno != EBADF);没有.
以上是
(a)不便携
(b)不是现行做法
"不可移植"部分来自这样一个事实:(正如有人指出的那样),一个线程环境,其中内核确实关闭FD上的错误,FD可能已被有效地重用(由内核)用于其他一些线程并且第二次关闭FD是一个BUG.
不仅循环直到不可EBADF移植,而且任何循环都是,由于竞争条件我可能会注意到如果我没有通过将这些事情视为理所当然"取得和平".
但是,在GCC C++标准库实现中basic_file_stdio.cc,我们有
do
__err = fclose(_M_cfile);
while (__err && errno == EINTR);
Run Code Online (Sandbox Code Playgroud)
这个库的主要目标是Linux,但似乎并没有听从Linus.
据我所知,EINTR只有在系统调用块之后才会发生,这意味着内核在开始任何被中断的工作之前收到了释放描述符的请求.所以没有必要循环.实际上,SA_RESTART信号行为close默认不适用于并生成这样的循环,正是因为它不安全.
这是一个标准的库bug,对吗?在每个由C++应用程序关闭的文件上.
编辑:为了避免在一些大师出现回答之前引起太多警报,我应该注意到,close在特定情况下似乎只允许阻止,可能没有一个适用于常规文件.我不是在所有的细节清晰,但你不应该看到EINTR从close没有通过选择到的东西fcntl或setsockopt.然而,这种可能性使通用库代码更加危险.
SA_RESTART根据man 信号(7),如果在安装信号处理程序时使用了标志,则内核可以透明地重启某些系统调用:
如果对信号处理程序中断了对以下某个接口的阻塞调用,则在使用SA_RESTART标志后,如果信号处理程序返回,则调用将自动重新启动 ; 否则呼叫将失败并显示错误EINTR:
然后它提到了一些可以(并且不能)重新启动的系统调用,但是close()在任何一个地方都没有提到,我怎么知道是否close()可以重启或其他任何功能?是POSIX指定它还是特定于Linux的行为?我在哪里可以找到更多信息?
在使用EINTR或EIO失败的close()系统调用之后,未指定文件是否已关闭.(http://pubs.opengroup.org/onlinepubs/9699919799/)在多线程应用程序中,重试关闭可能会关闭其他线程打开的不相关文件.不重试关闭可能导致无法打开的文件描述符堆积.一个干净的解决方案可能涉及在新近关闭的文件描述符上调用fstat()和一个非常复杂的锁定机制.此外,使用单个互斥锁序列化所有打开/关闭/接受/ ...调用可能是一种选择.
这些解决方案没有考虑库函数可能以不可控制的方式打开和关闭文件,例如,/ proc文件系统中的std :: thread :: hardware_concurrency()的一些实现打开文件.
文件流在[file.streams] C++标准部分中不是一个选项.
是否有一个简单而可靠的机制来在存在多个线程的情况下关闭文件?
编辑:
常规文件:虽然大多数情况下不会有不可用的打开文件描述符累积,但有两个条件可能会触发问题:1.某些恶意软件以高频率发出的信号2.刷新缓存之前失去连接的网络文件系统.
套接字:根据Stevens/Fenner/Rudoff的说法,如果套接字选项SO_LINGER设置在引用连接套接字的文件描述符上,并且在close()期间,定时器在FIN-ACK关闭序列完成之前经过,则close()失败作为共同程序的一部分.Linux没有显示这种行为,但FreeBSD会这样做,并且还将errno设置为EAGAIN.据我所知,在这种情况下,未指定文件描述符是否无效.用于测试行为的C++代码:http://www.longhaulmail.de/misc/close.txt那里的测试代码输出看起来像FreeBSD中的竞争条件,如果不是,为什么不呢?
人们可能会在调用close()期间考虑信号.