I’m encountering a discrepancy in the behavior of my code when using kill(pid, SIGINT)
between Debian 12 and CentOS 7 in a virtual machine. Previously, this code worked as expected in Fedora 39.
#include <iostream>
#include <unistd.h>
#include <sys/types.h>
#include <signal.h>
#include <cstdlib>
#include <cstring>
#include <sys/wait.h>
using namespace std;
void sigint_handler(int signal) {
cout << "The child process received the SIGINT signal. Ending..." << endl;
exit(EXIT_SUCCESS);
}
int main() {
pid_t pid = fork();
if (pid < 0) {
cerr << "Error creating the child process." << endl;
exit(EXIT_FAILURE);
} else if (pid == 0) {
// Child process
cout << "Child process created. PID: " << getpid() << endl;
// Signal handler for SIGINT
signal(SIGINT, sigint_handler);
// Executing ping for 10 iterations, although parent process should stop it earlier
execl("/bin/sh", "sh", "-c", "ping -c 10 google.com", NULL);
} else {
// Parent process
cout << "parent process. PID from child: " << pid << endl;
sleep(3); // Wait 3 seconds before sending the SIGINT
cout << "Sending SIGINT to the child process..." << endl;
if (kill(pid, SIGINT) == -1) {
cerr << "Error sending SIGINT to the child process." << endl;
exit(EXIT_FAILURE);
}
int status;
waitpid(pid, &status, 0); // Wait to the child process to end
if (WIFEXITED(status)) {
cout << "Child process end with status: " << WEXITSTATUS(status) << endl;
} else {
cout << "Child process end in an incorrect way" << endl;
}
}
return 0;
}
After migrating to Debian 12, the code snippet above doesn’t behave as expected. Instead of stopping the ping process after sending the SIGINT signal, it continues executing all 10 iterations. However, when running the same code in a CentOS VM, it behaves correctly, stopping after a few iterations.
Why is this happening?
Thanks in advance.
Additional information:
g++ version in Debian 12: g++ (Debian 12.2.0-14) 12.2.0
g++ version in CentOS 7 VM: g++ (GCC) 4.8.5 20150623 (Red Hat 4.8.5-44)
Edit 1:
I have also tried with system("ping -c 10 google.com")
instead of execl()
without success. Same with sigaction()
and it doesn’t work.
In addition to that, the problem isn’t related to the different implementations of ping
in Debian and Red Hat based distros, because I have tried with my original code and it doesn’t stop.
In my original code, instead of ping
, I call a program.o
that has a infinite loop that should be stopped with Ctrl+C
. In Fedora and CentOS my program runs perfectly while in Debian it doesn’t.
3
Answers
I have found the issue looking at the processes launched using
pgrep -l -u $USER
.If we consider that the binary of the program is called
program
andPID from child: 10001
, in CentOS we have this:While in Debian we have this:
That means that in Debian, in order to make the program work just like in CentOS, we should change the
pid
topid + 1
because of thatsh
process with ID 10001 in the middle.The only thing that would be useful to know is why in Debian that
sh
process is created while in CentOS is not.This does not work as it seems you expect:
execl
will reset the signal action back to the default in the new process. This is setting the signal handler tosigint_handler
only for an extremely short window.Therefore, how
ping
reacts toSIGINT
is completely dependent on how it sets up its signal handlers. Presumably, the different VMs have different implementations or versions ofping
.iputils
andinetutils
both have aping
.It depends the way the shell constructs the tree of (sub)processes when interpreting commands. It is possible that
/bin/sh
is not the same shell on different Linux distributions.Anyway, why do you launch
ping
under control of shell and not launching it directly?More: you may not use
SIGINT
whose semantic for shells may also differs greatly (some may just wait for sub command to terminate). Prefer the use ofSIGTERM
.Also note that
exec
-ing reset signal handlers to default behaviour if sets the a catching function before, and prefer the use ofsigaction
which have reliable behaviour (it is not the case fosignal
).