我的用户空间应用程序有时会在收到EINTR信号后以某种方式阻塞。
我用strace录制的内容:
time(NULL) = 1257343042
time(NULL) = 1257343042
rt_sigreturn(0xbff07be4) = -1 EINTR (Interrupted system call)
--- SIGALRM (Alarm clock) @ 0 (0) ---
time(NULL) = 1257343042
futex(0xb7cea80c, 0x80 /* FUTEX_??? */, 2) = ? ERESTARTSYS (To be restarted)
--- SIGUSR1 (User defined signal 1) @ 0 (0) ---
sigreturn() = ? (mask now [ALRM])
futex(0xb7cea80c, 0x80 /* FUTEX_??? */, 2) = ? ERESTARTSYS (To be restarted)
--- SIGWINCH (Window changed) @ 0 (0) ---
futex(0xb7cea80c, 0x80 /* FUTEX_??? */, 2) = ? ERESTARTSYS (To be restarted)
--- SIGTERM (Terminated) @ 0 (0) ---
time(NULL) = 1257343443
time(NULL) = 1257343443
futex(0xb7cea80c, 0x80 /* FUTEX_??? */, 2) = ? ERESTARTSYS (To be restarted)
--- SIGWINCH (Window changed) @ 0 (0) ---
futex(0xb7cea80c, 0x80 /* FUTEX_??? */, 2
我可以捕获EINTR信号吗?如何重复写入,读取或选择等相关调用?即使我使用第三方库处理系统调用,如何确定发生此EINTR的位置?
为什么我的应用程序在收到EINTR后被完全阻止(请参阅strace转储:我发送了一个通常应该处理的SIGUSR1)?为什么futex()将ERESTARTSYS返回给用户空间?
感谢
答案 0 :(得分:17)
调用write(或其他阻塞操作)的代码必须知道EINTR。如果在阻塞操作期间发生信号,则操作将(a)返回部分完成,或(b)返回失败,什么也不做,并将errno设置为EINTR。
因此,对于在中断后重试的全失败写操作,您可以执行以下操作:
while(size > 0) {
int written = write(filedes, buf, size);
if (written == -1) {
if (errno == EINTR) continue;
return -1;
}
buf += written;
size -= written;
}
return 0; // success
或者对于更好的行为,重试EINTR,尽可能多地写入,并报告失败时写入多少(因此调用者可以决定是否以及如何继续部分写入因为除了以外的原因而失败信号中断):
int total = 0;
while(size > 0) {
int written = write(filedes, buf, size);
if (written == -1) {
if (errno == EINTR) continue;
return (total == 0) ? -1 : total;
}
buf += written;
total += written;
size -= written;
}
return total; // bytes written
GNU有一个非标准的TEMP_FAILURE_RETRY宏,可能会引起我的兴趣,但是当我想要它时,我永远找不到它的文档。包括现在。
答案 1 :(得分:2)
另见“失败者模式”的讨论 Worse is Better