在这个信号处理程序中会发生什么?

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函数(显示“收到信号”)。 但我不确定,请随时纠正或给予更多的细节。 谢谢你的帮助!

Solutions Collecting From Web of "在这个信号处理程序中会发生什么?"

你的代码有一些错误:

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

    异步信号安全功能

    信号处理函数必须非常小心,因为在其他地方的处理可能会在程序执行的某个任意点被中断。 POSIX有“安全功能”的概念。 如果一个信号中断了一个不安全的函数的执行 ,并且处理程序调用了一个不安全的函数,那么程序的行为是不确定的。

  2. 使用main() int返回类型; 阅读“什么应main()返回在C?”

  3. x应该是pid_t 。 ( 过程识别 )。

现在让我们假设你的程序编译并运行(在处理程序执行时没有被任何其他信号中断):我只是缩进你的代码,并且在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()函数中,为SIGUSR1信号注册f()函数,之后调用fork()创建一个新进程。 在运行时, fork()函数返回一个子进程开始与父进程并行执行。
正如我可以看到你的代码,我认为你明白,child-process是父进程的副本,除了变量的值可以不同于fork()返回的点,因此x在子进程和父进程中是不同的。 我们可以使用fork的返回值来判断程序是在父进程还是在子进程中运行。 但是注意它不是父节点而是接收到SIGUSR1信号的子进程。 对于任何进程,自我进程id的值始终为0 。 你检查返回值x = fork() ,它是新创建的子进程的子进程,在子进程中, x值是0 ,父进程x != 0 。 因此信号从父进程发送到子进程。

你的评论:

我认为上面的程序要求系统在父进程收到SIGUSR1信号时启动f( )函数(显示"signal received" )。

我印象中,你不认为这两个进程并发执行和“可以发生在fork()后立即创建一个子进程,子进程开始执行,并且在父进程可以发送信号给孩子之前立即终止(或者子进程可以接收信号)“。 在这种情况下,函数f()永远不会得到执行的机会,并且信号处理程序中的printf不会被打印。

但是我上面描述的可能性非常低,因为fork需要时间来创建一个新的进程。 即使您一次又一次地执行代码,大部分从父进程发送的时间信号都将执行信号处理程序。

什么是写这个代码的正确方法?

代码是xc :正确的方法是设置一个标志,指示信号处理程序执行,然后调用printf函数的标志值的基础上的信号处理程序之外,我已经在我的答案中描述: 如何避免在信号处理程序中使用printf? Jonathan Leffler在他的回答中解释道 。

 #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 xc -ox @:~$ ./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 xc -ox @:~$ ./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库: 信号处理 (2)CERT C编码标准11.信号(SIG) 。

一个问题是,子进程没有做任何事情,但会立即从main函数返回,也许在父进程可以发送信号之前。

你可能想打电话给孩子,例如pause

为了练习,这里是一个可以编译和运行的原始代码的修正版本。

 #include <stdlib.h> #include <stdio.h> #include <signal.h> #include <bits/signum.h> void f ( ); int 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\n"); exit (0); } 

这正是原来的问题建议程序应该做的。 试试看,如果你不相信我会发生什么事情。

顺便说一句:我不是很有经验的C.有一些评论断言,在子进程中使用printf()是不安全的。 为什么?? 子进程是包含虚拟地址空间的父进程的副本。 那么为什么printf()不安全?