许多系统调用如close( fd )
可以被信号中断。在这种情况下,通常会返回-1
并设置errno
EINTR
。
问题是做什么是正确的?说,我仍然希望关闭此fd
。
我能想出的是:
while( close( fd ) == -1 )
if( errno != EINTR ) {
ReportError();
break;
}
有人可以提出更好/更优雅/标准的方法来处理这种情况吗?
更新:
正如mux所注意到的,在安装信号处理程序时可以使用SA _ RESTART
标志。
有人可以告诉我哪些功能可以保证在所有POSIX
系统上重启(不只是Linux
)?
答案 0 :(得分:9)
某些系统调用是可重启的,这意味着内核将在中断时重新启动调用,如果在安装信号处理程序时使用SA_RESTART
标志,signal(7)手册页说:
如果对以下某个接口的阻止呼叫被中断 通过信号处理程序,然后调用将自动重启 信号之后 如果使用了SA_RESTART标志,则处理程序返回;否则呼叫将失败并显示错误EINTR:
没有提及close()
是否可重启,但这些是:
read(2),readv(2),write(2),writev(2),ioctl(2),open(2),wait(2), wait3(2),wait4(2),waitid(2)和waitpid,accept(2),connect(2), recv(2),recvfrom(2),recvmsg(2),send(2),sendto(2)和sendmsg(2) flock(2)和fcntl(2)mq_receive(3),mq_timedreceive(3),mq_send(3), 和mq_timedsend(3)sem_wait(3)和sem_timedwait(3)futex(2)
请注意,这些详细信息,特别是不可重新启动的调用列表,是特定于Linux的
我发布了一个相关的问题,关于哪些系统调用可以重启,如果它是由某个POSIX指定的,它是由POSIX指定的,但是它是可选的,所以你应该查看你的操作系统的不可重启调用列表,如果它不在那里它应该是可重启的。这是我的问题: How to know if a Linux system call is restartable or not?
更新:关闭是一种特殊情况,它不能重新启动,不应在Linux中重试,请参阅此答案以获取更多详细信息: https://stackoverflow.com/a/14431867/1157444
答案 1 :(得分:3)
对于记录:基本上在每个UNIX上,如果返回EINTR,则不能重试close()
。 不要将EINTR重试循环放置到适当的位置,就像使用waitpid()
或read()
一样。有关详细信息,请参阅此页面:http://austingroupbugs.net/view.php?id=529在Linux,Solaris,BSD和其他人上,重试close()
不正确。 HP-UX是我能找到的唯一需要它的常见(!)系统。
对于read()
和select()
以及waitpid()
等,EINTR意味着与close()
相比有所不同。对于大多数电话,你在EINTR上重试,因为你要求完成哪些阻止,如果你被打断,这意味着它没有发生,所以你再试一次。对于close()
,您请求的操作是从fd表中删除一个条目,该条目是即时的,没有错误,并且无论close()
返回什么都会发生。[*]唯一的原因close()
块有时候,对于特殊语义(如TCP逗留),它可以等到I / O完成后再返回。如果关闭返回EINTR,这意味着你要求它等待,但它不能。但是, fd仍然关闭;你只是失去了等待它的机会。
结论:除非您知道无法接收信号,否则使用close()
进行等待是一件非常愚蠢的事情。使用应用程序级ACK(TCP)或fsync(文件I / O)以确保在关闭fd之前完成任何写入。
[*]有一个警告:如果进程的另一个线程在同一个fd上的阻塞系统调用内,那么......它取决于。
答案 2 :(得分:2)
假设您正在使用更短的代码,您可以尝试以下方法:
while (((rc = close (fd)) == -1) && (errno == EINTR));
if (rc == -1)
complainBitterly (errno);
假设您除了更短的时间之外还有更多可读代码,只需创建一个函数:
int closeWithRetry (int fd);
并将可读代码放在那里。然后它并不重要,它仍然是你所谓的单行,但你可以使函数体本身非常易读:
int closeWithRetry (int fd) {
// Initial close attempt.
int rc = close (fd);
// As long as you failed with EINTR, keep trying.
// Possibly with a limit (count or time-based).
while ((rc == -1) && (errno == EINTR))
rc = close (fd);
// Once either success or non-retry failure, return error code.
return rc;
}