【问题标题】:LInux signals conceptsLLinux 信号概念
【发布时间】:2016-05-27 07:12:01
【问题描述】:

信号处理程序如何在不将参数传递给main() 中的signalhandler() 函数的情况下获取信号编号?

例如,在main() 内的以下源代码中,将信号系统调用的第二个参数传递为signalhandler,在信号处理程序中没有任何参数,但是在到达signalhandler 定义时,它正在收集一个名为sig_num 的参数。 ..

实际上这怎么可能?

根据 ANSI C,如果我们不传递任何参数,那么函数定义将不会收集任何参数。

请帮我解决这个问题。

#include<stdio.h>
#include<signal.h>

void signalhandler(int sig_num)
{
  printf("caught signal number: %d\n", sig_num);
}

int main(void)
{
  while(1)
  {
    printf("hello world\n");
    sleep(1);
    signal(SIGINT, signalhandler);
  }
}

【问题讨论】:

  • 你明白signal不是直接调用signalhandler吧?
  • 您能详细解释一下吗?
  • 让我换一种说法 - 你认为signal 做了什么?
  • 信号在生成相应信号时执行它的默认行为,例如,如果我按 ctrl+c 它将终止程序...并修改此操作,我正在编写一个信号处理程序..
  • 否:signal() 设置将在信号事件上调用的处理函数。该调用由内核进行,内核会将信号事件 ID(信号编号)传递给您使用 signal() 设置的处理函数。

标签: c signals


【解决方案1】:

当您将函数signalhandler 作为signal(2) 的参数时,它不会被调用。原型是:

typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);

所以你可以看到第二个参数是一个函数指针。您的函数 signalhandler 稍后会被调用(当信号被传递时),调用它的代码会给它一个 int 参数。

查看其他一些函数指针示例以更好地理解此回调机制。

【讨论】:

    【解决方案2】:

    signal API 注册提供的函数指针,以便在收到指定信号时调用。可以针对多个信号注册相同的函数指针,因此在调用函数时将提供接收到的信号值。

    在 Linux(以及我知道的所有操作系统)中,信号传递与进程的执行是异步的,并且通知是即时的。操作系统的行为就好像它抢占了程序并在程序当前正在执行的任何操作之上注​​入信号处理函数调用。操作系统知道传递了哪个信号,并将其作为参数传递给函数调用。

    注意: 可以使用raise 向程序生成同步信号。但是,它通常会使用 OS 服务将其传递给进程,而不是直接调用信号处理程序。

    如果您使用调试器并在信号处理程序中设置断点并传递适当的信号,您可能会看到回溯将显示信号处理程序正在由操作系统注入。

    例如,考虑程序:

    void signalhandler (int sig)
    {
        write(2, "signal!\n", 8);
    }
    
    void foo (void)
    {
        for (;;) {}
    }
    
    int main (void)
    {
        signal(SIGINT, signalhandler);
        foo();
    }
    

    在gdb 中运行程序时,可以使用signal 命令传递信号。生成的回溯如下所示:

    (gdb) signal SIGINT 
    Continuing with signal SIGINT.
    
    Breakpoint 1, signalhandler (sig=2) at s.c:5
    5       write(2, "signal!\n", 8);
    (gdb) bt
    #0  signalhandler (sig=2) at s.c:5
    #1  <signal handler called>
    #2  foo () at s.c:10
    #3  0x08048498 in main () at s.c:16
    

    注意:重要的是要认识到信号处理函数调用不应被视为常规函数调用。由于操作系统会注入调用,因此它具有下面讨论的限制。

    可以在代码执行中的任意位置注入信号调用。出于这个原因,POSIX 要求某些函数可以安全地从信号处理程序中调用。未设计为可重入的函数如果被对信号处理函数的注入调用中断,则存在处于不一致状态的风险,然后信号处理函数又调用被中断的函数。

    作为一个可能发生的问题的示例,假设您正在编写操作数据结构的代码,例如从链表中删除一个节点。但是,如果在传递信号时元素的指针尚未完全修复,则信号处理程序可能会看到损坏的链表。这种情况发生的频率可能比您想象的要高,尤其是在您调用需要堆分配的函数时。

    因此,使信号处理程序变得简单通常是最安全的。例如,信号处理程序可能只是设置一个标志,然后您的应用程序代码将需要代码来检测是否设置了标志。

    【讨论】:

      【解决方案3】:

      我会简单的解释一下。

      信号是向程序传递消息的方式。例如,当程序在终端窗口中运行并且您按Ctrl+C 时,终端窗口会向程序发送SIGINT。

      现在,对于每个进程,内核维护一个表,该表将每个信号映射到接收到信号时要调用的对象。默认情况下,在SIGINT 上,行为设置为终止。但是,程序可以通过调用函数 signal 来更改某些信号的默认行为。

      例如。 signal(SIGINT, funcHandler) 将接收SIGINT 的行为从终止更改为调用funcHandler。但它从不调用funcHandler。 funcHandler 将在程序收到 SIGINT 时调用。

      现在,当程序接收到SIGINT 时,内核会查找表来决定调用哪个函数(比如处理程序),然后内核将接收到的信号作为参数设置处理程序的堆栈,然后返回给程序.实际上funcHandler 是在程序上下文中使用signal_number 作为参数调用的。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2013-03-13
        • 2017-02-27
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-08-26
        • 1970-01-01
        相关资源
        最近更新 更多