【问题标题】:System calls and EINTR error code系统调用和 EINTR 错误代码
【发布时间】:2014-11-02 00:08:23
【问题描述】:

有没有专家可以帮助我解决以下问题?

我在 C 中有以下系统调用:

access()
unlink()
setsockopt()
fcntl()
setsid()
socket()
bind()
listen()

我想知道它们是否会失败并显示错误代码 -1 和 errno EINTR/EAGAIN。

我应该为这些处理 EINTR/EAGAIN 吗?

文档没有提到任何与 EINTR/EAGAIN 相关的内容,但我看到很多人处理它。

哪个是正确的?

这是我注册信号处理程序的方法:https://gitorious.org/zepto-web-server/zepto-web-server/source/b1b03b9ecccfe9646e34caf3eb04689e2bbc54dd:src/signal-dispatcher-utility.c

使用此配置:https://gitorious.org/zepto-web-server/zepto-web-server/source/b1b03b9ecccfe9646e34caf3eb04689e2bbc54dd:src/server-signals-support-utility.c

还有一个提交,我在一些我知道返回 EINTR 或 EAGAIN 的系统调用中添加了一些 EINTR/EAGAIN 处理:https://gitorious.org/zepto-web-server/zepto-web-server/commit/b1b03b9ecccfe9646e34caf3eb04689e2bbc54dd

【问题讨论】:

  • 根据平台、系统调用和 SA_RESTART 标志,系统调用可能会或可能不会因 EINTR 而失败。 (阅读 pep-0475)
  • 我已启用 SA_RESTART。但是,我已经读过明确地处理 EINTR 应该很好。
  • 还有其他想法吗?我应该为呼叫处理 EINTR 还是 SA_RESTART 就足够了?
  • EINTREAGAIN 表示不同的东西。我建议总是编写代码来处理EINTR。如果您在没有要求的情况下得到EAGAIN,则说明您的程序或内核中存在严重的逻辑错误。

标签: c posix interrupt system-calls eintr


【解决方案1】:

参见http://man7.org/linux/man-pages/man7/signal.7.html——开始阅读底部附近的内容,其中讨论“系统调用和库函数的中断......”这是一个 Linux 手册页,但该信息非常普遍适用于任何 Unix/Posix/ Linux 风格的系统。

【讨论】:

  • 我已经阅读了它,对于引用的系统调用,我已经处理了 EINTR。但是,只有这些是返回 EINTR 的系统调用吗?我不这么认为。许多人在他们的手册页没有引用它的其他系统调用中处理 EINTR。为什么要这样做?这是一种好的做法还是像魔术一样?
  • 还有其他想法吗?我应该为呼叫处理 EINTR 还是 SA_RESTART 就足够了?
  • 就个人而言,我通常会选择检查 EINTR 而不是设置 SA_RESTART,因为通常我想知道发生了什么事情。如果您看到对 EINTR 的检查没有返回它,那么我会说这是迷信或“某事”的文档是错误的(它确实返回了 EINTR)。
  • 是的,但是对于我知道它们不返回 EINTR 但人们处理 EINTR 的引用系统调用,我现在应该怎么做。是因为文献还是迷信?我不想开始看Linux内核中系统调用的源码...
  • 通常只有“慢”的系统调用返回 EINTR。缓慢的事情是终端 I/O 和等待的事情(选择、等待、睡眠、暂停等)。如果你看到有人在检查 EINTR,比如说,seteuid(),那么程序员就是疯了。
【解决方案2】:

除非您安装一个中断信号处理程序(一个安装了sigaction 省略了SA_RESTART 标志,或者在某些系统上安装了signal 函数),否则您不应该期望看到EINTR 全部

在您的特定功能列表中,除了fcntl 之外,我看不到任何可以体验EINTR 的功能,并且仅当它用于锁定时。不过,John 答案中的链接应该有助于回答有关特定功能的问题。

【讨论】:

  • 我已经安装了许多带有 sigaction 的信号处理程序,其中大多数使用 SA_RESTART。信号处理程序也不能被中断。在我读过的一些文章和线程中,我们还应该检查 EINTR,而不考虑 SA_RESTART。我正在使用 HANDLE_EINTR 和 HANDLE_EAGAIN 宏(从 Chrome 源代码中获取)。
  • 还有其他想法吗?我应该为呼叫处理 EINTR 还是 SA_RESTART 就足够了?
【解决方案3】:

在 *NIX 系统调用的每个手册页中都有一个名为 ERRORS 的部分。参考手册,例如:http://man7.org/linux/man-pages/man2/accept.2.html。也可以使用命令行man accept查看。

通常,可能需要一些时间来计算的系统调用可以在信号传递时设置 -1+EINTR,而短系统调用则不能。例如,accept() 可以阻止您的进程,以便它可以被信号中断,但 setsid() 太短以至于它被写入不会被信号中断。

