由信号中断的系统调用仍然需要完成

很多系统调用如close( fd )可以被信号中断。 在这种情况下通常返回-1errno被设置为EINTR

问题是什么是正确的做法? 说,我仍然希望这个fd被closures。

我能想到的是:

 while( close( fd ) == -1 ) if( errno != EINTR ) { ReportError(); break; } 

有人可以build议一个更好/更优雅/标准的方式来处理这种情况?

更新:正如注意到的多路复用器,安装信号处理程序时可以使用SA_ RESTART标志。 有人可以告诉我哪些function可以在所有POSIX系统(不仅仅是Linux )上重新启动吗?

有些系统调用是可重新启动的,这意味着如果中断,内核将重新启动调用,如果在安装信号处理程序时使用了SA_RESTART标志,那么信号(7)手册页将显示:

如果以下接口之一的阻塞呼叫被信号处理程序中断, 则在信号处理程序返回后,如果使用SA_RESTART标志,呼叫将自动重新启动 ; 否则该调用将失败,出现错误EINTR:

它没有提到close()是否可重启,但是它们是:

(2),readv(2),write(2),writev(2),ioctl(2),open(2),wait(2),wait3(2),wait4(2),waitid(2), (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指定的,但是它是可选的,所以你应该检查你的操作系统的不可重启调用列表,如果它不是应该重新启动。 这是我的问题: 如何知道Linux系统调用是否可重启?

更新:关闭是一种特殊情况,它不能重新启动,不应该在Linux中重试,请参阅此答案的更多详细信息: https : //stackoverflow.com/a/14431867/1157444

记录:在基本上每个UNIX上,如果返回EINTR,则close() 不得重试不要waitpid()read()那样放置一个EINTR重试循环。 有关更多详细信息,请参阅此页面: 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上的阻塞系统调用内部,那么…取决于。

假设你在更短的代码之后,你可以尝试如下所示:

 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; }