This is a race condition. Your code assumes that the child starts first and is not unloaded by the parent until he has installed all the signal handlers and starts the cycle forever.
If this is not the case, the parent can send a signal to the child before the child has the opportunity to catch the signal. Thus, the child process is killed because the default action for SIGHUP , SIGINT and SIGQUIT ends.
In your particular case, you will never see any conclusion from the child. This means that the parent sent SIGHUP child, and SIGHUP was delivered before the child changed the default behavior. Thus, the child was killed.
Actually, if you performed some error checking of the return value of kill(2) - what you need - you will see the ESRCH in the parent when you try to send SIGINT and SIGQUIT , because the child is already (provided that no other process in the system has been started, and meanwhile the same PID was assigned).
So how do you fix this? Either use some form of synchronization to force the child to start first, and allow parents to execute only after all the signal handlers are installed, or to configure the signal handlers before branching, and then disable them in the parent element. The code below uses the latter approach:
#include <stdio.h> #include <signal.h> #include <stdlib.h> #include <unistd.h> void sighup(int); /* routines child will call upon sigtrap */ void sigint(int); void sigquit(int); int main(void) { int pid; signal(SIGHUP,sighup); /* set function calls */ signal(SIGINT,sigint); signal(SIGQUIT, sigquit); /* get child process */ if ((pid = fork()) < 0) { perror("fork"); exit(1); } if (pid == 0) { /* child */ for(;;); /* loop for ever */ } else { signal(SIGHUP, SIG_DFL); signal(SIGINT, SIG_DFL); signal(SIGQUIT, SIG_DFL); /* parent */ /* pid hold id of child */ printf("\nPARENT: sending SIGHUP\n\n"); kill(pid,SIGHUP); sleep(3); /* pause for 3 secs */ printf("\nPARENT: sending SIGINT\n\n"); kill(pid,SIGINT); sleep(3); /* pause for 3 secs */ printf("\nPARENT: sending SIGQUIT\n\n"); kill(pid,SIGQUIT); sleep(3); } return 0; } void sighup(int signo) { signal(SIGHUP,sighup); /* reset signal */ printf("CHILD: I have received a SIGHUP\n"); } void sigint(int signo) { signal(SIGINT,sigint); /* reset signal */ printf("CHILD: I have received a SIGINT\n"); } void sigquit(int signo) { printf("My DADDY has Killed me!!!\n"); exit(0); }
In addition, you should not use signal(2) : it is unreliable in different ways, and its exact semantics are platform dependent. To ensure maximum portability, you should use sigaction(2) . Refer to the guides to find out more. Here's the code using sigaction(2) :
#include <stdio.h> #include <signal.h> #include <stdlib.h> #include <unistd.h> void sighup(int); /* routines child will call upon sigtrap */ void sigint(int); void sigquit(int); int main(void) { struct sigaction sigact; sigact.sa_flags = 0; sigemptyset(&sigact.sa_mask); sigact.sa_handler = sighup; if (sigaction(SIGHUP, &sigact, NULL) < 0) { perror("sigaction()"); exit(1); } sigact.sa_handler = sigint; if (sigaction(SIGINT, &sigact, NULL) < 0) { perror("sigaction()"); exit(1); } sigact.sa_handler = sigquit; if (sigaction(SIGQUIT, &sigact, NULL) < 0) { perror("sigaction()"); exit(1); } pid_t pid; /* get child process */ if ((pid = fork()) < 0) { perror("fork"); exit(1); } if (pid == 0) { /* child */ for(;;); /* loop for ever */ } else { sigact.sa_handler = SIG_DFL; sigaction(SIGHUP, &sigact, NULL); sigaction(SIGINT, &sigact, NULL); sigaction(SIGQUIT, &sigact, NULL); /* parent */ /* pid hold id of child */ printf("\nPARENT: sending SIGHUP\n\n"); kill(pid,SIGHUP); sleep(3); /* pause for 3 secs */ printf("\nPARENT: sending SIGINT\n\n"); kill(pid,SIGINT); sleep(3); /* pause for 3 secs */ printf("\nPARENT: sending SIGQUIT\n\n"); kill(pid,SIGQUIT); sleep(3); } return 0; } void sighup(int signo) { signal(SIGHUP,sighup); /* reset signal */ printf("CHILD: I have received a SIGHUP\n"); } void sigint(int signo) { signal(SIGINT,sigint); /* reset signal */ printf("CHILD: I have received a SIGINT\n"); } void sigquit(int signo) { printf("My DADDY has Killed me!!!\n"); exit(0); }
Last but not least, you should always compile with -Wall . Your program has some errors:
- The return type of
main() must be int . - Signal handlers get the signal number as an argument, use the correct prototype and declaration.
fork(2) returns a pid_t , not int , use the correct type.- You need to enable
unistd.h to get the correct prototype for fork(2) . printf(3) not safe for an asynchronous signal, and therefore you should not call it inside the signal handler. In this toy program, it's good to see how signals work together, but keep in mind that you should never do this in the real world. For a list of the safe functions of the asynchronous signal, as well as the default actions for each signal, see man 7 signal .
Tip: Stop studying on this website. If you want to learn such things, read Advanced Programming with UNIX. Go straight to chaper 10 to find out why signal(2) is considered unreliable and deprecated. This is a great book, but it is worth spending your time on it.