Replace Parent Process with Child Process After Death - c

It is my objective to make a program with a parent and child process, and when either one them is killed, they are replaced. The part I am struggling with is the case of the parent's death. In this case, the child must step up to become the new parent, and then fork() a child of its own. When I send a SIGKILL to the parent process, my entire program seems to end abruptly, and since I cannot handle a SIGKILL, I am unsure of how to do this properly.
Is there a way to keep the child running in order to become the new parent process?
Thanks

Normally the child you fork shouldn't be killed when it's parent is killed, unless you do something like: How to make child process die after parent exits?
If the parent is killed, the children become a children of the init process. You probably saw on terminal that the process returns immediately after you send KILL to parent. That's because the sub-bash is waiting only on the parent's PID. But the child is actually running elsewhere.
Here is a example to show it:
#!/usr/bin/env python
# test_parent_child_kill.py
import os
import time
def child():
print "Child process with PID= %d"%os.getpid()
time.sleep(20)
def parent():
print "Parent process with PID= %d"%os.getpid()
newRef=os.fork()
if newRef==0:
child()
else:
print "Parent process and our child process has PID= %d"%newRef
time.sleep(20)
parent()
Then within sleep period:
user#mac:/tmp|⇒ python test_parent_child_kill.py
Parent process with PID= 17430
Parent process and our child process has PID= 17431
Child process with PID= 17431
user#mac:/tmp|⇒ kill 17430
user#mac:/tmp|⇒ ps -ef | grep 17431
503 17431 1 0 9:30PM ttys000 0:00.00 /usr/local/Cellar/python/2.7.10_2/Frameworks/Python.framework/Versions/2.7/Resources/Python.app/Contents/MacOS/Python test_parent_child_kill.py
So the child is actually still alive.
--Edit--
Why when the parent is killed my program exits back to the shell?
Bash invokes the command also via folk/exec via something like this:
childPid = fork();
if (childPid == 0){
executeCommand(cmd); //calls execvp
} else {
if (isBackgroundJob(cmd)){
record in list of background jobs
} else {
waitpid (childPid);
}
}
Since from bash's point of view, the parent of your program is the child, it would return to prompt input when it returns from waitpid(childPid).
Is there a way to stay within the program and continue functioning as it was but with a new parent?
It might be a bit difficult if you want to "re-attach", but it's not impossible:
Attach to a processes output for viewing
https://unix.stackexchange.com/questions/58550/how-to-view-the-output-of-a-running-process-in-another-bash-session
Reference:
http://www.cs.cornell.edu/Courses/cs414/2004su/homework/shell/shell.html

Related

How to get the return value of a child process in C after parent process ends?

I'm new so bear with me. I compiled an exe from a c file which uses fork(). I have the parent process return a value, let's say "A," and the child process returns "B." When the program ends and I "echo $?" the last return, I get "A."
I do have waitpid() implemented for the child to finish. But is there a way to output "B"(whatever that value is) from the shell after the process has finished?

