Determine pid of terminated process - 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.

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

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.

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.

No Child Process Error from waitpid() when waiting for process group

Writing my own toy shell, and have run into a bump trying to implement job control.
I am setting the process group of the child, both in the child and the parent with setpgid. My wait call is:
pid = waitpid(-pid, &status, 0)
However, waitpid returns -1 and perror says "No child process". However, it does seem to wait every time. Also, ps output looks right to mean when run from the shell. Since ps parent's process is kbsh like I would expect.
% ps -o pid,ppid,pgrp,session,tpgid,comm
Forking
In Parent: Setting process group to 20809 of process 20809 with setpgid
In Child Processes, pid of child process is 20809
in Child: Setting process group to 20809 of process 20809 with setpgid
Requesting that Process Group 20809 becomes the foreground process with tcsetpgrp
Waiting for job with process group 20809
PID PPID PGRP SESS TPGID COMMAND
12002 32573 12002 12002 20809 zsh
20808 12002 20808 12002 20809 kbsh
20809 20808 20809 12002 20809 ps
Wait Error: No child processes
Restoring Shell process group 20808 to forground
Anyone see what I am doing wrong? can post more code if need be...
I was ignoring sigchld, from the waitpid man page:
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
behaviour of setting SIGCHLD to
SIG_IGN unspecified.) Linux 2.6
conforms to this specification.
However, Linux 2.4 (and earlier) does
not: if a wait() or waitpid() call is
made while SIGCHLD is being ignored,
the call behaves just as though
SIGCHLD were not being ignored, that
is, the call blocks until the next
child terminates and then returns the
process ID and status of that child.
I found this thread while trying to implement a tiny shell for my computer science course, and figured I would share what worked for me. I was getting the following error:
Waitpid error: No child processes
In my case, I was using a wrapper provided by the Computer Systems:
A Programmer’s Perspective textbook. To fix my error, I changed Waitpid in csapp.c from
pid_t Waitpid(pid_t pid, int *iptr, int options)
{
pid_t retpid;
if ((retpid = waitpid(pid, iptr, options)) < 0)
unix_error("Waitpid error");
return(retpid);
}
to
pid_t Waitpid(pid_t pid, int *iptr, int options)
{
pid_t retpid;
retpid = waitpid(pid, iptr, options);
if (retpid < 0 && errno != ECHILD)
unix_error("Waitpid error");
return(retpid);
}
You don't have to set the process group ID. The child inherits the parent's pid as group by default.
When you wait, you need to wait for the parent's pid:
int main(int argc, char **argv)
{
pid_t pid;
int stat;
if ((pid = fork()))
{
printf("PARENT: %d | CHILD: %d\n", getpid(), pid);
waitpid(-getpid(), &stat, 0);
printf("DONE: %m\n");
}
else
{
printf("CHILD: %d\n", getpid());
sleep(3);
}
return 0;
}

Resources