Catching multiple SIGCHLD processes for Unix Style Shell in C [duplicate] - c

I'm trying to figure out what the pid is of a process that sent the SIGCHLD signal, and I want to do this in a signal handler I created for SIGCHLD. How would I do this? I'm trying:
int pid = waitpid(-1, NULL, WNOHANG);
because I want to wait for any child process that is spawned.

If you use waitpid() more or less as shown, you will be told the PID of one of the child processes that has died — usually, that will be the only process that has died, but if you get a flurry of them, you might get one signal and many corpses to collect. So, use:
void sigchld_handler(int signum)
{
pid_t pid;
int status;
while ((pid = waitpid(-1, &status, WNOHANG)) > 0)
{
unregister_child(pid, status); // Or whatever you need to do with the PID
}
}
You can replace &status with NULL if you don't care about the exit status of the child.
Different systems document the return value of waitpid() slightly differently. However, POSIX is one of the more careful ones and says:
If wait() or waitpid() returns because the status of a child process is available, these functions shall return a value equal to the process ID of the child process for which status is reported. If wait() or waitpid() returns due to the delivery of a signal to the calling process, -1 shall be returned and errno set to [EINTR]. If waitpid() was invoked with WNOHANG set in options, it has at least one child process specified by pid for which status is not available, and status is not available for any process specified by pid, 0 is returned. Otherwise, -1 shall be returned, and errno set to indicate the error.

Related

Confused on "zombie processes" in c programming

