This question already has an answer here:
Strange behavior with Child process
(1 answer)
Closed 1 year ago.
I'm currently testing a program that executes the Linux command echo Hello | wc using piping.
The parent process in main() spawns two child processes, the first one which executes echo Hello, and the second one which executes wc. These two processes communicate via a pipe.
However, when I call waitpid() on the two child processes, only the second process exits. The first process successfully runs execvp(), but hangs there.
Here is the supposed output of the code:
command 0
command 0 should exit
command 1
1 1 6
If I uncommment the line waitpid(id[0],&status,0);
then the output is
command 0
command 0 should exit
command 1
int test(pid_t id[])
{
int i;
int pipefd[2];
char *cat_args[] = {"echo","Hello", NULL};
char *grep_args[] = {"wc", NULL};
// make a pipe
pipe(pipefd);
for(i = 0; i < 2; i++){
id[i] = fork();
if (id[i] == -1){
printf("Unable to create child process");
fprintf(stderr,"fork() failed to spawn child process");
exit(1);
}
else if (id[i] == 0){
printf("command ");
printf("%d\n",i);
if (i == 0){
dup2(pipefd[0],STDIN_FILENO);
}
else if (i == 1){
dup2(pipefd[1],STDOUT_FILENO);
}
// Close pipes
close(pipefd[0]);
close(pipefd[1]);
if (i == 1){
execvp(*cat_args,cat_args); //exit(0) normally
}
else if (i == 0){
printf("command 0 should exit\n");
execvp(*grep_args,grep_args);
printf("command 0 exited\n"); //If first child exits, should print it out
}
}
}
return 0;
}
Here is the main function:
int main(int argc, char **argv)
{
pid_t id[2];
test(id);
int status;
// If this is uncommented, the piped Linux command is never ran
// and main() never exits
//waitpid(id[0],&status,0);
waitpid(id[1],&status,0); // This works
return 0;
}
Thank you.
The sub-process running wc hangs because the pipe is still readable despite the sub-process running echo Hello died. wc still waits for something to read through the pipe.
Why is the pipe still readable? Because you didn't close it in the parent process, the one that does the two forks. Therefore, the parent can still read and write to the pipe and the wc sub-process stays blocked on read() and never receives an EOF.
BTW, your printf("command 0 exited\n") is useless since nothing is executed after a successful execvp().
Related
I have parent process that creates two child processes. First child will write to pipe and second child will read from the pipe. After 5 seconds parent will terminate first child.(so its write end should be automatically closed, isn't it?). I need second child to terminate automatically, because it uses pipe and after first child is terminated, the pipe should be terminated too. My problem: how can i force child 2 to die immediately when child 1 i killed?(i don't need child 2 to print something after child 1 is dead, even if he still has any information in the pipe buffer to read). Should i use pipe2 instead of simple pipe?
void do_close(int fd){
if(close(fd) != 0){
perror("close");
exit(2);
}
}
void signalhandler(int signum){
fprintf(stderr, "f1 terminated!\n");
exit(1);
}
int main(){
pid_t f1 = -1, f2 = -1;
int pipefd[2];
if(pipe(pipefd) == -1){
perror("pipe");
exit(2);
}
f1 = fork();
if(f1 > 0){
f2 = fork();
}
if(f1 > 0 && f2 > 0){
do_close(pipefd[0]);
do_close(pipefd[1]);
sleep(5);
kill(f1, SIGTERM);
waitpid(f1, NULL, 0);
waitpid(f2, NULL, 0);
}
else if(f1 == 0){
signal(SIGTERM, signalhandler);
do_close(pipefd[0]);
while(1){
fflush(stdout);
write(pipefd[1], "Hello world!\n", 13);
sleep(1);
}
}
else if(f2 == 0){
do_close(pipefd[1]);
char *buf = (char*)malloc(13);
while(read(pipefd[0], buf, 13) > 0){
for(int i = 0; i < 13; i++){
printf("%c", buf[i]);
}
sleep(3);
}
}
return 0;
}
"Terminated" isn't the usual terminology for a pipe, and it might be causing a slight misunderstanding. The termination of the writing process isn't immediately "felt" by the reader if there is still data in the buffer.
To summarize the program, you have one process that writes to a pipe at a rate of 1 line per second for 5 seconds, then dies. Another process reads from the pipe at a rate of 1 line every 3 seconds until EOF or error, then exits.
The lines are small and fixed-size so there's no chance of reading an incomplete line.
Nothing in the program should cause an error on the pipe, so the second child process will read until EOF.
EOF on a pipe occurs when 2 conditions are met: there are no writers, and the buffer is empty. The death of the first child process accomplishes the first condition. The second condition is not immediately true, because at the 5 second mark, there have been 5 lines written to the pipe, and only 2 of them have been read (one at the start, and one after 3 seconds).
The second child process keeps reading, pulling in the remaining lines, and eventually exits after about 5*3=15 seconds.
(The timing isn't infinitely precise, so you aren't guaranteed to get exactly 5 lines written to the pipe. When I ran it I got 6.)
The program is below
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/wait.h>
#include<sys/types.h>
int main(int argc,char **argv)
{
int fds[2];
int error=pipe(fds);
if(error==-1)
{
perror("pipe");
return 1;
}
int ret=fork();
if(ret<0)
{perror("fork failed");
return 1;
}
else if(ret==0)
{
close(fds[0]);
close(1);
dup(fds[1]); char *argv[3];
argv[0]="ls";
argv[1]="-l";
argv[2]=NULL;
execvp(argv[0],argv);
}
else
{
int rett=fork();
if(rett<0)
{
perror("fork failed");
return 1;
}
else if(rett==0)
{
close(fds[1]);///
close(0);
dup(fds[0]);
char *argv[2];
argv[0]="wc";
argv[1]=NULL;
execvp(argv[0],argv);
}
else
{
waitpid(ret,NULL,7); /// wait for child process
waitpid(rett,NULL,7); //// wait for another child process
printf("Process %d finished\n",ret);
printf("Process %d finished\n",rett);
}
}
return 0;
}
There is the outcome below; why is printf is executing before the "wc"?
Process 7189 finished
Process 7190 finished
zyy#ubuntu:~$ 24 209 1125
Check your return values functions that can return errors. And don't use magic numbers instead of correct flags.
Your waitpid calls don't actually wait for any process to finish and just return errors. What does your magic number "7" mean? That's not what the documentation tells you to do. One of the bits in the number 7 is WNOHANG, which tells waitpid to not actually wait for the children to finish. Another bit in those flags is valid, but quite useless since you're not doing any ptrace on your processes. The third bit is invalid and will make waitpid error our immediately. I'm not going to tell you which is which because you're not supposed to do it this way at all. Read the manual page.
Another problem that you have is that you need to close the pipe in the parent process as well. wc will try to read data from the pipe (on its stdin) until the pipe is closed and it still remains open in the parent. The reason wc always prints after the parent has exited is that it's the parent exit that closes the pipe and that makes wc finish reading.
In the code below, do the forks actually run in parallel or one after another?
What is the meaning of wait(NULL) ?
(The program creates an n number of child processes, n is supplied via command line)
int main ( int argc, char *argv[] ) {
int i, pid;
for(i = 0; i < atoi(argv[1]); i++) {
pid = fork();
if(pid < 0) {
printf("Error occured");
exit(1);
} else if (pid == 0) {
printf("Child (%d): %d\n", i + 1, getpid());
exit(0);
} else {
wait(NULL);
}
}
}
They do run in parallel, up until the point that one of them waits.
wait(NULL) or more accurately wait(0) means wait until a state change in the child process. To find out about Unix / Linux C api calls, type man <function Name> on the command line. You'll need to get used to reading those pages, so better start now.
In your case
man wait
would have given you what you needed.
Since you've only fork(...)ed once, you have only one child. The parent waits until it changes state, and since the child's state during the fork is the same as the parent's state prior to the fork (that of running), the likely outcome is that the parent waits until the child dies. Then the parent will continue executing, but since it doesn't have much to do after the wait(...) call, it will quickly exit too.
I'm trying to write a program that will spawn an arbitrary number of child processes and pipe between them, similar to a command line pipeline. In my case I'm trying to do "ls -l | more" and output that to stdout, then have the parent continue executing more commands.
I have the following code as a minimal example:
int main (int argc, const char * argv[]) {
int fd[2];
pipe(fd);
chdir("/directory/with/lots/of/files");
// Create one child process for more
int pid = fork();
if (pid == 0) {
close(fd[1]);
int ret = dup2(fd[0],0);
if (ret < 0) perror("dup2");
char *argv[10];
argv[0] = "more"; argv[1] = NULL;
execvp("more", argv);
}
// Create another child process for ls
int pid2 = fork();
if (pid2 == 0) {
int ret = dup2(fd[1],1);
if (ret < 0) perror("dup2");
char *argv[10];
argv[0] = "ls"; argv[1] = "-l";
argv[2] = NULL;
execvp("ls", argv);
}
// wait for the more process to finish
int status;
waitpid(pid, &status, 0);
printf("Done!\n");
return 0;
}
Now, when I execute the program (enclosed in a main() function of course) what I end up with is more, which is expected. I'll hit "d" to page down more's output and "u" to go up, and it seems to work fine. But when I reach the bottom, instead of exiting like more does, it just leaves a blank line. Ctrl-C works to exit it but it exits the entire program, meaning the "Done!" line never gets printed. A movie is available here that illustrates what happens (note that at the very end I press Ctrl-C to get back to bash).
Any thoughts on this? I'm just trying to figure out how to change it to where instead of going to a blank line after more reaches the bottom, more quits and returns to the parent process so it can continue executing.
You need to close() at least the writing end of your pipe, otherwise more will never see EOF. For example:
...
// close parent's pipes
close(fd[0]);
close(fd[1]);
// wait for the more process to finish
int status;
waitpid(pid, &status, 0);
printf("Done!\n");
return 0;
}
I think it is because of the wait() function. Following the logic, your second child process outputs to the first child process, meaning it should end first than the second one.
In your wait function you are waiting for the first process to end, but you are not waiting for the second process. That means that if the second process does not send an EOF to the output ever, your first process won't end, i guess.
You can try to wait for the second process instead of the first and find out if that's the problem.
I have a homework to do that says the following:
Write a program in C that creates a child who will also create a child, make a pipe between the three processes, the fist process(father) will connect the second(child) and the child will connect with the third (child of the child). Our program should display the total number of system users who use bash as default shell. The result of the program should be identical to the "cat / etc / passwd | grep" / bin / bash $ "| wc-l"
I am confused with the first child and the method that we close the first pipe and open the second in the same time. If you reply me with the right code I 'll undestand it right once.
Thank you.
Here is what I 've wrote so far:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
main()
{
int pid, pid2;
int fd[2];
int fd2[2];
char *arg[3];
char *arg2[3];
char *arg3[3];
if (pipe(fd) == -1)
{
perror("pipe");
exit(1);
}
pid = fork();
if (pid == -1)
{
perror("fork");
exit(2);
}
if (pid == 0)
{
if (pipe(fd2) == -1)
{
perror("pipe");
exit(11);
}
pid2=fork();
if(pid2 == -1)
{
perror("fork 2");
exit(22);
}
if (pid2 == 0)
{
//i am child 2 (child of the child)
close (fd2[1]);
dup2 (fd2[0],0);
close (fd2[0]);
arg3[0] = "wc";
arg3[1] = "-l";
arg3[2] = NULL;
execvp("wc", arg3);
perror("execvp second child");
}
else
{
//i am child 1
close (fd[1]);
dup2(fd[0],0);
close (fd[0]);
close (fd2[0]);
dup2(fd2[1],1);
close (fd2[1]);
arg2[0] = "grep";
arg2[1] = "/bin/bash$";
arg2[2] = NULL;
execvp("grep", arg2);
perror("execvp first child");
}
}
else
{
//i 'm the father
close (fd[0]);
dup2(fd[1],1);
close (fd[1]);
arg[0] = "cat";
arg[1] = "/etc/passwd";
arg[2] = NULL;
execvp("cat", arg);
perror("execvp father");
}
}
Your program very nearly works. What's missing is
//i am child 2 (child of the child)
close (fd[1]);
close (fd[0]);
The pipe you called fd is for communicating between 'cat' and 'grep'. What's happening in your current code is that cat dumps the file and exits, closing its output. Grep reads all of that and waits for the EOF on its input. Since "child 2" still has the input side of the pipe open (it inherited it via fork), grep waits forever. If run your program and then type ps you should see a grep and a wc hanging around waiting to finish.
The other thing you would normally do when constructing a pipeline like this is arrange it so that the final task (in this case wc) is the one that the shell is waiting for. As written, when your program is run from the shell it will appear to finish when cat finishes, and the output of wc will print as if from a background task. If you arrange the pipe so that wc is under "i am child 1" then the shell will be waiting for wc instead.
Alternatively you could fork all of the three processes off and "child 1" would invoke wait() to wait for all of them before exiting. That waiting process would be like your own tiny shell.