Child Process Receives Parent's Sigint

Child process receives parent's SIGINT

If you are generating the SIGINT with Ctrl+C on a Unix system, then the signal is being sent to the entire process group.

You need to use setpgid or setsid to put the child process into a different process group so that it will not receive the signals generated by the controlling terminal.


[Edit:]

Be sure to read the RATIONALE section of the setpgid page carefully. It is a little tricky to plug all of the potential race conditions here.

To guarantee 100% that no SIGINT will be delivered to your child process, you need to do something like this:

#define CHECK(x) if(!(x)) { perror(#x " failed"); abort(); /* or whatever */ }
/* Block SIGINT. */
sigset_t mask, omask;
sigemptyset(&mask);
sigaddset(&mask, SIGINT);
CHECK(sigprocmask(SIG_BLOCK, &mask, &omask) == 0);

/* Spawn child. */
pid_t child_pid = fork();
CHECK(child_pid >= 0);
if (child_pid == 0) {
/* Child */
CHECK(setpgid(0, 0) == 0);
execl(...);
abort();
}
/* Parent */
if (setpgid(child_pid, child_pid) < 0 && errno != EACCES)
abort(); /* or whatever */
/* Unblock SIGINT */
CHECK(sigprocmask(SIG_SETMASK, &omask, NULL) == 0);

Strictly speaking, every one of these steps is necessary. You have to block the signal in case the user hits Ctrl+C right after the call to fork. You have to call setpgid in the child in case the execl happens before the parent has time to do anything. You have to call setpgid in the parent in case the parent runs and someone hits Ctrl+C before the child has time to do anything.

The sequence above is clumsy, but it does handle 100% of the race conditions.

child process receives SIGINT which should be handled only by parent process, resulting in abrupt termination of child

thanks to the people that answered in the comments this is an acceptable solution (still not perfect as explained in the comments)

just add this few lines before executing cmd.Start():

    // launch as new process group so that signals (ex: SIGINT) are not sent also the the child process
cmd.SysProcAttr = &syscall.SysProcAttr{
CreationFlags: syscall.CREATE_NEW_PROCESS_GROUP, // windows
// Setpgid: true, // linux
}

Sending signal from child to parent

You need to set the signal handler before SIGINT is sent to the parent process, otherwise, the handler will not be executed. Also, the parent process is being killed before it executes anything. The easy way to fix this would be to move the sleep call after the code for the parent process, and add a delay to the child process.

#include<stdio.h>
#include<signal.h>
#include<unistd.h>
#include<stdlib.h>
void sig_usr(int signo){
if(signo == SIGINT)
printf("Signal caught!");
return;
}

int main(void){
pid_t pid, ppid;
ppid = getpid();
printf("ppid = %d\n", ppid);
if((pid = fork()) == 0){
sleep(1); // Wait for parent to finish setting up
printf("killing parent...\n");
kill(ppid, SIGINT);
printf("After killing parent...\n");
}
else{
printf("%d %d ",ppid, pid);
if(signal(SIGINT,sig_usr) == SIG_ERR)
printf("Signal processed ");
sleep(5); // Wait to be killed
}
return 0;
}

Will ctrl+c send SIGINT signals to both parent and child processes in Linux?

In both the parent and child processes I implemented a SIGINT signal
handler. So when I press "ctrl+c", will both the handlers be called at
the same time?

Yes, they both will receive SIGINT.

Or do I need to call the child process's signal handler explicitly in
the parent process's handler?

"Calling" another process' signal handler doesn't make sense. If the both the process have a handler installed then they will be called once they receive the signal SIGINT.

I just didn't quite understand what does "foreground process group"
means.

Typically, a process associated with a controlling terminal is foreground process and its process group is called foreground process group. When you start a process from the command line, it's a foreground process:

E.g.

$ ./script.sh # foreground process
$ ./script & # background process

I suggest you read about tty and The TTY demystified for a detailed explanation.

How to keep sending signals to children from parent process repeatedly?

Note: This is not ment as an answer, just some edited code and conclusions that might lead to a solution.

#include<stdio.h>
#include<stdlib.h>
#include<sys/types.h>
#include<unistd.h>
#include<signal.h>
#include<fcntl.h>
#include<sys/wait.h>

int counter = 0;
int fd;
const char* filename;
int j = 0;
int no_of_children;
int* cpid;
int my_pid;

