Kill child process spawned with execl without making it zombie - c

I need to spawn a long-running child process and then kill it from the parent code. At the moment I do like this:
int PID = fork();
if (PID == 0) {
execl("myexec", "myexec", nullptr);
perror("ERROR");
exit(1);
}
// do something in between and then:
kill(PID, SIGKILL);
This does the job in the sense that the child process is stopped, but then it remains as a zombie. I tried to completely remove it by adding:
kill(getpid(), SIGCHLD);
to no avail. I must be doing something wrong but I can't figure out what, so any help would be greatly appreciated. Thanks.

signal(SIGCHLD, SIG_IGN);
kill(getpid(), SIGCHLD);
Presto. No zombie.
By ignoring SIGCHLD we tell the kernel we don't care about exit codes so the zombies just go away immediately.

You have been answered with:
signal(SIGCHLD, SIG_IGN);
to ignore the signal sent to the parent when a child dies. This is an old mechanism to avoid zombies, but zombies are your friends, as my answer will explain.
The zombies are not a bug, but a feature of the system. They are there to complete the fork(2), wait(2), exit(2), kill(2) group of system calls.
When you wait(2) for a child to die, the kernel tests if there's a child running with the characteristics you state in the wait(2). If it exists, the wait(2) will block, because the wait(2) system call is the one used in unix to give the parent the exit status of the waited child. If you use wait() and you have done no fork() a new child previously, wait() should give you an error, because you are calling wait with no fork (i'll stop boldening the system calls in this discussion from here on) but what happens if the parent did a fork but the child died before the parent was capable of making a wait. Should this be taken as an error? No. The system maintains the process table entry for the child proces, until one of two things happen: The parent dies (then all children processess get orphaned, being adopted by process id 1 ---init/systemd--- which is continously blocked in wait calls; or the parent does a wait, in which case the status of one (or the one requested) of the children is reported.
So in a proper usage of the system, it is possible (or necessary) to make a wait call for each fork you make. if you do more waits than forks, you get errors... if you make more forks than waits, you get zombies. In order to compensate this, your code should be changed to make the appropiate wait call.
kill(PID, SIGINT); /* don't use SIGKILL in the first time, give time to your process to arrange its last will before dying */
res = waitpid(PID, &status, 0);
And this will allow the child to die normally. The child is going to die, because you killed it (except if the child has decided to ignore the signal you send to it)
The reason for no race condition here (the child could die before is is wait()ed for) is the zombie process. Zombie processes are not proper processes, they don't accept signals, it is impossible to kill them, because they are dead already (no pun here ;)). They only occupy the process table slot, but no resource is allocated to them. When a parent does a wait call, if there's a zombie, it will be freed and the accounting info will be transferred to the parent (this is how the accounting is done), including the exit status, and if there isn't (because it died prematurely and you had invoked the above behaviour) you will get an error from wait, and the accounting info will be tranferred to init/systemd, which will cope for this. If you decide to ignore the SIGCHLD signal, you are cancelling the production of zombies, but the accounting is being feed in the wron way to init/systemd, and not accounted in the parent. (no such process can be waited for) you cannot distinguish if the wait fails because the child process died or because you didn't spawn it correctly. More is to come.
Let's say that the child cannot exec a new program and it dies (calling exit()). When you kill it, nothing happens, as there's no target process (well, you should receive an error from kill call, but I assume you are not interested in the kill result, as it is irrelevant for this case, you are interested in the status of the child, or how did the child died. This means you need to wait for it. if you get a normal exit, with a status of 1, (as you did an exit in case exec() fails) you will know that the child was not able to exec (you still need to distinguish if the 1 exit code was produced by the child or by the program later run by the child). If you successfully killed the child, you should get a status telling you that the child was killed with signal (the one you sent) and you will know that your code is behaving properly.
In case you don't want to block your parent process in the wait system call (well, your child program could have decided to ignore signals and the kill had no effect), then you can substitute the above by this:
kill(PID, SIGINT);
res = waitpid(PID, &status, WNOHANG);
that will not block the parent, in the case the child program has decided to ignore the signal you send to it. In this case, if wait returns -1 and errno value EINTR, then you know that your child has decided to ignore the signal you sent to it, and you need help from the operator (or be more drastic, e.g. killing it with SIGKILL).
A good approach should be
void alarm_handler()
{
}
...
kill(PID, SIGINT); /* kill it softly (it's your child, man!!) */
void *saved = signal(SIGALRM, alarm_handler);
alarm(3); /* put an awakener, you will be interrupted in 3s. */
res = waitpid(PID, &status, 0);
signal(SIGALRM, saaved); /* restore the previous signal handler */
if (res == -1 && errno == EINTR) {
/* we where interrupted by the alarm, and child didn't die. */
kill(PID, SIGKILL); /* be more rude */
}

Related

SIGKILL to a subprocess tree on parent termination

I have a daemon application that starts several 3rd party executables (all closed-sources and non modifiable).
I would like to have all the child processes to automatically terminate when the parent exits for any reason (including crashes).
Currently, I am using prctl to achieve this (see also this question):
int ret = fork();
if (ret == 0) {
//Setup other stuff
prctl (PR_SET_PDEATHSIG, SIGKILL);
if (execve( "childexecutable" ) < 0) { /*signal error*/}
}
However, if "childexecutable" also forks and spawns "grandchildren", then "grandchildren" is not killed when my process exits.
Maybe I could create an intermediate process that serves as subreaper, that would then kill "someexecutable" when my process dies, but then wait for SIGCHLD and continue to kill child processes until none is left, but it seems very brittle.
Are there better solutions?
Creating a subreaper is not useful in this case, your grandchildren would be reparented to and reaped by init anyway.
What you could do however is:
Start a parent process and fork a child immediately.
The parent will simply wait for the child.
The child will carry out all the work of your actual program, including spawning any other children via fork + execve.
Upon exit of the child for any reason (including deathly signals e.g. a crash) the parent can issue kill(0, SIGKILL) or killpg(getpgid(0), SIGKILL) to kill all the processes in its process group. Issuing a SIGINT/SIGTERM before SIGKILL would probably be a better idea depending on what child processes you want to run, as they could handle such signals and do a graceful cleanup of used resources (including children) before exiting.
Assuming that none of the children or grandchildren changes their process group while running, this will kill the entire tree of processes upon exit of your program. You could also keep the PR_SET_PDEATHSIG before any execve to make this more robust. Again depending on the processes you want to run a PR_SET_PDEATHSIG with SIGINT/SIGTERM could make more sense than SIGKILL.
You can issue setpgid(getpid(), 0) before doing any of the above to create a new process group for your program and avoid killing any parents when issuing kill(0, SIGKILL).
The logic of the "parent" process should be really simple, just a fork + wait in a loop + kill upon the right condition returned by wait. Of course, if this process crashes too then all bets are off, so take care in writing simple and reliable code.

How do I get the PID of a set process in the handler of its own SIGCHLD?

I'm writting a toy shell for university and I have to update the status of a background process when it ends. So I came up with the idea of making the handler of SIGCHLD do that, since that signal is sent when the process ends. The problem is that in order to implement the command jobs, I have to update the status from "running" to "terminated" and first I have to find that specific process in the array I have dedicated to it, and one way to do it is by searching by pid, since the array stores the information that is display in jobs. Each entry stores the process pid, the status (which is a string) and the command itself.
Now the question is:
Is there a way to get the pid of the process that called the signal when it ended?
Right now this is what my handler function looks like:
void handler(int sig){
int child_pid;
child_pid = wait(NULL);
//finds the process with a pid identical
//to child_pid in the list and updates its status
...
}
Since wait(NULL) returns the pid of the first process that ends since it's called, the status is only updated when another background process ends and therefore the wrong process status is updated.
We haven't been tought many things from the wait() and waitpid()functions apart from that they waits for a process to end, so any insight may be helpful.
While it is not a good idea to use wait in a signal handler, you can do the following to accomplish what you are trying to do.
void handler(int sig){
int child_pid;
int status;
child_pid = waitpid(-1, &status, WUNTRACED | WNOHANG);
if(child_pid > 0)
{
// your code
// make sure to deal with the cases of child_pid < 0 and child_pid == 0
}
}
What you are doing is not technically wrong, however, it would be better to use waitpid. When you use waitpid(-1,...) it works similarly to if you used wait(...) and will not only wait for a specified process but any process that terminates. The main difference is that you can specify WUNTRACED which will suspend execution until a process in the wait set becomes either terminated or stopped. The WNOHANG will tell waitpid to not suspend the execution of the process. You don't want your handler to be suspended.
If multiple signals are sent to the same process i.e. due to multiple children terminating at the same time, then it will seem like only one signal was sent because the signal handler will not be executed again. This is due to how signals are sent; when a signal is created it is put into an exception table for the process to then "receive" it; signals do not use queues. To account for this, you will need to iterate over the waitpid(-1,...) call until it returns 0 to make sure you are reaping all of the terminated children.
Additionally, do watch out for where else you are reaping the child (note that if the child has already been reaped then waitpid will return 0 if you use the WNOHANG flag). I would assume that this is what is causing the behavior you are seeing of the status only updating when another background process ends. For example, because you are making a toy shell I assume that you are waiting for the foreground processes somewhere, and if you use the wait function there as well as in your handler you could get 1 of two things to happen. The child gets reaped in the 'wait for foreground process' method and then when the handler is executed there is nothing for it to reap. And 2, the child gets reaped in the handler method, and then the 'wait for foreground process' never exits.

Why fork() twice [duplicate]

This question already has answers here:
What is the reason for performing a double fork when creating a daemon?
(9 answers)
Closed 8 years ago.
Nagios lets me configure child_processes_fork_twice=<0/1>.
The documentation says
This option determines whether or not Nagios will fork() child processes twice when it executes host and service checks. By default, Nagios fork()s twice. However, if the use_large_installation_tweaks option is enabled, it will only fork() once.
As far as I know fork() will spawn a new child process. Why would I want to do that twice?
All right, so now first of all: what is a zombie process? It's a process that is dead, but its parent was busy doing some other work, hence it could not collect the child's exit status. In some cases, the child runs for a very long time, the parent cannot wait for that long, and will continue with it's work (note that the parent doesn't die, but continues its remaining tasks but doesn't care about the child). In this way, a zombie process is created. Now let's get down to business. How does forking twice help here? The important thing to note is that the grandchild does the work which the parent process wants its child to do. Now the first time fork is called, the first child simply forks again and exits. This way, the parent doesn't have to wait for a long time to collect the child's exit status (since the child's only job is to create another child and exit). So, the first child doesn't become a zombie. As for the grandchild, its parent has already died. Hence the grandchild will be adopted by the init process, which always collects the exit status of all its child processes. So, now the parent doesn't have to wait for very long, and no zombie process will be created. There are other ways to avoid a zombie process; this is just a common technique. Hope this helps!
In Linux, a daemon is typically created by forking twice with the intermediate process exiting after forking the grandchild. This has the effect of orphaning the grandchild process. As a result, it becomes the responsibility of the OS to clean up after it if it terminates. The reason has to do with what are known as zombie processes which continue to live and consume resources after exiting because their parent, who'd normally be responsible for the cleaning up, has also died.
Also from the documentation,
Normally Nagios will fork() twice when it executes host and service checks. This is done to (1) ensure a high level of resistance against plugins that go awry and segfault and (2) make the OS deal with cleaning up the grandchild process once it exits.
Unix Programming Faq ยง1.6.2:
1.6.2 How do I prevent them from occuring?
You need to ensure that your parent process calls wait() (or
waitpid(), wait3(), etc.) for every child process that terminates;
or, on some systems, you can instruct the system that you are
uninterested in child exit states.
Another approach is to fork() twice, and have the immediate child
process exit straight away. This causes the grandchild process to be
orphaned, so the init process is responsible for cleaning it up. For
code to do this, see the function fork2() in the examples section.
To ignore child exit states, you need to do the following (check your
system's manpages to see if this works):
struct sigaction sa;
sa.sa_handler = SIG_IGN;
#ifdef SA_NOCLDWAIT
sa.sa_flags = SA_NOCLDWAIT;
#else
sa.sa_flags = 0;
#endif
sigemptyset(&sa.sa_mask);
sigaction(SIGCHLD, &sa, NULL);
If this is successful, then the wait() functions are prevented from
working; if any of them are called, they will wait until all child
processes have terminated, then return failure with errno == ECHILD.
The other technique is to catch the SIGCHLD signal, and have the
signal handler call waitpid() or wait3(). See the examples section
for a complete program.
This code demonstrates how to use the double fork method to allow the grandchild process to become adopted by init, without risk of zombie processes.
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
int main()
{
pid_t p1 = fork();
if (p1 != 0)
{
printf("p1 process id is %d", getpid());
wait();
system("ps");
}
else
{
pid_t p2 = fork();
int pid = getpid();
if (p2 != 0)
{
printf("p2 process id is %d", pid);
}
else
{
printf("p3 process id is %d", pid);
}
exit(0);
}
}
The parent will fork the new child process, and then wait for it to finish. The child will fork a grandchild process, and then exit(0).
In this case, the grandchild doesn't do anything except exit(0), but could be made to do whatever you'd like the daemon process to do. The grandchild may live long and will be reclaimed by the init process, when it is complete.

Killing and reaping process

I have a child process specified by pid. This process could be:
Running
Defunct/Zombie (unreaped)
Already reaped (and thus no longer exists)
I would like to kill this process and ensure no zombie remains. Currently my code is
kill(pid, SIGKILL);
int temp;
waitpid(pid, &temp, 0);
Would this work?
EDIT: The process specified by pid is a child of my program.
This looks fine so far, but I wonder why you would let case 3 happen. You should perform some bookkeeping, which of your child processes have terminated and are waiting to be reaped.
One way would be to install a handler for SIGCHLD, setting a flag that a waitpid is in order. That way you guarantee that all pids are actually those of your child processes.
Should work fine, but be sure to check the returnvalue of waitpid.
The call may have returned due to a signal.

Does a KILL signal exit a process immediately?

I'm working on a server code that uses fork() and exec to create child processes. The PID of the child is registered when fork() succeeds and cleaned up when the CHILD signal has been caught.
If the server needs to stop, all programs are killed, eventually with a KILL signal. Now, this works by means of iterating through all registered PIDs and waiting for the CHILD signal handler to remove the PIDs. This will fail if child program did not exit properly. Therefore I want to use kill in combination with waitpid to ensure that PID list is cleaned up and log and do some other stuff otherwise.
Consider the next code sample:
kill(pid, SIGKILL);
waitpid(pid, NULL, WNOHANG);
Excerpt from waitpid(2):
waitpid(): on success, returns the process ID of the child whose state has changed; if WNOHANG was specified and one or more child(ren)
specified by pid exist, but have not yet changed state, then 0 is returned. On error, -1 is returned.
Is the process given by pid always gone before the next function kicks in? Will waitpid always return -1 in the above case?
Is the process given by pid always gone before the next function kicks in?
There is no guarantee for that. On a multiprocessor your process might be on CPU 0 while the cleanup in the kernel for the killed process takes place on CPU 1. That's a classical race-condition. Even on singlecore processors there is no guarantee for that.
Will waitpid always return -1 in the above case?
Since it is a race condition - in most cases it perhaps will. But there is no guarantee.
Since you are not interested in the status, this semicode might be more appropriate in your case:
// kill all childs
foreach(pid from pidlist)
kill(pid, SIGKILL);
// gather results - remove zombies
while( not_empty(pidlist) )
pid = waitpid(-1, NULL, WNOHANG);
if( pid > 0 )
remove_list_item(pidlist, pid);
else if( pid == 0 )
sleep(1);
else
break;
The KILL signal handler will run during the killed processes CPU time. This is potentially much later than your waitpid call, especially on a loaded system, so waitpid can very well return 0.
JFYI there is also the option of pidfd_send_signal(). From the manual:
The pidfd_send_signal() system call allows the avoidance of race
conditions that occur when using traditional interfaces (such as
kill(2)) to signal a process. The problem is that the
traditional interfaces specify the target process via a process
ID (PID), with the result that the sender may accidentally send a
signal to the wrong process if the originally intended target
process has terminated and its PID has been recycled for another
process. By contrast, a PID file descriptor is a stable
reference to a specific process; if that process terminates,
pidfd_send_signal() fails with the error ESRCH.
You can see an example on how to use it here.

Resources