C++ 如何处理 EINTR(中断的系统调用)

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1674162/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-27 20:52:37  来源:igfitidea点击:

How to handle EINTR (interrupted System Call)

c++clinux

提问by Maus

My user-space application sometimes blocks after receiving an EINTR-Signal, somehow.

我的用户空间应用程序有时会在收到 EINTR 信号后以某种方式阻塞。

What I recorded with strace:

我用 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

Can I catch the EINTR signal and how can I repeat concerned calls such as write, read or select? How can I determine WHERE this EINTR occurred, even if I used third-party libraries working with system calls?

我能捕捉到 EINTR 信号吗?我怎样才能重复有关的调用,例如写、读或选择?即使我使用了处理系统调用的第三方库,我如何确定这个 EINTR 发生在哪里?

Why my app is completely blocked after receiving an EINTR (see strace dump: I sent a SIGUSR1 which normally should be handled)? And why is futex() returning ERESTARTSYS to user space?

为什么我的应用程序在收到 EINTR 后被完全阻止(请参阅 strace dump: I sent a SIGUSR1 which 通常应该被处理)?为什么 futex() 将 ERESTARTSYS 返回到用户空间?

thanks

谢谢

回答by Steve Jessop

The code which calls write (or other blocking operations) has to be aware of EINTR. If a signal occurs during a blocking operation, then the operation will either (a) return partial completion, or (b) return failure, do nothing, and set errno to EINTR.

调用 write(或其他阻塞操作)的代码必须知道 EINTR。如果在阻塞操作期间出现信号,则该操作将 (a) 返回部分完成,或 (b) 返回失败,什么都不做,并将 errno 设置为 EINTR。

So, for an all-or-fail write operation which retries after interruptions, you'd do something like this:

因此,对于在中断后重试的全部或失败写入操作,您可以执行以下操作:

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

Or for something a bit better behaved, which retries EINTR, writes as much as it can, and reports how much is written on failure (so the caller can decide whether and how to continue partial writes which fail for a reason other than interruption by signal):

或者对于一些表现更好的东西,它重试 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 has a non-standard TEMP_FAILURE_RETRY macro that might be of interest, although I can never find the docs for it when I want them. Including now.

GNU 有一个非标准的 TEMP_FAILURE_RETRY 宏可能会引起人们的兴趣,尽管我在需要时永远找不到它的文档。包括现在。

回答by Tim Schaeffer

See also the discussion of "loser mode" in Worse is Better

另请参阅Worse is Better 中关于“失败者模式”的讨论