//User define signal handler
static void sig_usr1(int);
static void sig_usr2(int);

static void sig_usr1(int signo)
{
//Now the child process waits for reading the Filename

//Block SIGUSR1 until it's complete
signal(SIGUSR1, SIG_IGN);
printf("%d\tBlocked now.\n", my_pid);
printf("%d\tChild no %d is reading now.\n",my_pid, getpid());
fd = open(filename, O_RDONLY | O_CREAT);

char buf = 'a';
int k=0;
char* op = (char*) malloc (255*sizeof(char));

while(read (fd, &buf, 1))
{
if (buf == '\n')
{
op[k] = '\0';
break;
}

else
{
op[k++] = buf;
}
}

//Now wait for a second and then send a signal
sleep(1);
//Print the contents of the buffer via op
printf("%d\tOutput: %s\n", my_pid, op);

//Now unblock the signal
kill(getppid(), SIGUSR2);
signal(SIGUSR1, sig_usr1);
printf("%d\tUnblocked now\n", my_pid);
}

static void sig_usr2(int signo)
{
if (signo == SIGUSR2)
{
printf("%d\tParent Received SIGUSR2. Child Process with PID %d is now free\n", my_pid, cpid[j]);
kill (cpid[j++], SIGUSR1);
if (j == no_of_children)
{
j = 0;
}

}
}

int main(int argc, char* argv[])
{
my_pid = getpid();
//Filename is the first argument
filename = argv[1];

//Number of Child Processes to be spawned
no_of_children = atoi(argv[2]);

cpid = (int*) malloc (no_of_children*sizeof(int));

signal(SIGUSR2, sig_usr2);
//Create no_of_children children
for(int i=0; i<no_of_children; i++)
{
cpid[i] = fork();
if (cpid[i] == 0)
{
//Inside a child
my_pid = getpid();
printf("%d\tCreated %dth child process", my_pid, i);
printf(" with Process ID = %d\n", getpid());

signal(SIGUSR1, sig_usr1);

while(1)
{
pause();
}

//Every child process must exit so control goes back to the parent
exit(0);
}
printf("%d\tforked %dth child -> %d\n", my_pid, i, cpid[i]);
}

//Returns to the parent process
while(1)
{
int fpid = cpid[j];
//Send the signal to the free child process
printf("%d\tSending to PID %d\n", my_pid, fpid);
kill(fpid, SIGUSR1);
//j = (j < no_of_children - 1) ? j + 1 : 0;
pause();
}

return 0;
}

OK, what did I do?

  1. Add the PID of the current process at the beginning of each printf(). For this the variable my_pid was added and set at convenient places. Yes, I'm aware that some outputs gave this information already. I didn't want to change too much of the OP's source.
  2. Removed double \n in the course. They don't help.
  3. To see the success of fork() print its result.
  4. Add a printf in the endless loop of the main process in the end. I wanted to see which process got signaled how many times at which rate. Wow, that was a lot!

Hm, could it be that the main process floods the child with so many signals that it can't even get to its call of signal() to block it temporarily?


  1. Add pause() in the endless loop of the main process in the end.

Now things start to work, but most probably not in a way the OP wanted. Anyway, all child processes are created and receive their signals, reading the file, sending back their signals, and so on.

It seems there are too many signals in the system. The algorithm as such has to be thought over. So this is my advice:

  • Make it slow. You could speed it up later.
  • Put as many watch points (here: printf()) in the code to follow all events.
  • Especially for multi-processing think about a way to visualize the results. I copied the shell output in an editor and indented the lines like this:
