c - 在此信号处理程序中会发生什么?

标签 c linux signals

void main ( )
{   int x;
    signal (SIGUSR1, f);
    x= fork ( );
    if (x == -1) exit (1);
    if (x != 0) 
    {   kill (x, SIGUSR1) ;
        sleep (2);
        exit (0);
    }
}
void f ( )
{
    printf ("signal received");
    exit (0);
}

我认为上面的程序要求系统在父进程接收到SIGUSR1信号时启动f函数(显示“收到信号”)。但我不确定,请随时进行更正或提供更多详细信息。感谢您的帮助!

最佳答案

您的代码中有一些错误:

  • 避免在信号处理程序中调用printf( )函数。 SIGNAL(7) 手册提供了授权函数的列表,这些函数在信号处理程序内部是安全的。读:

    Async-signal-safe functions

    A signal handler function must be very careful, since processing elsewhere may be interrupted at some arbitrary point in the execution of the program. POSIX has the concept of "safe function". If a signal interrupts the execution of an unsafe function, and handler calls an unsafe function, then the behavior of the program is undefined.

  • 使用main()的返回类型int;阅读"What should main() return in C?"
  • x应该是pid_t。 (Process Identification)。

  • 现在,假设您的程序已编译并运行(在执行处理程序时不会被任何其他信号中断):
    我只是缩进代码,并在main之前移f()函数定义,因为缺少函数声明,还添加了一些应该阅读的注释:



    #include <stdlib.h>
    #include <sys/types.h>
    #include <unistd.h>
    #include <signal.h>
    void f( )
    {
        printf ("signal received\n");
        exit (0);//if child receives signal then child exits from here
    }            // ******* mostly happens
    int main ( )
    {   int x;
        signal (SIGUSR1, f);// First: handler registered
        x = fork ( );       // Second: Child created, now child will execute in
        if (x == -1)        // parallel with parent 
          exit (1);
        if (x != 0) {// parent           
            kill(x, SIGUSR1) ; // Third: sends signal to child
            sleep(2);          // Forth: parent sleep
        }
        return 0; // parent always exits  
        // Child can exits from here **** 
        // if signal sent from parent delayed
    }
    

    main()函数中,为f()信号注册SIGUSR1函数,然后调用fork()创建一个新进程。在运行时作为fork()函数返回一个子进程,该进程开始与父进程并行执行。
    正如我看到的代码所示,我认为您理解子进程是父进程的副本,只是变量的值可能与fork()返回的点不同,因此子进程和父进程的x是不同的。我们可以使用fork的返回值来判断程序是在父进程中运行还是在子进程中运行。但是请注意,接收信号SIGUSR1的不是父进程,而是子进程。对于任何进程,其自身进程ID的值始终为0。您检查返回值x = fork(),它是新创建的子进程的pid,在x的子进程值是0,在父x != 0中。因此,信号从父进程发送到子进程。

    你的评论:

    I think that the program above asks the system to launch the f( ) function ( which displays "signal received") when the SIGUSR1 signal is received by the parent process.



    我给您的印象是,您不认为这两个进程会同时执行,并且“有可能在fork()创建子进程之后不久,子进程开始执行并立即终止,然后父进程可以向子进程发送信号(或子进程可以接收信号)”。在这种情况下,函数f()将永远不会执行,并且信号处理程序中的printf不会打印。

    但是我上面所描述的可能性很小,因为fork需要花费时间来创建一个新的过程。即使您一次又一次地执行代码,大多数时候从父进程发送的信号也会执行信号处理程序。

    编写此代码的正确方法是什么?

    代码是 xc :正确的方法是设置一个标志,该标志指示信号处理程序已执行,然后如我在答案中所述在信号处理程序之外的标志值的基础上调用printf函数:How to avoid using printf in a signal handler?及其背后的原因由Jonathan Leffler解释为他的answer



    #define _POSIX_SOURCE 
    #include <stdio.h>
    #include <stdlib.h>
    #include <sys/types.h>
    #include <unistd.h>
    #include <sys/wait.h>
    #include <signal.h>
    volatile sig_atomic_t flag = 0; //initially flag == 0
    void f(){
        flag = 1; // flag is one if handler executes
    }
    int main(void){   
      pid_t x;
      (void) signal (SIGUSR1, f);
      x = fork();
      if(x == -1) 
        exit(EXIT_FAILURE);
      if (x != 0){// parent 
        kill(x, SIGUSR1);
        sleep(1);
      }
      if(flag)//print only if signal caught and flag == 1
        printf("%s signal received \n", x == 0 ? "Child:" : "Parent:");
      return EXIT_SUCCESS;
    }
    

    现在编译并执行:

    @:~$ gcc -Wall  -pedantic -std=c99  x.c  -o x
    @:~$ ./x
    Child: signal received 
    @:~$ 
    

    注意子进程的打印是因为父进程向子进程发送信号(但是父进程不会打印,因为父进程没有捕获信号)。因此,上述代码的行为仍然与您获取代码时的行为类似。在下面,我添加了另一个示例,其中我试图证明“进程的并行执行在不同的执行实例上导致不同的结果”(请阅读注释)。

    // include header files...
    volatile sig_atomic_t flag = 0;
    void f(){
        flag = 1;
    }
    int main(void){   
      pid_t x;
      (void) signal (SIGUSR1, f);
      (void) signal (SIGUSR2, f); // added one more signal
      x= fork ( );
      if(x == -1) 
        exit(EXIT_FAILURE);
      if (x != 0){// parent 
        kill(x, SIGUSR1);
        while(!flag); // in loop until flag == 0
      }
      if (x == 0){//child 
        kill(getppid(), SIGUSR2); // send signal to parent 
        while(!flag); // in loop until flag == 0
      }//  ^^^^ loop terminates just after signal-handler sets `flag` 
      if(flag)
        printf("%s signal received \n", x == 0 ? "Child:" : "Parent:"); 
      return EXIT_SUCCESS;
    }
    

    在上面的代码中,在父进程和子进程中都注册了两个信号。父进程不会休眠,而是在一个while循环中忙碌,直到信号置位标志为止。同样,子进程具有一个循环,该循环在信号处理程序中的标志变为1时中断。现在编译此代码并重复运行。我经常尝试在系统中进行以下跟踪输出。

    @:~$ gcc -Wall  -pedantic -std=c99  x.c  -o x
    @:~$ ./x
    Child: signal received 
    Parent: signal received 
    @:~$ ./x
    Child: signal received 
    Parent: signal received 
    @:~$ ./x
    Child: signal received  
    Parent: signal received 
    @:~$ ./x
    Parent: signal received   // <------
    @:~$ Child: signal received 
    ./x
    Child: signal received 
    Parent: signal received 
    @:~$ ./x
    Parent: signal received   // <------
    @:~$ Child: signal received 
    
    @:~$ 
    

    注意输出,一种情况是:“直到子进程创建了父进程发送的信号并进入while循环,当子进程有机会执行(取决于CPU调度)时,它会向父进程发回信号,并且在父进程获得机会执行子程序接收信号并打印消息”。但是有时也会在子printf打印之前发生;家长收到并打印消息(即我用箭头标记)。

    在最后一个示例中,我试图显示子进程与父进程并行执行,如果不应用并发控制机制,则输出可能会有所不同。

    一些好的资源来学习信号(1)GNU C库:Signal Handling
    (2)CERT C编码标准11. Signals (SIG)

    关于c - 在此信号处理程序中会发生什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21707588/

    相关文章:

    c - 如何在堆上创建可变长度数组?

    linux - Bash 命令 : export BLAS_LIBS ="-L$LAPACKHOME/lib -lblas"

    linux - 如何找到哪些数据包被丢弃

    c++ - 对异步运行并发出信号的命令使用撤消堆栈

    c++ - 如何捕获 SIGBUS 错误?

    c - 选择性调用写包装器

    c - 使用 typeof 编译器扩展在 C 中实现 min 函数会出现错误

    预处理器指令 #include 可以被禁用/排除吗?

    linux - 按条件选择行并用一行命令计数

    c - 当进程再次执行时,使用 signal.h 终止在后台运行的进程