【问题标题】:What does "wait and waitpid are always interrupted when a signal is caught" mean?“当信号被捕获时,wait 和 waitpid 总是被中断”是什么意思?
【发布时间】:2019-08-20 02:17:49
【问题描述】:

来自APUE

防止应用程序不得不处理中断的系统 调用,4.2BSD引入了某些自动重启 中断的系统调用。自动进行的系统调用 重新启动的是ioctlreadreadvwritewritevwaitwaitpid。作为 我们已经提到,前五个函数被 仅当它们在慢速设备上运行时才发出信号; waitwaitpid 当信号被捕获时总是被中断。因为这会导致 一些不想重新启动操作的应用程序的问题 如果它被中断,4.3BSD 允许进程禁用它 基于每个信号的功能。

是不是说在引入自动重启之前,如果进程捕捉到信号,waitwaitpid会立即停止等待并执行后续代码?

例如:

#include <unistd.h>
#include <sys/types.h>
#include <signal.h>

void handler(int sig){}
void handler2(int sig){}


int main(){
    pid_t pid;
    int status;
    signal(SIGUSR1, handler);
    signal(SIGUSR2, handler2);    

    if((pid == fork()) < 0){
        printf("fork error\n");
    }else{
        if(pid){
            //child
            //do something, needs several hours.
        }else{
            //parent
            waitpid(pid, &status, 0);
            printf("Hello world\n");
        }
    }
    return 0;
}

如果不提供自动重启,当我在后台运行这个程序gcc test.c &amp;&amp; ./a.out &amp;,然后我发送一个信号kill -SIGUSR1 pidkill -SIGUSR2 pidwaitpid 会返回并且后面的代码waitpid(pid, &amp;status, 0); 会执行。

如果提供自动重启waitpid 将再次执行,而父级将继续等待。

我的理解正确吗?

【问题讨论】:

  • 中断的系统调用将返回 -1(或任何用于指示错误的值)并将 errno 设置为 EINTR。接下来发生的事情取决于调用者如何处理这种情况。如果它是可重新启动的,它不会返回那个特定的 errno 代码,除非另有说明这样做。它会继续运行,直到由于其他原因返回为止。
  • 请注意,您应该使用sigaction() 而不是signal() 来获得对此的控制权,并且sigaction() 提供了一个控制标志SA_RESTART 来指定是否应在收到信号后恢复系统调用处理。
  • @JonathanLeffler 但是,signal() 默认情况下确实现在在 linux 和 bsd 上应用重启行为(至少)

标签: c unix signals wait interrupt


【解决方案1】:

signal()(System-V 语义)的原始行为是,如果进程当前处于休眠状态,则中断任何系统调用,执行信号处理程序并且系统调用返回 -EINTR。然后,BSD4.3发明了restart机制,任何系统调用被中断后会自动重启。如果涉及信号处理程序,这避免了必须为每个系统调用编写一个循环。

Linux 没有改变signal() 系统调用 的语义。然而,signal() glibc 包装函数 现在默认调用带有SA_RESTART 标志的系统调用sigaction()。因此,如果您确实不需要需要重新启动行为,则必须调用 sigaction() 并省略该标志。

所以,您的代码确实在 BSD 和 linux 上都使用了 restart 机制

【讨论】:

  • 嗨@Ctx,我又读了一遍这本书,突然想到一个问题。 1.似乎只有那些所谓的“慢速系统调用”才能被中断。 2.在所有“慢速系统调用”中,并不是所有的都可以自动重启。我对这两个陈述是否正确?
  • 如果这两个语句是正确的,我怎么知道哪个系统调用是“慢速系统调用”之一?在那些“慢速系统调用”中,我怎么知道哪些可以自动重启?手册页是否特别包含此信息?
  • @Rick 慢速系统调用都是可以无限期休眠的系统调用。如果系统调用记录了EINTR,它可以被中断。据我所知,所有慢速系统调用都可以重新启动。
  • 我正在使用 Linux 顺便说一句。 :)
  • 好的,我明白了。好的。只需检查 EINTR 是否已记录在案。我会记住这一点。最后一个问题???:我不明白为什么我问confusion-about-a-implementation-of-the-system-function-in-unix的上一个问题需要重试循环代码。在接受的答案下查看我刚刚制作的 cmets。
【解决方案2】:

waitwaitpid 与任何其他阻塞函数一样,可以通过将 errno 设置为 EINTR 来中断 - 这正是因为信号处理程序可以做的事情很少 - 主要设置一些标志。现在,如果一个阻塞函数返回EINTR,你怎么能以任何方式对信号做出反应?!

但这也意味着您需要对每个函数都有一个复杂的循环 - 您可能有 一些 信号,您知道您不希望系统调用被中断,因此您可以设置此信号自动重启。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-06-06
    • 2019-04-07
    • 2010-10-01
    • 1970-01-01
    • 2023-04-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多