【问题标题】:When to check for EINTR and repeat the function call?何时检查 EINTR 并重复函数调用?
【发布时间】:2011-02-10 16:06:30
【问题描述】:

我正在为嵌入式 Linux 系统编写用户应用程序,并且我正在为设备使用诸如打开、关闭、读取、ioctl 等常用功能。现在,我阅读了有关 EINTR 的信息,表明该函数被信号中断,但我不确定其含义。在我拥有的所有示例程序中,有时它已经完成,例如ioctl(),有时它没有完成,例如读()。所以,我有点困惑。

我最好什么时候检查 EINTR 并重复函数调用?

【问题讨论】:

标签: linux eintr


【解决方案1】:

查看签名: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。

【讨论】:

  • 那么,只要我不在我的用户应用程序中使用信号,我就不必担心 EINTR?但是例如read() 是一个系统调用,所以 read() 有没有可能被信号中断?
  • 在花了更多时间阅读之后,我得出的结论是我必须检查中断的系统调用,SA_RESTART 标志没有考虑到这些。
  • 毕竟,这仅在信号处理程序内部很重要。只要我没有信号处理程序,我就不管了。
  • @stefangachter 正如您总结的那样,重要的是要注意 SA_RESTART 并不总是适用。某些接口在被信号处理程序中断后永远不会重新启动,无论是否使用 SA_RESTART;当被信号处理程序中断时,它们总是以错误 EINTR 失败。检查Signal man page for details
  • @stefangachter 实际上有时你会这样做。 “在 Linux 上,即使在没有信号处理程序的情况下,某些阻塞接口也会在进程被其中一个停止信号停止然后通过 SIGCONT 恢复后失败并出现错误 EINTR。POSIX.1 不认可这种行为,并且不会在其他系统上发生。” 正如 kikeenrique 所指出的,请参阅signal(7) 以获取特定于 Linux 的明确信息。
【解决方案2】:

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

在事件驱动的应用程序中,您可以阻止所有信号,并且仅在 pselect()/epoll_pwait() 调用期间解除阻止它们。这样,您的其余代码就不必处理 EINTR。

【讨论】:

    【解决方案3】:

    我知道这个问题很老,但我认为还有更多要说的。要回答标题中的具体问题:基本上永远不会

    除了selectpoll 之外,EINTR 只有在您安装后才会发生(可能是错误/不了解如何使用sigaction,或者是因为您想要能够中断阻塞操作)中断信号处理程序。当函数以EINTR 失败时循环重试只会撤消该操作。这样做是 80 年代和 90 年代初到中期的一种旧反模式,当时许多操作系统都有错误,据报道在不符合要求的情况下会生成 EINTR,但这些错误早已不复存在。

    有可能,即使在您希望能够用信号中断事物的应用程序中,有些代码的成功完成非常重要,以至于您不能将 EINTR 视为错误条件并返回在未完成操作的情况下发送给调用者。在这种情况下,安装重试循环可能是有意义的,但最好在操作期间屏蔽信号(至少是可能中断的信号)并在完成后取消屏蔽。

    【讨论】:

    • 是什么让selectpoll 在这里特别?我在其他 libc 调用中看到 EINTR,即 waitpid
    • @Max: 对于大多数函数,EINTR 仅用于“函数被信号中断”或类似的情况,其中 interrupted 仅定义为非-SA_RESTART 信号处理程序。然而,对于poll,它被定义为“在 poll() 期间捕获了一个信号”,与该信号是否是一个中断信号无关。
    • 如果你使用像 ncurses 这样的库,它会与信号混淆(即它设置一个没有 SA_RESTART 的 SIGWINCH 处理程序)怎么办?
    • @mosvy:然后你要么不使用有缺陷的库,要么报告错误并修复它并且只支持固定版本,或者你绕过它(例如,在库设置后修复它它错了,或者给它一个包装好的sigaction,它忽略了错误库集的恶意标志)。注意:库不应该在开始时设置信号处理程序,但应该允许调用者设置它们如果它愿意
    【解决方案4】:

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

    我在GNU libc documentation 中找到了原语的解释和有用的宏: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);
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-12-30
      • 2021-12-11
      • 1970-01-01
      • 1970-01-01
      • 2022-01-01
      • 2019-03-12
      • 2020-04-25
      • 1970-01-01
      相关资源
      最近更新 更多