[英]What happens if during a signal handling in UNIX, the same signal gets sent to the program?
[英]What happens during this signal handling program?
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)
手册提供了授权函数的列表,称这些函数在信号处理程序内部是安全的。 读:
信号处理程序的功能必须非常小心,因为在程序执行过程中的任意位置可能会中断其他地方的处理。 POSIX具有“安全功能”的概念。 如果信号中断了不安全函数的执行 ,并且处理程序调用了不安全函数,则程序的行为是不确定的。
使用main()的返回类型int
; 阅读“ main()
应该在C中返回什么?”
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()
函数返回一个子进程,并开始与父进程并行执行。
正如我看到的代码所示,我认为您理解子进程是父进程的副本,只是变量的值可以与fork()
返回的点不同,因此x
在子进程和父进程中是不同的。 我们可以使用fork的返回值来判断程序是在父进程中运行还是在子进程中运行。 但是请注意,接收信号SIGUSR1
不是父进程,而是子进程。 对于任何进程,自身进程ID的值始终为0
。 您检查返回值x = fork()
是新创建的子进程的pid,在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 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库: 信号处理 (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()
不安全?
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.