Reputation: 11
I wrote this simple program which endlessly asks the user for an input and then creates another process with fork() to execute "ls -la".
When the user input was 0, the process is created as a background process. If the user input was anything else, the parent process waits for the child process to terminate.
In each iteration, zombie processes are terminated. This seems to work just fine
The problem with this code is: When a background process is created, the parent process for any subsequent processes does not wait any more.
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <errno.h>
void delete_zombies(void)
{
pid_t kidpid;
int status;
while ((kidpid = waitpid(-1, &status, WNOHANG)) > 0)
{
printf("Child %ld terminated\n", kidpid);
}
}
int main() {
int runBGflag =0;
while(1)
{
scanf("%d",&runBGflag); //get user input
if (runBGflag==0) printf("making a child process and wait for it to finish\n");
else printf("making a background process\n");
pid_t pid; //fork
pid = fork();
if (pid > 0 && runBGflag==0) //Parent process. Waits for child termination and prints exit status
{
int status;
if (waitpid(-1, &status, 0) == pid && WIFEXITED(status))
{
printf("child dead. parent continues\n");
}
}
if (pid == 0) //Child process. Executes commands and prints error if something unexpected happened
{
execlp ("ls", "-la", NULL);
printf ("exec: %s\n", strerror(errno));
exit(1);
}
delete_zombies();
}
}
I expect the output
E.G. I would expect following output after input 010
0
making a child process and wait for it to finish
CMakeCache.txt cmake_install.cmake sigchild Testing
CMakeFiles Makefile sigchild.cbp
child dead. parent continues
1
making a background process
CMakeCache.txt cmake_install.cmake sigchild Testing
CMakeFiles Makefile sigchild.cbp
0
making a child process and wait for it to finish
CMakeCache.txt cmake_install.cmake sigchild Testing
CMakeFiles Makefile sigchild.cbp
child dead. parent continues
However i only get
0
making a child process and wait for it to finish
CMakeCache.txt cmake_install.cmake sigchild Testing
CMakeFiles Makefile sigchild.cbp
child dead. parent continues
1
making a background process
CMakeCache.txt cmake_install.cmake sigchild Testing
CMakeFiles Makefile sigchild.cbp
0
making a child process and wait for it to finish
CMakeCache.txt cmake_install.cmake sigchild Testing
CMakeFiles Makefile sigchild.cbp
Upvotes: 0
Views: 643
Reputation: 180093
In the event that you run the child with the intention of waiting for it, you do not actually wait for that specific child, but rather for any child. If there is a previously launched and unwaited-for child then that can be collected instead.
You want something more like this:
if (pid > 0 && runBGflag==0) //Parent process. Waits for child termination and prints exit status
{
int status;
if (waitpid(pid, &status, 0) == pid && WIFEXITED(status)) // CHANGED
{
printf("child dead. parent continues\n");
}
}
Note also that WIFEXITED(status)
tests whether the process being reported on terminated normally. That does not include those that are killed by signals, which are nevertheless just as dead. It also does not include those that have been stopped but not terminated -- those are not dead, but your program stops waiting for them anyway.
Upvotes: 1
Reputation: 75062
You are waiting for any child processes in this part:
if (pid > 0 && runBGflag==0) //Parent process. Waits for child termination and prints exit status
{
int status;
if (waitpid(-1, &status, 0) == pid && WIFEXITED(status))
{
printf("child dead. parent continues\n");
}
}
Therefore, the "background process" may corrected here instead of the new process. The "background process" should have different pid from the new process, so the message "child dead..." may not be printed.
This can be verified by inserting a debug-printing statement like this:
if (pid > 0 && runBGflag==0) //Parent process. Waits for child termination and prints exit status
{
int status;
pid_t ret = waitpid(-1, &status, 0);
printf("debug: expected %d, actual %d\n", (int)pid, (int)ret);
if (ret == pid && WIFEXITED(status))
{
printf("child dead. parent continues\n");
}
}
To avoid this, you should specify the process to wait via the first argument of waitpid()
like this:
if (pid > 0 && runBGflag==0) //Parent process. Waits for child termination and prints exit status
{
int status;
if (waitpid(pid, &status, 0) == pid && WIFEXITED(status))
{
printf("child dead. parent continues\n");
}
}
Upvotes: 1