$ ./signal_repetition signal_repetition.c 4
1901 forked 0th child -> 1902
1902 Created 0th child process with Process ID = 1902
1901 forked 1th child -> 1903
1903 Created 1th child process with Process ID = 1903
1901 forked 2th child -> 1904
1904 Created 2th child process with Process ID = 1904
1901 forked 3th child -> 1905
1901 Sending to PID 1902
1902 Blocked now.
1902 Child no 1902 is reading now.
1905 Created 3th child process with Process ID = 1905
1902 Output: #include<stdio.h>
1902 Unblocked now
1901 Parent Received SIGUSR2. Child Process with PID 1902 is now free
1901 Sending to PID 1903
1902 Blocked now.
1902 Child no 1902 is reading now.
1903 Blocked now.
1903 Child no 1903 is reading now.
1902 Output: #include<stdio.h>
1902 Unblocked now
1901 Parent Received SIGUSR2. Child Process with PID 1903 is now free
1901 Sending to PID 1904
1904 Blocked now.
1903 Output: #include<stdio.h>
1904 Child no 1904 is reading now.
1903 Unblocked now
1903 Blocked now.
1903 Child no 1903 is reading now.
1901 Parent Received SIGUSR2. Child Process with PID 1904 is now free
1901 Sending to PID 1905
1905 Blocked now.
1905 Child no 1905 is reading now.
1903 Output: #include<stdio.h>
1904 Output: #include<stdio.h>
1903 Unblocked now
1904 Unblocked now
1904 Blocked now.
1904 Child no 1904 is reading now.
1901 Parent Received SIGUSR2. Child Process with PID 1905 is now free
  • You could create a log for each process in its own log file. Add a timestamp with at least milliseconds resolution to synchronize all logs later. This way you avoid problems with mixed up output on stdout.

There are still enough other things to sort out. Have a lot of fun! And good luck!

Using signals in a child process

If you send SIGINT, whose default disposition is to kill the process, to a process that neither blocks it nor handles it, the process will die.

If you want the signal to interrupt blocking calls like pause(), it needs to have a handler.

But simply installing a handler introduces race conditions:

if (c == 0 ){
//< if the signal arrives here the child dies
signal(SIGINT, handler);
//< if the signal arrives here then nothing happens except the handler is run
pause(); //< if the handler arrives here then pause gets interrupted
printf("signal was given\n");
exit(0);
}

To eliminate the race conditions, you need to

  1. block the signal in the parent so that the child starts with the signal blocked
  2. install the handler in the child
  3. unblock the signal and pause() in one atomic step

To achieve 3. in one step, you need sigsuspend() instead of pause().

#include <stdio.h>
#include<sys/types.h>
#include<unistd.h>
#include<stdlib.h>
#include<sys/wait.h>
#include<signal.h>

void handler(int Sig){}

int main()
{
sigset_t sigint, oldmask; sigemptyset(&sigint); sigaddset(&sigint, SIGINT);
sigprocmask(SIG_BLOCK, &sigint, &oldmask);

pid_t c=fork();
if(0>c) return perror(0),1;
if (c==0){
signal(SIGINT, handler);
sigdelset(&oldmask,SIGINT); /*in (the unlikely) case the process started with SIGINT blocked*/
sigsuspend(&oldmask);
printf("signal was given\n");
exit(0);
}
kill(c,SIGINT);
wait(0);
return 0;
}

Alternatively, you can use sigwait() and drop the need for a handler altogether:

#include <stdio.h>
#include<sys/types.h>
#include<unistd.h>
#include<stdlib.h>
#include<sys/wait.h>
#include<signal.h>

int main()
{
sigset_t sigint, oldmask; sigemptyset(&sigint); sigaddset(&sigint, SIGINT);
sigprocmask(SIG_BLOCK, &sigint, &oldmask);

pid_t c=fork();
if(0>c) return perror(0),1;
if (c==0){
int sig; sigwait(&sigint,&sig);
printf("signal was given\n");
exit(0);
}
kill(c,SIGINT);
wait(0);
return 0;
}

C child process doesn't receive SIGINT signal

TL;DR: you are signaling a different process than you think you are.

You mention in comments that you are trying to deliver a SIGINT to your process by typing CTRL-C on the keyboard. That's fine when you want to kill the foreground process group of the terminal then owning the keyboard. Supposing that you launch your program from a shell window and it does nothing to put itself in the background, the initial process will indeed be in the foreground process group, and if you never fork() then nothing else you do do changes that until the process terminates. Typing CTRL-C in that terminal will therefore deliver a SIGINT to that process.

HOWEVER, when the initial process dies, the shell that launched it puts itself back in the foreground. You can check this by typing commands to it. Moreover, in the case where the initial process successfully fork()s a child, the child and its process group move into the background when the when the shell puts itself in the foreground. At that point, any CTRL-C you type goes to the shell (which ignores it), not to the child of your webserver process.

You can send a SIGINT to a process running in the background or without a controlling terminal via the kill command, for example

kill -INT 12345

If you use that method to deliver a SIGINT to the child process in your forking case, you will see that the process's registered signal handler catches and handles the signal exactly as it should.



Related Topics



Leave a reply



Submit