被信号中断的系统调用仍然需要完成
System call interrupted by a signal still has to be completed
很多像close( fd )
这样的系统调用可以被信号打断。在这种情况下,通常返回-1
并errno
设置为EINTR
。
问题是什么是正确的做法?说,我仍然希望关闭此fd
。
我能想到的是:
while( close( fd ) == -1 )
if( errno != EINTR ) {
ReportError();
break;
}
谁能建议一种更好/更优雅/标准的方式来处理这种情况?
更新: 正如 mux 所注意到的,安装信号处理程序时可以使用SA_RESTART
标志。 有人可以告诉我哪些功能可以保证在所有POSIX
系统上重新启动(不仅是Linux
)?
一些系统调用是可重新启动的, 这意味着如果中断内核将重新启动调用, 如果在安装信号处理程序时使用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) 羊群(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
假设你追求较短的代码,你可以尝试这样的东西:
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;
}
郑重声明:基本上在每个 UNIX 上,如果close()
返回 EINTR,则不得重试。不要像waitpid()
或read()
那样放置 EINTR 重试循环进行关闭。有关更多详细信息,请参阅此页面:http://austingroupbugs.net/view.php?id=529 在 linux、Solaris、BSD 等设备上,重试close()
是不正确的。HP-UX是我能找到的唯一需要这个的常见(!)系统。
EINTR 对read()
、select()
和waitpid()
等的意义与close()
的含义非常不同。对于大多数呼叫,您会在 EINTR 上重试,因为您要求执行哪些阻止的操作,如果您被打断,则意味着它没有发生,因此您重试。对于close()
,您请求的操作是从 fd 表中删除一个条目,这是即时的,没有错误,并且无论close()
返回什么都会始终发生。[*]close()
块的唯一原因是,有时,对于特殊的语义(如TCP徘徊),它可以等到I/O完成再返回。如果 close 返回 EINTR,这意味着您要求它等待,但它不能。但是,FD仍然关闭;你只是失去了等待它的机会。
结论:除非你知道你无法接收信号,否则使用close()
等待是一件非常愚蠢的事情。 使用应用程序级 ACK (TCP) 或 fsync(文件 I/O) 确保在关闭 fd 之前完成任何写入。
[*] 有一个警告:如果进程的另一个线程位于同一 fd 上的阻塞系统调用内,那么......这要看情况。
- Qt VTK交互风格的信号到小部件
- 获取日期异步信号安全吗?如果在信号处理程序中使用,它会导致死锁吗
- 如何将点击的信号和插槽添加到qt中的自定义按钮中
- 如何在没有信号的情况下从C++执行QML插槽
- 线程之间的布尔停止信号
- 如何在信号处理程序和普通函数中对全局变量进行互斥读写操作
- 当用户在qtablewidget中输入单元格时,如何获得信号?C++
- 有可能在信号处理程序中设置promise吗
- 被信号中断的系统调用仍然需要完成
- 如何中断epoll_pwait以捕获信号
- 在不中断 IO 的情况下处理信号
- Linux 中的信号处理和中断的函数调用
- C++当被ctrl-c中断时,在死亡之前调用带有参数(信号号除外)的函数
- 我想知道当系统调用()被中断时,哪个信号到达
- 尝试使用 pselect() 阻止竞争条件,但信号不会中断
- boost::mpi阻塞不被linux信号中断的IO
- 线程中断与插槽和信号
- 如何在Python中捕获阻塞boost c++方法中的中断信号?
- 在服务器应用程序中处理中断信号的最佳方式
- 如何在命令行界面中处理ctrl中断信号