检查 errno != EINTR:这是什么意思?

57

我发现这段代码被多次使用(还有一段类似的代码,只是使用了open()而不是write())。


int c = write(fd, &v, sizeof(v));
if (c == -1 && errno != EINTR) {
    perror("Write to output file");
    exit(EXIT_FAILURE);
}

为什么这里要检查 && errno != EINTR ?

在查找man页中的errno时,我找到了关于EINTR的以下文本,但即使我访问了man 7 signal也不清楚。

EINTR:被信号中断的函数调用(POSIX.1);请参阅signal(7)。


3
请查看write命令的手册页。手册页会告诉你在特定错误时,errno的值应该是多少。 - Kevin
6
请注意,write() 函数的返回值是 ssize_t 类型,而不是 int 类型。它们并不相同。 - Andrew Henle
1
谢谢你们两个!@AndrewHenle 我知道这一点,我只是决定复制代码片段,因为我发现它:) 你认为我应该编辑问题,用 ssize_t 替换 int 吗? - Robb1
3个回答

79
许多系统调用在进程调用进行中发生信号时会报告错误代码EINTR。实际上并没有错误发生,只是这样报告是因为系统无法自动恢复系统调用。这种编码模式会在出现此情况时简单地重试系统调用,以忽略中断。
例如,如果程序使用alarm()在计时器到期时异步运行某些代码,当程序调用write()时发生超时,我们只需要重新尝试系统调用(如read/write等)。

4
或许对于计算机发烧友有用,中断。 - Orkhan Alikhanov

11

这里的回答非常好,我想补充一些内部细节:

被信号中断的系统调用可能会中止并返回 EINTR ,或者在 sigaction(2) 中指定了 SA_RESTART 的情况下自动重启。

而负责此任务的是 restart_block,它用于跟踪重启系统调用的信息和参数。


2
您能告诉我这个引用是哪里来的吗?我想看一下。 :) - Rick
@Rick,我忘了,非常抱歉。但我认为这可能与x86和内核有关,因为当时我正在阅读很多关于如何工作的书籍,所以可能是《深入理解Linux内核》或《Linux编程接口》这本书。 - zerocool

1

write手册页面中得知:

在写入任何数据之前,调用被信号中断。


5
这个答案并不充分,它没有解释在什么情况下EINTR会发生(或更重要的是,不会发生)。我认为后者更重要,因为不理解通常不会发生会导致无休止的模仿。 - R.. GitHub STOP HELPING ICE
1
@R.. 这个答案并不充分;它没有解释EINTR可能(或更重要的是,不能)发生的情况。我认为后者更重要,因为如果不理解通常不能发生,那么就会导致无休止的“货物崇拜”。如果您不“货物崇拜”系统调用,则更改进程的信号掩码可能会在任何地方破坏事物。如果你喜欢编写可以被完全不相关的东西打断的脆弱代码,那是你的选择。但是,请不要将编写健壮代码描述为“货物崇拜”。 - Andrew Henle
2
@AndrewHenle:安装一个中断信号处理程序是一种具有侵入性和有意的操作,当你希望从被中断的阻塞系统调用中出错时,你会这样做。通常情况下,循环重复执行并不合理,只有当你确切知道你需要这种行为时才有意义。 - R.. GitHub STOP HELPING ICE
1
@R.. 你能解释一下(或者只是提供一个解释的链接),可能会导致这样的信号处理程序成为明智的选择的情况吗? - bmcorser
1
@Sahsahae:在这种情况下,您也不知道他们选择安装中断信号处理程序是否表示他们调用的代码(您的库代码)立即退出信号(中断信号的标准语义)的意图,还是希望您的代码将自己视为特殊并尝试在被中断后恢复阻塞的意图。由于您无法知道,为了满足他们想要后者的假设而做出特殊的事情似乎不是一个好主意。什么也不做就可以自动实现前者。 - R.. GitHub STOP HELPING ICE
显示剩余2条评论

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