何时检查EINTR并重复函数调用?

44

我正在为一个嵌入式Linux系统编写用户应用程序,使用了诸如open、close、read、ioctl等设备的常见函数。现在,我读到了关于EINTR的内容,它表示该函数被信号中断。但是我不确定这会产生什么影响。在我所有的示例程序中,有时候会这样做,例如ioctl(),有时候不会这样做,例如read()。所以,我有点困惑。

在什么情况下最好检查EINTR并重复函数调用?


为什么要在中断处理完成后再次调用函数?当中断处理完成后,控制权将交回给您的应用程序/函数。 - knittl
类似问题:https://dev59.com/BFDTa4cB1Zd3GeqPHTu0 - stefangachter
4个回答

17

参见sigaction:http://pubs.opengroup.org/onlinepubs/009695399/functions/sigaction.html

SA_RESTART
  This flag affects the behavior of interruptible functions; that is, those 
  specified to fail with errno set to EINTR. If set, and a function specified 
  as interruptible is interrupted by this signal, the function shall restart 
  and shall not fail with EINTR unless otherwise specified. If the flag is not 
  set, interruptible functions interrupted by this signal shall fail with errno 
  set to EINTR.

默认情况下,您具有SA_RESTART行为,因此如果您不处理信号,就不必担心EINTR问题。


1
那么,只要我在用户应用程序中不使用信号,我就不必担心EINTR了吗?但是例如read()是一个系统调用,因此read()可能会被信号中断,这是有可能的吗? - stefangachter
3
经过更多阅读,我得出结论需要检查被打断的系统调用,因为这些调用没有被SA_RESTART标志考虑在内。 - stefangachter
6
正如你所得出的结论,需要注意的是 SA_RESTART 并不总是适用的。有些接口被信号处理程序中断后永远不会重新启动,无论是否使用了 SA_RESTART;它们在被信号处理程序中断时总是会失败并返回 EINTR 错误。请查看信号手册页面以获取详细信息。 - kikeenrique
4
@stefangachter,有时候确实需要。在Linux中,即使没有信号处理程序,某些阻塞接口在进程被某个停止信号停止后,并通过SIGCONT恢复运行,仍可能出现错误EINTR。这种行为不符合POSIX.1标准,在其他系统上也不会发生。正如kikeenrique指出的那样,请参阅signal(7)以获取明确的针对Linux的信息。 - tne
@NiklasPeter 库 不得 在未经通知的情况下设置信号处理程序,否则很快就会出现故障。库应该与其链接的程序以及任何其他库进行合作。这通常与信号的使用不兼容。 - Yann Droneaud
显示剩余5条评论

4
我知道这个问题很老,但我认为还有更多需要说的。回答标题中具体的问题:基本上永远不会
除了selectpoll之外,EINTR只有在您安装(无论是出于错误/不理解如何使用sigaction,还是因为您希望能够中断阻塞操作)中断信号处理程序时才会发生。当函数失败并出现EINTR时重试循环只会撤消它。这是80年代和90年代初至中期许多操作系统存在缺陷且据称在非符合情况下产生EINTR的旧反模式,但这些错误已经不存在了。
即使在一个希望通过信号中断事物的应用程序中,也可能存在一些代码成功完成非常重要,以至于您不能将EINTR视为错误条件并在未完成操作的情况下返回到调用者。在这种情况下,安装重试循环可能是有意义的,但最好在操作期间屏蔽信号(至少是潜在中断的信号),并在完成后取消屏蔽。

1
@Max:对于大多数函数而言,EINTR 仅在“该函数被信号中断”或类似情况下指定,其中 interrupted 的定义仅适用于非 SA_RESTART 信号处理程序。然而,对于 poll 函数而言,它被定义为“在 poll() 过程中捕获到一个信号”,无论该信号是否是中断信号。 - R.. GitHub STOP HELPING ICE
1
@mosvy:那么你要么不使用有缺陷的库,要么报告错误并修复它,并仅支持修复后的版本,或者绕过它(例如,在库设置错误后修复它,或提供一个包装的sigaction,忽略有缺陷的库设置的恶意标志)。注意:库本来就不应该设置信号处理程序,而应该允许调用者如果需要的话自行设置。 - R.. GitHub STOP HELPING ICE
理论上,即使您没有设置任何信号处理程序,EINTR也可能发生。因此,为了安全起见,检查EINTR是一个好习惯。 - Heil Programmierung
@HeilProgrammierung:你是按照哪个理论来做的?规范中没有允许为除了它们所指定的条件之外的其他条件生成标准错误代码(已经为接口分配了含义),除非规范明确说明“或其他实现定义的条件”。 - R.. GitHub STOP HELPING ICE
@HeilProgrammierung:它分散在多个地方。主要是2.3 ¶7,包含“对进程没有影响”的2.4.3条款,以及阻塞并可能失败于EINTR的各个函数的规范(其错误标题下的规范文本)。https://pubs.opengroup.org/onlinepubs/9699919799/functions/V2_chap02.html - R.. GitHub STOP HELPING ICE
显示剩余8条评论

4

你的应用程序是否是事件驱动的?(意思是其主循环包括select()/epoll_wait()调用)。

在事件驱动的应用程序中,你可以阻塞所有信号,并仅在pselect()/epoll_pwait()调用的持续时间内取消阻塞。这样,你的其余代码就永远不必处理EINTR。


2

当使用read()函数等待命名管道的输入时,我遇到了类似的问题。

GNU libc文档中,我找到了有关原语的解释和有用的宏:TEMP_FAILURE_RETRY。

示例:

TEMP_FAILURE_RETRY (read_return = read((int)example_fifo, buffer, (size_t)n));
if (read_return==-1){
    fprintf(stderr, "reader.c: read_fifo: read(): %s \n", strerror(errno));
    fflush(stderr);
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接