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 does Ctrl-C terminate a child process?
Signals by default are handled by the kernel. Old Unix systems had 15 signals; now they have more. You can check </usr/include/signal.h>
(or kill -l). CTRL+C is the signal with name SIGINT
.
The default action for handling each signal is defined in the kernel too, and usually it terminates the process that received the signal.
All signals (but SIGKILL
) can be handled by program.
And this is what the shell does:
- When the shell running in interactive mode, it has a special signal handling for this mode.
- When you run a program, for example
find
, the shell:fork
s itself- and for the child set the default signal handling
- replace the child with the given command (e.g. with find)
- when you press CTRL+C, parent shell handle this signal but the child will receive it - with the default action - terminate. (the child can implement signal handling too)
You can trap
signals in your shell script too...
And you can set signal handling for your interactive shell too, try enter this at the top of you ~/.profile
. (Ensure than you're a already logged in and test it with another terminal - you can lock out yourself)
trap 'echo "Dont do this"' 2
Now, every time you press CTRL+C in your shell, it will print a message. Don't forget to remove the line!
If interested, you can check the plain old /bin/sh
signal handling in the source code here.
At the above there were some misinformations in the comments (now deleted), so if someone interested here is a very nice link - how the signal handling works.
Signals Behavior in C/C++ - Linux
raise(SIGINT)
sends SIGINT
signal only to the calling process - which is the parent process in this case. On the other hand, when you send the SIGINT
signal using CTRL + C
you send it to all of the foreground processes, i.e. the child process also receives it and therefore terminates. So there's no more a child process to reply to the SIGUSR1
signal of the parent process. Try ignoring the signal in the child process.
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.
If I register a custom signal handler on a process before forking, will the subsequent child processes also have the custom signal handler registered?
When you do this:
for (int count = 0; count < 10; count++) {
int pid = fork();
if (pid == 0) {
return 0;
}
}
You're telling the child processes to exit immediately, so they never get a chance to get the signal. Instead, have the children break
out of the loop:
for (int count = 0; count < 10; count++) {
int pid = fork();
if (pid == 0) {
break;
}
}
Then the child processes will be active when CTRL-C is pressed.
Related Topics
Determine Usb Device File Path
How to Use Cx_Freeze in Linux to Create a Package to Be Used in Windows
Errors in Make File:*** Missing Separator. Stop
Installing G++ on Windows Subsystem for Linux
How to Delete Duplicated Rows Based in a Column Value
On Linux, How to Make Sure to Unlock a Mutex Which Was Locked in a Thread That Dies/Terminates
Linux - Write Commands from One Terminal to Another
Linux Join Utility Complains About Input File Not Being Sorted
What Is the Status of Posix Asynchronous I/O (Aio)
Output the 2Nd Column of a File
Start Rails Server Automatically When Ever I Start My Ubuntu MAChine
Redirect Process Stdin and Stdout to Netcat
Run Command After 1 Hour in Linux
What Does This Command Do? "Exec Bash -L"
Apache Pig: Load a File That Shows Fine Using Hadoop Fs -Text
Putting Two Consecutive Lines into One Line with Perl/Awk
Pause Programmatically Video Player Mpv
Cannot Sudo Su Anymore, "No Tty Present and No Askpass Program Specified"