what's the difference between all fork function return values?

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<sys/wait.h>
#define LG_LIGNE 256
int main(void){
char ligne[LG_LIGNE];
while(1){//afficher un symbole d'invite(prompt)
fprintf(stderr, "-->");
//lire une ligne de commandes
if(fgets(ligne,LG_LIGNE,stdin)==NULL)
break;
//supprimer le retour chariot final
ligne[strlen(ligne)-1]='\0';
//lancer un processeur
if(fork()==0){
//processus fils
//executer la commande
execlp(ligne, ligne, NULL);
//msg d'erreur si on echoue
perror(ligne);
exit(EXIT_FAILURE);
}
else{//process père
//attendre la fin de son fils
waitpid(-1,NULL,0);
//et reprendre la boucle
}
}
fprintf(stderr,"\n");
return EXIT_SUCCESS;
}
I understand what each return value means but I didn't quite understand it here, what being in the child/father process technically means, and I couldn't see the use of waitpid, what will happen if I didn't use it?
Fork() creates a copy of the process's memory and state, and spawns a child process to run in it. Both the child and parent run in their own process, with their own copy of the address space and virtual memory. So how can you tell which is the parent and which is the child since both tasks resume execution after the fork() call with identical state and memory?
You can tell which is the parent and which is the child based on the return code from fork(). If the return value is zero, then you are the child process, and this process usually tests the return code for zero, and branches off to do the child's work.
Meanwhile, the parent also returns from the same fork() call but with the return code set to the process id of its child (which is non-zero). The parent may choose to save this process id, but it is only really useful if the parent is creating multiple children and wants to track them through their process ids.
Before the parent finishes, it typically waits for its child processses to complete first. This is what the waitpid call does. You can pass in the child process's id, in which case the waitpid call does not return until that child process completes. Passing -1 into waitpid tells it to wait until ONE of your child tasks completes.
The return code from waitpid contains the process id of the child task that completed. For more information, this has already been answered here https://stackoverflow.com/a/21249082/6693299
For a complete explanation of what your program does, read the excellent detailed description by H.S. that answers the same question here https://stackoverflow.com/a/46741392/6693299
Your first question -
what being in the child/father process technically means
The fork() creates a child process by duplicating the calling process.
The process that invoked fork() is the parent process and the newly created process is the child process.
So fork() splits a process in two, and returns 0 to the child process and the PID of the child process to the parent process, or -1 if the fork failed.
The child process and the parent process run in separate memory spaces. At the time of fork() both memory spaces have the same content.
There is concept called Copy on Write, its good to have knowledge about it-
Copy on Write is an optimization where the page tables are set up so that the parent and child process start off sharing all of the same memory, and only the pages that are written to by either process are copied when needed.
If the process does not modify any memory and immediately executes a new process, replacing the address space entirely. Thus, it would be wasteful to copy all of the process's memory during a fork, and instead the copy-on-write technique is used.
For example, in your program you have immediately calling execlp after fork:
if(fork()==0){
//processus fils
//executer la commande
execlp(ligne, ligne, NULL);
Your second question -
I couldn't see the use of waitpid, what will happen if I didn't use it?
To explain this, I have modified your program and added statement to print parent and child process pid's and commented out this statement-
waitpid(-1,NULL,0);
Output of program is -
parent process pid : 22325
-->ls
child process pid : 22326
< here the output of ls command >
Now, if we see the output of ps command with grep'ed parent process id -
# ps -eaf | grep 22325
root 22325 21555 0 10:39 pts/4 00:00:00 ./a.out
root 22326 22325 0 10:39 pts/4 00:00:00 [ls] <defunct>
root 22339 21644 0 10:39 pts/5 00:00:00 grep 22325
Here, in the output the first column is UID, second is PID and third id PPID (parent pid).
You can see the child process (pid - 22326) is marked as < defunct >.
A "defunct" process (also referred to as "zombie" process) is a process which has finished execution, it will have an exit status to report to its parent process. Because of this last little bit of information, the process will remain in the operating system’s process table as a zombie process, indicating that it is not to be scheduled for further execution, but that it cannot be completely removed (and its process ID cannot be reused) until it has been determined that the exit status is no longer needed.
And here comes the use of waitpid() -
The wait() and waitpid() functions shall obtain status information pertaining to one of the caller's child processes.
waitpid() suspends the calling process until the system gets status information on the child. If the system already has status information on an appropriate child when waitpid() is called, waitpid() returns immediately. waitpid() is also ended if the calling process receives a signal whose action is either to execute a signal handler or to end the process.
For other details on waitpid() like syntax (status, options) and return value you can check its man page.
If I uncomment the waitpid() in your program and compile and run it, the output -
parent process id : 23069
-->ls
child process id : 23070
<here the output of ls command>
Now, if we see the output of ps command with grep'ed parent process id -
# ps -eaf | grep 23069
root 23069 21555 0 10:51 pts/4 00:00:00 ./a.out
root 23108 21644 0 10:51 pts/5 00:00:00 grep 23069
There is no zombie process. The child process which runs ls command has been finished and the parent process read its exit status.
Hope this answer your both the questions.

stdin control is not given to the child after the parent death

Considering the following code, when the parent is terminated, the stdin control is taken back by the shell and there is no way to see that the child process is running except through the "ps -e" command.
Is there is any way to give the stdin control to the child process before the parent dies?
I read some similar topics here, but non of them gives a solution to this issue. One has suggested to use "#cat | ./a.out" as a work around but I want a code level solution if there is any.
pid = fork();
if( pid == 0)
{
while(1);
}
else
{
//wait(&childstatus);
printf("Hello From Parent\n");
}
Yes, this is the way shell works. If you don't want the shell take active process group, keep the parent alive.

Separate I/O for child process after fork()

I am trying to implement an application on Linux using C, and I have a requirement that I need to do I/O separately on my child & parent process. Here is what I am looking for
User runs the application, the parent process spawns 3 child processes.
Each of the child process will spawn a thread that waits for the user input.
There should be an intuitive method by which the user can specify which of the child process he is interacting with.
Ideally I would like if each of the child processes is executed on different terminal, that way it is very clear to the user with whom he is interacting.
I saw a similar question in Executing child process in new terminal, but the answer is not very clear regarding the steps involved. It seems to suggest that it can be done by execing the xterm like this xterm -e sh -c, but it is not confirmed. I would also want to setup some IPC between the parent <--> child & child <--> child process as well, so if I launch the child process in a new terminal by execing xterm, who is the child of my parent process? Is it xterm? If so, the code that I actually want to execute in my child process, will it get executed as a child of xterm?
Assume that you have already spawned the three child processes and that you run your parent on tty1.
tty1: Now contains all the diagnostics information
tty2: Child process 1
tty3: Child process 2
tty4: Child process 3
tty5: User input
So each child process will read from its tty as if it were a file (note: requires root permissions). To give input to, say, child process 2, go to tty5 and type in this command:
cat - >/dev/tty3
Then type in the input to your program, and then press Ctrl-D. Your child process 2 should now recieve that input.
EDIT You do not need to actually run the child processes on different ttys. It is only required to run them with root permissions and then read and write from those tty numbers, just as you would read from stdin and write to stdout. Sorry for the confusion.

why SIGUSR1 kills my dd child process?

Hi I have a simple function where i create a child process and parent process.
The child process suppose to run dd if=/some/file of=/somedisk.
The parent process suppose to run in a loop (until the child exists) and send signal SIGUSR1 which is forcing the dd in child process to report progress data.
Of course i have pipes where i redirect stdio and stderr from the child to the parent. (this i use in other functions and it works fine)
The problem i have is that:
1. I don't get anything on my stderr;
2. As soon as i send SIGUSR1 the process exits somehow.
if(my_pid>0) //The parent part
{
close(FD_pipe_stdout[1]);// Parent process closes output side of the pipe
close(FD_pipe_stderr[1]);// Parent process closes output side of the pipe
while(0==waitpid(my_pid, &my_pid_status, WNOHANG))
{
kill(my_pid, SIGUSR1);
sleep(1);
//read(FD_pipe_stderr[0], pipe_error,PIPE_MAX_SIZE); // Read in a string from the stderror
//puts(pipe_error);
}
puts("going out");
read(FD_pipe_stdout[0], pipe_output,PIPE_MAX_SIZE); // Read in a string from the pipe's input side
close(FD_pipe_stdout[0]);//on the end close the other side of pipe
close(FD_pipe_stderr[0]);//on the end close the other side of pipe
}
else
{ // The child part
close(FD_pipe_stdout[0]);/* Child process closes input side of the pipe */
close(FD_pipe_stderr[0]);/* Child process closes input side of the pipe */
dup2(FD_pipe_stdout[1],1); //redirect the stdout(1) to the fd_pipe and then close the sdtout
dup2(FD_pipe_stderr[1],2);//redirect also stderr to the pipe
system(dd if=/image.img of=/dev/sda);
close(FD_pipe_stdout[1]); //on the end close the other side of pipe
close(FD_pipe_stderr[1]); //on the end close the other side of pipe
exit(0);
}
I see on the screen that the parent is going out from the while loop and i don't understand why.
Thanks in advance
system() creates a child process to run the specified command, so you really have three processes here:
The parent process (the one with the loop)
The child process (the one that calls system()
The dd process
You're signaling the child process instead of the dd process. SIGUSR1 by default causes a process to exit, so you're killing the child process.
To fix this, you could run dd using one of the exec functions, instead of calling system():
{ // The child part
close(FD_pipe_stdout[0]);
close(FD_pipe_stderr[0]);
dup2(FD_pipe_stdout[1],1);
dup2(FD_pipe_stderr[1],2);
execlp("dd", "dd", "if=/image.img", "of=/dev/sda", NULL);
perror("exec failed");
exit(1);
}
Now you only have two processes, because the child process becomes the dd process. When the parent signals the child, the signal will go to dd.
Note that there is a race condition here. The parent process may send a SIGUSR1 signal before dd starts and sets up its signal handler. To be robust, you should handle this somehow.

Resources