【讨论】:

  • 我做的第一件事是从手册页和 Open Group 规范中获取信息。所有提到的系统调用都没有在其手册页中引用 EINTR 或 EAGAIN。但我认为其中一些甚至全部都可能返回 EINTR,因为它们可能使用内部模块或调用可能会因 EINTR 而失败并反向传播该错误。许多系统调用都引用了这样的东西。例如 fopen 指的是 errno 可能由 open 返回的手册页,在其中使用...仍在搜索。
  • 另外,系统调用'close'虽然它在手册页出错的情况下引用了EINTR,但处理它是错误的。通常我们会忽略它。这是来自 Android 和 Chrome 项目的 Google 人员所说的。
  • @EfstathiosChatzikyriakidis:如果标准允许,它会明确说明,如您的fopen 示例。请注意,某些函数明确地记录在文档中,但EINTR 不会失败(主要是大多数 POSIX 线程函数),所以也许您可以将其解读为暗示 EINTR 可能发生在其他未明确记录不受其影响的功能。
  • @EfstathiosChatzikyriakidis:关于close,您找到的建议只是“正确”作为解决 Linux 错误的方法。见ewontfix.com/4(披露:我的博客)。
  • 那么,您认为我不会处理来自所引用系统调用的 EINTR,因为手册页根本没有引用任何内容?只有套接字系统调用指的是:“底层协议模块可能会产生其他错误。”。此外,fcntl 在锁定的情况下返回 EINTR,但我不在乎。
【解决方案4】:

signal(7) 用于 Linux 列表

accept
connect
fcntl
flock
futex
ioctl
open
read
readv
recv
recvfrom
recvmsg
send
sendmsg
sendto
wait
wait3
wait4
waitid
waitpid
write
writev

可能被 no-SA_RESTART 处理程序中断 (EINTR) 和

setsockopt
accept
recv
recvfrom
recvmsg
connect
send
sendto
sendmsg
pause
sigsuspend
sigtimedwait
sigwaitinfo
epoll_wait
epoll_pwait
poll
ppoll
select
lect
msgrcv
msgsnd
semop
semtimedop
clock_nanosleep
nanosleep
read
io_getevents
sleep

作为 EINTR 可中断的,即使是 SA_RESTART 处理程序。

此外,它还列出:

setsockopt
accept
recv
recvfrom
recvmsg
connect
send
sendto
sendmsg
epoll_wait
epoll_pwait
semop
semtimedop
sigtimedwait
sigwaitinfo
read
futex
msgrcv
msgsnd
nanosleep

作为 EINTR 可被停止信号 + SIGCONT 中断,并表示此特定行为是 Linux 特定的,不受 POSIX.1 的认可。

除了这些,特别是如果函数的规范没有列出EINTR,你不应该得到EINTR

如果您不相信系统会兑现它,您可以尝试使用带有no-SA_RESTART 无操作处理程序的SIGSTOP/SIGCONT+a 信号用您怀疑的系统功能轰炸循环,看看您是否可以引出一个 EINTR。

我试过了:

#include <assert.h>
#include <errno.h>
#include <fcntl.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>

static void chld(int Sig)
{
    int status;
    if(0>wait(&status))
        _exit(1);
    if(!WIFEXITED(status)){ 
        //this can only interrupt an AS-safe block
        assert(WIFSIGNALED(status) && WTERMSIG(status) == SIGALRM);
        puts("OK");
        exit(0);
    } else {
        switch(WEXITSTATUS(status)){
        case 1: puts("FAIL"); break;
        case 2: puts("EINTR"); break;
        }
    }
    exit(0);
}
static void nop(int Sig)
{
}

int main()
{
    sigset_t full;
    sigfillset(&full);
    sigaction(SIGCHLD, &(struct sigaction){ .sa_handler=chld, .sa_mask=full, .sa_flags=0 } , 0); 
    sigaction(SIGUSR1, &(struct sigaction){ .sa_handler=nop, .sa_mask=full, .sa_flags=0 } , 0); 

    pid_t p;
    if(0>(p=fork())) { perror(0); return 1; }
    if(p!=0){
        //bombard it with SIGSTOP/SIGCONT/SIGUSR1
        for(;;){
            usleep(1); kill(p, SIGSTOP); kill(p, SIGCONT); kill(p, SIGUSR1);
        }
    }else{
        sigaction(SIGCHLD, &(struct sigaction){ .sa_handler=SIG_DFL }, 0);
        if(0>alarm(1))
            return 1;
        for(;;){

    #if 1
            /*not interruptible*/
            if(0>access("/dev/null", R_OK)){

                if(errno==EINTR)
                    return 2;
                perror(0);
                return 1;
            }

    #else
            int fd;
            unlink("fifo");
            if(0>mkfifo("fifo",0600))
                return 1;

            /*interruptible*/
            if(0>(fd=open("fifo", O_RDONLY|O_CREAT, 0600))){
                if(errno==EINTR)
                    return 2;
                perror(0);
                return 1;
            }
            close(fd);
    #endif

        }
    }
    return 0;
}

unlinkaccess 绝对看起来是EINTR-uninterruptible(符合他们的规范),这意味着围绕它们的EINTR-retry 循环将是不必要的。

【讨论】:

    猜你喜欢
    • 2014-11-01
    • 1970-01-01
    • 2021-01-09
    • 1970-01-01
    • 1970-01-01
    • 2013-02-09
    • 1970-01-01
    • 2022-08-17
    • 2015-04-29
    相关资源
    最近更新 更多