通常像
write(2)
、read(2)
、close(2)
等系统调用会因被具有 errno
值 EINTR
的信号中断而失败(假设终端窗口的大小已更改并且收到了 SIGWINCH
) ,这是一个暂时性错误,应该重试,代码通常使用这些系统调用的包装器,在 EINTR
(通常是 EAGAIN
或 ENOBUFS
)上重试。
但是有可能陷入理论情况,即由于接收不间断信号,或者因为系统调用被仅返回
EINTR
的系统调用的自定义实现拦截,代码继续在 EINTR
上无限循环.
在这种情况下,在库代码中,重试系统调用多少次才有意义?
对于在 EINTR 上重试的系统调用的包装器,重试多少次才有意义?
从零到无穷多。
Glibc 标准库已在全球数十亿台设备中使用。调用
printf("Hello world\n");
最终会出现在 _IO_new_file_write
函数中,如下所示,来自 https://github.com/bminor/glibc/blob/5aa2f79691ca6a40a59dfd4a2d6f7baff6917eb7/libio/fileops.c#L1176 :
ssize_t
_IO_new_file_write (FILE *f, const void *data, ssize_t n)
{
ssize_t to_do = n;
while (to_do > 0)
{
ssize_t count = (__builtin_expect (f->_flags2
& _IO_FLAGS2_NOTCANCEL, 0)
? __write_nocancel (f->_fileno, data, to_do)
: __write (f->_fileno, data, to_do));
if (count < 0)
{
f->_flags |= _IO_ERR_SEEN;
break;
}
to_do -= count;
data = (void *) ((char *) data + count);
}
n -= to_do;
if (f->_offset >= 0)
f->_offset += n;
return n;
}
尽你所能,
while (to_do > 0)
该函数将无限次循环,直到数据被写入,忽略任何EINTR
信号,甚至不检查任何信号。
因为世界上几乎每一个 Linux 设备都使用这个软件,所以可以肯定地说无限多次循环是完全没问题的。
如果您愿意,可以循环 40 次或 100 次。没关系。