Question: How can I determine which one produces a "zombie process"
// Case 1
while(fork())
;
exit(0);
// Case 2
while(!fork())
;
exit(0);
I know that a "zombie" is - when a process terminates and still consumes resources. (Or at least I think that's what it is)
I think that Case 1 is the case in which will produce a zombie process because it will return -1 on an error, and while(-1) = true so it will just keep forking? I'm not really sure. Any insight would be great,
BTW: This code is being run on a Linux environment in the c programming language
Thanks in advance
A zombie process is a child process that terminates, but has not been waited for by the parent. Child processes are typically created by fork:
int pid = fork();
if (pid < 0) {
// fork failed
} else if (pid == 0) {
// this is the child process
}
// this is the parent
fork returns 0 to the child process and a positive pid to the parent. There are two cases in terms of their termination:
child exit before parent, then the child becomes a "zombie" process until the parent calls the wait family functions to get the child's exit status.
parent exit before child, then the child will be re-parented to the init process, the init process will call wait upon the child exits. This works because the parent will receive SIGCHLD signal when its child exits, and it can call wait in the signal handler. So in this case, no zombie will be created.
Also, note the posix defines no zombies are created if the SIGCHLD is ignored by the parent:
POSIX.1-2001 specifies that if the disposition of SIGCHLD is set to SIG_IGN or the SA_NOCLDWAIT flag is set for SIGCHLD (see sigaction(2)), then children that terminate do not
become zombies and a call to wait() or waitpid() will block until all children have terminated, and then fail with errno set to ECHILD. (The original POSIX standard left the
behavior of setting SIGCHLD to SIG_IGN unspecified. Note that even though the default disposition of SIGCHLD is "ignore", explicitly setting the disposition to SIG_IGN results
in different treatment of zombie process children.)
For the two case in the OP:
// Case 1
while(fork()) // same as while(fork() != 0)
;
exit(0);
// Case 2
while(!fork()) // same as while(fork() == 0)
;
exit(0);
The 1st code keeps forking in the parent, no matter it succeeds or not, and the resulted children will exit immediately since the return value will be 0. Since the parent is hung in the while loop no matter the fork succeeds or failed(fork only returns 0 for children), all the children will become zombie.
For the 2nd case, the parent exit immediately when fork returns, but the child will keep forking, and this child will again do the same, that is, it will exit immediately, and the child it created will keep forking. In this case, since the parent exit, all its children will reparented to init process, as a result, no zombies will be created.

Why is waitpid(-1, &status, 0) not suspending the process without any childs at all?

I don't understand the waitpid() function very well. The manual says:
The wait() system call suspends execution of the calling process until one of its children terminates. The call wait(&status) is equivalent to: waitpid(-1, &status, 0);
Source: http://manpages.ubuntu.com/manpages/raring/man2/wait.2.html
As far as I understand this: If I have a process and no child process what so ever and call the waitpid(-1, &status, 0) function then the process should hang there and don't continue because there can never be a state change without children and so the process just hangs there waiting for the child process to change state which will never happen in this case, but this understanding seems to be wrong because the code below does not hang at the waitpid() function. Instead it returns -1 for error which is plausible because there was no child process whatsoever but as my understanding according to the manual goes the program should just hang at/in the waitpid() function and wait for a child to change state. Yet there is no child, so there is no status change, so it should just hang in the waitpid() function and wait and wait. The below code should never reach the printf() statement but instead it does reach the printf() function.
This code is just for demonstrations puposes:
#include <sys/wait.h>
#include <sys/types.h>
#include <stdio.h>
int main() {
int status;
pid_t pid;
pid = waitpid(-1, &status, 0);
printf("%i\n", (int) pid); // pid returns -1
return 0;
}
This program executes until the end but to my understanding according to the manual it shouldn't. What's wrong with my above reasoning?
Clearly, the intention is that, by returning -1, it is indicating that there are no children to wait for (in a sense, all children have terminated).
Check errno to find out why waitpid returned -1:
ECHILD (for wait()) The calling process does not have any unwaited-for
children.
ECHILD (for waitpid() or waitid()) The process specified by pid
(waitpid()) or idtype and id (waitid()) does not exist or is not
a child of the calling process. (This can happen for one's own
child if the action for SIGCHLD is set to SIG_IGN. See also the
Linux Notes section about threads.)
EINTR WNOHANG was not set and an unblocked signal or a SIGCHLD was
caught; see signal(7).
EINVAL The options argument was invalid.

Signal handling: how to parse code and status so WIFEXITED etc. can use them?

sa_sigaction function takes argument siginfo_t which holds "si_code" and "si_status" depending on what changed child process'es state.
I'm using a library function that takes one integer being the status integer that you can pass to WIFEXITED, WIFTERMINATED, WIFSTOPPED etc. functions.
Is there a way to derive this integer from a siginfo_t struct?
Regards,
Danyel.
I am assuming you have a signal handler for SIGCHLD, meaning a child process has died. The signal handler can then use waitpid() to reap the exit status.
pid_t pid;
int status;
while ((pid = waitpid(-1, &status, WNOHANG)) > 0) {
/* use appropriate macros to check status */
}
If you are using SA_SIGINFO, then the second parameter to the sa_sigaction callback has the exit status:
if (info->si_code == CLD_EXITED) {
/* info->si_status is the exit status */
}
However, you still need to wait on the child process explicitly to reap the process from the process table.

Determine pid of terminated process

I'm trying to figure out what the pid is of a process that sent the SIGCHLD signal, and I want to do this in a signal handler I created for SIGCHLD. How would I do this? I'm trying:
int pid = waitpid(-1, NULL, WNOHANG);
because I want to wait for any child process that is spawned.
If you use waitpid() more or less as shown, you will be told the PID of one of the child processes that has died — usually, that will be the only process that has died, but if you get a flurry of them, you might get one signal and many corpses to collect. So, use:
void sigchld_handler(int signum)
{
pid_t pid;
int status;
while ((pid = waitpid(-1, &status, WNOHANG)) > 0)
{
unregister_child(pid, status); // Or whatever you need to do with the PID
}
}
You can replace &status with NULL if you don't care about the exit status of the child.
Different systems document the return value of waitpid() slightly differently. However, POSIX is one of the more careful ones and says:
If wait() or waitpid() returns because the status of a child process is available, these functions shall return a value equal to the process ID of the child process for which status is reported. If wait() or waitpid() returns due to the delivery of a signal to the calling process, -1 shall be returned and errno set to [EINTR]. If waitpid() was invoked with WNOHANG set in options, it has at least one child process specified by pid for which status is not available, and status is not available for any process specified by pid, 0 is returned. Otherwise, -1 shall be returned, and errno set to indicate the error.

Does waitpid yield valid status information for a child process that has already exited?

If I fork a child process, and the child process exits before the parent calls waitpid, then is the exit status information that is set by waitpid still valid? If so, when does it become not valid; i.e., how do I ensure that I can call waitpid on the child pid and continue to get valid exit status information after an arbitrary amount of time, and how do I "clean up" (tell the OS that I am no longer interested in the exit status information for the finished child process)?
I was playing around with the following code, and it appears that the exit status information is valid for at least a few seconds after the child finishes, but I do not know for how long or how to inform the OS that I won't be calling waitpid again:
#include <assert.h>
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
int main()
{
pid_t pid = fork();
if (pid < 0) {
fprintf(stderr, "Failed to fork\n");
return EXIT_FAILURE;
}
else if (pid == 0) { // code for child process
_exit(17);
}
else { // code for parent
sleep(3);
int status;
waitpid(pid, &status, 0);
waitpid(pid, &status, 0); // call `waitpid` again just to see if the first call had an effect
assert(WIFEXITED(status));
assert(WEXITSTATUS(status) == 17);
}
return EXIT_SUCCESS;
}
Yes, waitpid will work after the child has exited. The OS will keep a child process' entry in the process table (including exit status) around until the parent calls waitpid (or another wait-family function) or until the parent exits (at which point the status is collected by the init process). This is what a "zombie" process is: a process that has exited by is still resident in the process table for exactly this purpose.
The process' entry in the table should go away after the first call to waitpid. I suspect the reason that in your example you seem to be able to call waitpid twice is simply because waitpid will not modify the status argument if pid no longer exists. So the first call should be working and filling in status, and the second call should be returning an error code and not changing status. You can verify this by inspecting the return values of the waitpid calls and/or using two different status variables.
The OS keeps terminated process in a zombie state until its parent (which might be the init if the original parent process terminated earlier) collects that exit status with wait(2) system call. So the answer is - the exit status of the process does not become invalid.
Yes.
From the man page:
A child that terminates, but has not
been waited for becomes a "zombie".
The kernel maintains a minimal set of
information about the zombie process
(PID, termination status, resource
usage information) in order to allow
the parent to later perform a wait to
obtain information about the child.

Resources