I would like to check whether / when a child process has terminated in C on Unix. It's not supposed to be blocking, rather a short check in a loop.
My code:
pid_t pid = fork();
if (pid > 0)
// Parent Process
while (1) {
// Do a short check whether Child has already terminated if yes break the loop.
// Ik that it's possible to use waitpid(pid, &status, 0) but that blocks the whole loop until the child has terminated
}
if (pid == 0)
printf("child process born");
exit(0);
Thx in advance
The third argument to waitpid is a set of flags. If you pass WNOHANG to this argument, the function will return immediately if no children have yet exited.
You can then check if waitpid returned 0. If so, no child exited and you wait and try again.
while (1) {
pid_t rval = waitpid(pid, &status, WNOHANG);
if (rval == -1) {
perror("waitpid failed");
exit(1);
} else if (rval == 0) {
sleep(1);
} else {
break;
}
}
Traditional way is:
#include <errno.h>
#include <sys/types.h>
#include <signal.h>
#include <stdlib.h>
#include <stdio.h>
int exist(pid_t pid) {
return kill(pid, 0) > 0 || errno != ESRCH;
}
int main(int ac, char **av) {
while (--ac > 0) {
pid_t p = strtol(*++av, 0, 0);
printf("%d %s\n", p, exist(p) ? "exists" : "doesn't exist");
}
return 0;
}
It doesn't care about parent : child relation (whereas wait derivatives do), and works even if you don't have permission to affect the process.
Related
I run my C program on debian-linux ,the program is expected to output
$ ./kill_raise
Child(pid : 4877) is waiting for any signal
Parent kill 4877
but I just got
Parent kill 4877
the string in subprocess (Child(pid : %d) is waiting for any signal) is not print out,why?
and this is my program
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
int main(int argc, char *argv[])
{
pid_t pid;
int res;
pid = fork();
if (pid < 0)
{
perror ("Fork error\n");
exit(0);
}
if (pid == 0)
{
printf ("child(pid:%d) is waiting for signal\n", getpid());
raise(SIGSTOP);
exit(0);
}
else
{
if ((waitpid(pid, NULL, WNOHANG)) == 0)
{
if ((res = kill(pid, SIGKILL)) == 0)
{
printf ("parent kill %d\n", pid);
}
}
waitpid(pid, NULL, 0);
exit(0);
}
}
You're hitting a race condition. The parent is executing the kill before the child can execute its printf. Remove WNOHANG so the parent actually waits for the child to stop. Also, you need to add WUNTRACED so that waitpid reports the stop (by default, it will only report termination). Finally, you shouldn't be testing wait()'s return value against 0 in this case.
So I wrote this code on C. I created a father, that has two child processes, and one becomes zombie. After one second it exits, and the father, that was waiting for him, finishes. The other child process remains orphan, and then finishes. My question is, what happens if I change the wait for waitpid.
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
int main() {
pid_t pid;
int status, value;
pid = fork();
if (pid > 0) { // Father
pid = fork();
if (pid > 0) { // Father
wait(&status);
value = WEXITSTATUS(status);
if (value == 2)
printf("Child 2");
else if (value == 3)
printf("Child 1");
} else if (pid == 0) { //Child 2 - Orphan
sleep(4);
exit(2);
} else {
exit(1);
}
} else if (pid == 0) { // Child 1 - Zombie
sleep(1);
exit(3);
} else {
printf("Error al ejecutar el fork");
exit(1);
}
return 0;
}
Quoting wait/waitpid,
The waitpid() function is provided for three reasons:
To support job control
To permit a non-blocking version of the wait() function
To permit a library routine, such as system() or pclose(), to wait for its children without interfering with other terminated children for which the process has not waited
and
The waitpid() function shall be equivalent to wait() if the pid argument is (pid_t)-1 and the options argument is 0. Otherwise, its behavior shall be modified by the values of the pid and options arguments.
So the behavior of waitpid() depends on its arguments.
Generalities and explanations about the functioning of my program
I wrote a program whose aim is to create processes until it can't do it anymore (id est : it must glue the OS and completely fill the processes table). However, when OS is glued, a message like "fork can't be done anymore" appears, and all the processes can be killed by the final user thanks to CTRL+Z.
My program contains two important processes : the main one, which creates the second. The first is called "MAIN_P" in my code and the latter "P_ROOT". P_ROOT's aim is to fork until he can't do it anymore. When a fork error appears (id est : when my program has succeeded !), the final user can send a CTRL-Z signal to MAIN_P, which will kill P_ROOT and its children.
I precise that P_ROOT and its children have the same GPID (inheritance). But the latter is different than the MAIN_P's one, of course (setsid applied to P_ROOT).
My problem
When I launch my program, it fork the first child, which fork its children until the OS is glued (ie. : until the processes table is completely filled). The only problem is that I can't CTRL + Z in my console to stop it... And of course, if I just exit the terminal, it doesn't kill all these processes (and others continue to be forked moreover).
Thus, I don't recommend you to execute it...
What is wrong with my code ?
Source
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/resource.h>
int main(int argc, char* argv[]) {
pid_t pid_first_child = 0;
if((pid_first_child = fork()) == -1) { // We `fork` the first child, which will always `fork` (more precisely : until the OS is glued, processes table completely filled)
perror("fork");
exit(EXIT_FAILURE);
}
if(pid_first_child == 0) { // BEGINNING OF <FirstChild>'S CODE
pid_t pid_session_leader = 0;
if((pid_session_leader = setsid()) == -1) { // FirstChild is its process group's leader
perror("setsid");
exit(EXIT_FAILURE);
}
if(setpriority(PRIO_PGRP, pid_session_leader, -10) == -1) { // The priority of FirstChild (which is the group's leader)
perror("setpriority");
exit(EXIT_FAILURE);
}
unsigned children_counter = 0;
pid_t pid_calculation_process = 0;
while((pid_calculation_process = fork()) != -1) { // Now, FirstChild will `fork` until the limit ! When the limit is reached, -1 is returned : there isn't anymore `fork` and we exit the loop
if(pid_calculation_process > 0) {
children_counter++;
fprintf(stdout, "%u\n", children_counter);
} else { // BEGINNING OF <FirstChild's children>'s CODE (Why ? Consequently to the `while` and the `if` !)
float j=1;
while(1) { // Children can't die
int i = 0;
for(; i < 1000; i++) {
j /= 3;
}
usleep(1000);
}
} // END OF <FirstChild's children>'s CODE (FirstChild's children)
}
perror("fork"); // It's what we wanted ! This message will tell the user "OS is glued, program worked correctly"
exit(EXIT_SUCCESS); // `EXIT_SUCCESS` ? Because we reached the limit !
} // END OF <FirstChild>'S CODE
}
Comments:
To reach your fork() limit quickly, you have to make sure that each forked process doesn't consume too much resources. Your forked processes are spinning in the for-loop and taking up too much resources. If you remove the for-loop, you will hit your process limit more quickly since the processes will be blocked on the sleep() call instead of spinning.
You don't need the wait loop to wait for the processes to complete after the fork() error. That will happen automatically.
The updated source:
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
int main(int argc, char* argv[]) {
// This (first !) child, say "P_ROOT", will create its own children, which will glue the system (thus, MAIN_P is freed
int p_root = fork();
if(p_root == -1) {
perror("fork");
exit(EXIT_FAILURE);
}
// P_ROOT's PGID will be set to its PID (so we have : P_ROOT's PGID != MAIN_P's PGID)
if (p_root == 0) {
if(setpgid(p_root, p_root) == -1) {
perror("setpgid");
exit(EXIT_FAILURE);
}
int p_root_number_of_created_children = 0;
pid_t p_root_child = 0;
while((p_root_child = fork()) != -1) { // P_ROOT forks until it can't do it anymore...
if(p_root_child != 0) {
p_root_number_of_created_children++;
} else {
#ifdef CONSUME_RESOURCES
int i = 0;
while(i < 1000000000000000000) {
i++;
}
#endif
sleep(6000);
exit(EXIT_FAILURE);
}
}
// NOW it's impossible to create new child processes
perror("fork");
fprintf(stdout, "\nImpossible to create more children. Their number is : %d\n", p_root_number_of_created_children);
exit(EXIT_SUCCESS);
} else {
printf("Waiting, top level, root = %d\n", p_root);
wait(NULL); // MAIN_P waits for P_ROOT
char cmd = 0;
if(scanf("%c", &cmd) < 0) {
perror("scanf");
exit(EXIT_FAILURE);
}
if(cmd == '\n' && kill(-p_root, SIGKILL) == -1) {
perror("kill");
exit(EXIT_FAILURE);
}
exit(EXIT_SUCCESS);
}
}
I have the following code in C:
if ((childpid = fork()) == 0) {
if (execvp(argv[0], argv) < 0) {
//execute failed
exit(1);
}
} else if (childpid < 0) {
//fork failed
} else {
//if execvp failed don't do anything here
//else do something
}
What I want is:
I enter a command.
If it is not executable it should not do anything but wait for my next entered command.
If it is executable it should do some things in the parent process.
If I enter e.g. sleep 1m it should execute it in my child process, do things in the parent process and should be still able to execute more jobs (this works fine). But when I execute something like abcdef (invalid command) it does the stuff in my parent process anyway.
Can someone tell me how the code should look like?
I also tried the following:
void signalHandler(int signal)
{
if (signal==SIGCHLD) {
printf("Child ended\n");
wait(NULL);
}
}
//in main
signal(SIGCHLD,signalHandler);
//...
if ((childpid = fork()) == 0) {
if (execvp(t_argv[0], t_argv) < 0) {
kill(getppid(),SIGCHLD);
}
}
Is this correct?
This way I get an error afterwards (when it's finished).
waitpid(childpid, &status, WNOHANG)
tells me it finished with an error (-1).
One possible solution is to use a pair of anonymous pipes, where the child process writes in the write-end of the pipe any status it needs to pass on to the parent. Then in the parent you check the read-end of the pipe, if you don't receive anything before the child-process exits then everything was okay and the child process successfully executed the program.
If the parent does receive anything before the child process exits, then it means that the exec call failed.
One possible solution is to terminate abnormally with a signal (for example, SIGUSR1) and check for that in the parent. This assumes that whatever program you execute in the child never terminates with SIGUSR1 - a reasonable assumption in most cases, I'd say. The parent can then check the termination status of the child.
#include <unistd.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[]) {
pid_t childpid;
if ((childpid = fork()) == 0) {
if (execvp(argv[1], &argv[1]) < 0) {
raise(SIGUSR1);
}
} else if (childpid < 0) {
perror("fork()");
exit(EXIT_FAILURE);
} else {
int term_status;
if (wait(&term_status) < 0) {
perror("wait()");
exit(EXIT_FAILURE);
}
if (WIFSIGNALED(term_status) && WTERMSIG(term_status) == SIGUSR1) {
printf("execvp failed\n");
} else {
printf("success\n");
}
}
return 0;
}
Side note: you probably want execvp(argv[1], &argv[1]), because execvp(argv[0], argv) will execute this same program over and over.
Again, this works as long as the process executed by execvp(2) never terminates with SIGUSR1. Notice that if the process executed by execvp(2) terminates with SIGSEGV or other abnormal termination condition, it is still seen as success by the parent.
In Linux C, how do you read the last programs exit status.
For example:
true; echo $?
Will show a 0 for success. But I'd like to replace that with a C program:
true; ./echo_exit_status
How/where is the last exit code $? available to a C program?
The last command's exit status is not accessible to a subsequently-executed program (absent insanely-horrible hackery -- attaching to the parent shell with a debugger or somesuch).
You could write a shell function or helper that exported this value into the environment -- but no solution is possible which does not require the shell's involvement.
You'd have to know the pid but otherwise it's http://linux.die.net/man/2/waitpid
More info from the man page
WIFEXITED(status)
returns true if the child terminated normally, that is, by
calling exit(3) or _exit(2), or by returning from main().
WEXITSTATUS(status)
returns the exit status of the child. This consists of the
least significant 8 bits of the status argument that the child
specified in a call to exit(3) or _exit(2) or as the argument
for a return statement in main(). This macro should be
employed only if WIFEXITED returned true.
Here is an example from the
#include <sys/wait.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
int
main(int argc, char *argv[])
{
pid_t cpid, w;
int status;
cpid = fork();
if (cpid == -1) {
perror("fork");
exit(EXIT_FAILURE);
}
if (cpid == 0) { /* Code executed by child */
printf("Child PID is %ld\n", (long) getpid());
if (argc == 1)
pause(); /* Wait for signals */
_exit(atoi(argv[1]));
} else { /* Code executed by parent */
do {
w = waitpid(cpid, &status, WUNTRACED | WCONTINUED);
if (w == -1) {
perror("waitpid");
exit(EXIT_FAILURE);
}
if (WIFEXITED(status)) {
printf("exited, status=%d\n", WEXITSTATUS(status)); //this line will return the exit status, whether it was 1 or 0
} else if (WIFSIGNALED(status)) {
printf("killed by signal %d\n", WTERMSIG(status));
} else if (WIFSTOPPED(status)) {
printf("stopped by signal %d\n", WSTOPSIG(status));
} else if (WIFCONTINUED(status)) {
printf("continued\n");
}
} while (!WIFEXITED(status) && !WIFSIGNALED(status));
exit(EXIT_SUCCESS);
}
}