why SIGUSR1 kills my dd child process? - c

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.

Related

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.

How can I suspend/kill a child process from the child using keyboard?

In my shell program I am reading from the keyboard via Readline:
rl_bind_keyseq ("\\C-c", readline_func); // ctrl + c
int readline(...){
// handle keyboard controls
}
This works when I'm in my outside loop, but once I get a long command such as 'wc' where I'm in my child process, ctrl+c is not being picked up, I get ^C in the terminal. The keystrokes are defined before I forked.
I'm interested in killing the process, so when I'm running 'wc' and ctrl+c is detected, I'll kill(pid, sigint)
my parent process is waiting for the child via:
waitpid(pid, &child_status, 0);
Also am I able to call kill from within the child to get it to stop/pause?

Replace Parent Process with Child Process After Death

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

How to create proxy process which redirects stdin to child?

I want to make a proxy process which opens the real one.
Like if I rename linux's espeak to espeak_real and my app to espeak.
espeak opens espeak_real and I get the output.
I want to make possible to:
Prints it's STDIN to the console
Prints it's STDIN to another process's STDIN
Prints the second process's STDOUT
I'm trying to do it in C (I guesss it's possible with raw bash too).
I don't exactly understand what you do, but it seems like a combination of fork, exec, pipe and dup2 should do it.
app can use pipe to get a pair of file descriptors, connected with a pipe (what's written into one is read from the other).
Then it can fork, and the child can exec app_real.
But between fork and exec, dup2 can be used to change any file descriptor you want to 0,1 and 2 (but close the real 0,1,2) first.
Short code example:
int pipe_fds[2];
pipe(pipe_fds);
if (fork()==0) {
// Child
close(fds[1]); // This side is for the parent only
close(0); // Close original stdin before dup2
dup2(fds[0],0); // Now one side of the pipe is the child's stdin
close(fds[0]); // No need to have it open twice
exec(...);
} else {
// Parent
close(fds[0]); // This side is for the child only
write(fds[1],data,len); // This data goes to the child
}

Sending data from parent to child process and vice versa in C

I've made a program that create 5 pipes and fork 5 processes in a loop. I've managed to send data from the parent to each child processe, when each child processe is overlapped by another program. Each loop is done in the following manner
(parent.c):
// Child process - reads from pipe
if (childpid == 0) {
dup2(fd[0], 0); // replace stdin with pipe input
execv("program", arguments);
} else { // Parent process - writes to pipe
write(fd[1], buffer, strlen(buffer)+1);
}
So now i'm able to get the data that is sent from the parent to the pipe, by reading from STDIN_FILENO in the program the childs executes with execv(...).
Like this (program.c):
char *buffer = (char *)malloc(50);
read(STDIN_FILENO, buffer, 50);
My problem however is, how can I send data back to the parent? I was thinking of something like replacing stdout with the pipe output by using dup2 again, but I can't get it to work. I realise this have to be done before using execv(...) at least.
I'm not sure if that explanation was sufficient so I can make a little image with text : )
This is how it is right now:
Parent -> Pipe
Pipe -> Child process 1
Pipe -> Child process 2
Pipe -> ...
Pipe -> Child process 5
I want it to be like this.
Parent -> Pipe
Pipe -> Child process 1
Pipe -> Child process 2
Pipe -> ...
Pipe -> Child process 5
Child process 1 -> Parent
Child process 2 -> Parent
...
Child process 5 -> Parent
Thankful for help!
Pipes are unidirectional, not bidirectional, so a generic solution is to create five more pipes (gak!) for the return data. The parent then needs to use select() system call to know which of the return pipes have data ready for reading.
Oh, and I would have written
dup2(fd[0], 0); // replace stdin with pipe input
should be
dup2(fd[0], 0); // replace stdin with output from pipe
and vice versa.
Like the other replies say, there are other communication methods that may be more appropriate, depending on what you are trying to achieve.
See this unix programming faq here, look at question 1.5, maybe using a memory map to allow the parent/child processes to read/write from on both ends...There is an excellent guide to understanding IPC here.
I looked around on the internet for a bit. It seems that you can use pipes only to communicate one way. Which makes sense - input from stdin and output to stdout. If you want to have two way communication between your parent and child processes, use sockets. There are other methods to achieve two way IPC. I suggest that you learn more about inter process communication.
All the best.
If you want to keep using pipes, then make one group of FDs for child->parent communication and one for parent->child communication. So instead of having int fd[2], you have int toParent[2], toChild[2]. Instead of dup2(fd[0], 0), you have dup2(toChild[0], 0); dup2(toParent[1], 1).
And don't just dup, but close the FDs you aren't using.

Resources