【问题标题】:Waiting for a signal inside the signal handler itself等待信号处理程序本身内部的信号
【发布时间】:2019-07-31 20:24:10
【问题描述】:

我正在尝试编写一个程序,该程序涉及向进程发送信号以通知它暂停一段时间并在收到另一个信号后重新开始工作。我写了一个这样的信号处理程序:

void sig_handler(int alrm)
{
    if(sig_rcv==0)
    {
        printf("Signal received..sig_rcv value: %d\n",sig_rcv);
        sig_rcv = (sig_rcv+1)%2;
        printf("After increment sig_rcv value: %d\n",sig_rcv);
        signal(SIGUSR1,sig_handler);
        if(pause()<0)
        {
            printf("Signal received again..\n");
        }
    }
    else
    {
        sig_rcv = (sig_rcv+1)%2;
        printf("Signal received..sig_rcv value: %d\n",sig_rcv);
        signal(SIGUSR1,sig_handler);
    }
    printf("Exiting..\n");
}

这里我维护一个全局变量 sig_rcv,它最初为 0,如果在它为零时接收到信号,它将进入 if 条件并暂停另一个信号。另一方面,如果它在收到信号时sig_rcv 是 1,它只会改变那个变量的值。 我以这种方式编写信号处理程序的目的是将相同的信号用于两个不同的目的。我使用了这个系统调用:

signal(SIGUSR1,sig_handler);

现在,当我将 SIGUSR1 发送到它正在执行的进程时,直到 pause()statement。但是在第二次发送 SIGUSR1 后,它没有任何效果。它只是在暂停语句处冻结。在我使用另一个信号来移除暂停状态后,这个问题得到了解决,例如:

void sig_handler2(int a)
{
    sig_rcv = (sig_rcv+1)%2;
    printf("Restarting reading...\n");
}

signal(SIGUSR2,sig_handler2);

在这种情况下效果很好。 所以,我的问题是,为什么会发生这种现象?我们不能在执行为同一信号编写的信号处理程序时等待一个信号吗?如果是这样,原因是什么?有没有什么方法可以在不使用 SIGUSR2 的情况下达到同样的效果?

【问题讨论】:

  • 这只是一个小细节,但仍然:虽然sig_rcv = (sig_rcv+1)%2 确实完成了工作,但同样可以通过sig_rcv = 1-sig_rcv 甚至sig_rcv ^= 1 更优雅地实现。关于实际问题,通常的做法是让程序的“主循环”看起来像while (running) ...(其中running 是一个全局变量),并让信号处理程序简单地更改running。这样您就可以在单个信号处理程序中检查 running 的值,而不是对两个不同的信号使用两个不同的处理程序。
  • 您使用的是什么操作系统?
  • @MarkPlotnick Ubuntu 18.04

标签: c unix signals posix ipc


【解决方案1】:

我们不能在执行为同一信号编写的信号处理程序时等待一个信号吗?

代码是“永远”等待还是在处理它时再次接收到相同的信号,取决于 (C/OS) 实现。

来自POSIX documentation

在信号的产生和它的交付或接受之间的这段时间内,该信号被称为“未决”。

如果生成了后续出现的未决信号,则由实现定义是否多次传递或接受信号[...]


顺便说一句,calling printf() & friends from a signal handler is unsafe

【讨论】:

    【解决方案2】:

    比@alk 的回答更普遍的是,您不能在严格符合 C 代码的信号处理程序中调用任何库函数。

    7.1.4 Use of library functions, paragraph 4 of the C standard:

    标准库中的函数不保证是可重入的,可能会修改具有静态或线程存储持续时间的对象。188

    注意footnote 188,就该段而言,声明:

    因此,信号处理程序通常不能调用标准库函数。

    正如@alk 所说,POSIX 只允许您从信号处理程序中安全地调用 async-signal-safe 函数。

    没有针对您的平台的特定文档说明从信号处理程序中进行特定函数调用是安全的,您不能从信号处理程序中调用函数。

    【讨论】:

      【解决方案3】:

      由于您使用的是signal 而不是sigaction,因此请务必注意the POSIX standard 中的以下内容:

      当一个信号出现时,func指向一个函数,实现定义是否等价于a:

      signal(sig, SIG_DFL);

      被执行或实现阻止某些实现定义的信号集(至少包括sig)在当前信号处理完成之前发生。

      这反映了两种历史上的信号实现方式,SVID 和 BSD。 由于您使用的是 Ubuntu 18.04,因此您很可能使用 glibc,它实现了后者 (BSD) 语义。 SIGUSR1 在其处理程序执行时被屏蔽。

      由于您需要 SVID 语义,其中没有信号被屏蔽,并且您需要在每次调用信号处理程序时重新建立信号处理程序,您应该将您的 signal(SIGUSR1, sig_handler); 调用替换为以下内容:

      struct sigaction sa = { .sa_handler = sig_handler, .sa_flags = SA_NODEFER|SA_RESETHAND };
      sigemptyset(&sa.mask);
      sigaction(SIGUSR1, &sa, NULL);
      

      SA_NODEFER 标志与空掩码一起意味着不会屏蔽任何信号; SA_RESETHAND 表示信号动作将被重置为SIG_DFL

      此外,正如其他答案所说,您不应该从信号处理程序中调用printf。 Linux signal safety 手册页说明了可以调用哪些函数。 sigactionsignalpause 都可以。您可以使用write 代替printf 来编写字符串。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2019-08-04
        • 2023-03-28
        • 2013-06-19
        • 1970-01-01
        • 1970-01-01
        • 2018-03-19
        相关资源
        最近更新 更多