I'm trying to better understand pipes between a parent and multiple child processes, so I made a simple program that spawns two child processes, gives them a value (i), has them change that value, and then prints it out.
However it's not working, as the program prints i as if it was unaltered, and prints the altered i inside the children. I'm obviously not sending the i variable through correctly, so how should I fix this?
int main ( int argc, char *argv[] ){
int i=0;
int pipefd[2];
int pipefd1[2];
pipe(pipefd);
pipe(pipefd1);
pid_t cpid;
cpid=fork();
cpid=fork();
if (cpid ==0) //this is the child
{
close(pipefd[1]); // close write end of first pipe
close(pipefd1[0]); // close read end of second pipe
read(pipefd[0], &i, sizeof(i));
i=i*2;
printf("child process i= %d\n",i); //this prints i as 20 twice
write(pipefd1[1],&i, sizeof(i));
close(pipefd[0]); // close the read-end of the pipe
close(pipefd1[1]);
exit(EXIT_SUCCESS);
}
else
{
close(pipefd[0]); // close read end of first pipe
close(pipefd1[1]); // close write end of second pipe
i=10;
write(pipefd[1],&i,sizeof(i));
read (pipefd1[1], &i, sizeof (i));
printf("%d\n",i); //this prints i as 10 twice
close(pipefd[1]);
close(pipefd1[0]);
exit(EXIT_SUCCESS);
}
}
The main problem is that you're not creating two child processes. You're creating three.
cpid=fork();
cpid=fork();
The first fork results in a child process being created. At that point, both the child and the parent execute the next statement, which is also a fork. So the parent creates a new child and the first child also creates a child. That's why everything is printing twice.
You need to check the return value of fork immediately before doing anything else.
If you were to remove one of the fork calls, you'd still end up with the wrong value for i in the parent. That's because it's reading from the wrong end of the pipe.
The child is writing to pipefd1[1], but the parent is then trying to read from pipefd1[1] as well. It should be reading from pipefd1[0].
EDIT:
Removed erroneous sample code which assumed pipes are bidirectional, which they are not.
Related
I am confused as to how to properly use close to close pipes in C. I am fairly new to C so I apologize if this is too elementary but I cannot find any explanations elsewhere.
#include <stdio.h>
int main()
{
int fd[2];
pipe(fd);
if(fork() == 0) {
close(0);
dup(fd[0]);
close(fd[0]);
close(fd[1]);
} else {
close(fd[0]);
write(fd[1], "hi", 2);
close(fd[1]);
}
wait((int *) 0);
exit(0);
}
My first question is: In the above code, the child process will close the write side of fd. If we first reach close(fd[1]), then the parent process reach write(fd[1], "hi", 2), wouldn't fd[1] already been closed?
int main()
{
char *receive;
int[] fd;
pipe(fd);
if(fork() == 0) {
while(read(fd[0], receive, 2) != 0){
printf("got u!\n");
}
} else {
for(int i = 0; i < 2; i++){
write(fd[1], 'hi', 2);
}
close(fd[1]);
}
wait((int *) 0);
exit(0);
}
The second question is: In the above code, would it be possible for us to reach close(fd[1]) in the parent process before the child process finish receiving all the contents? If yes, then what is the correct way to communicate between parent and child. My understanding here is that if we do not close fd[1] in the parent, then read will keep being blocked, and the program won't exit either.
First of all note that, after fork(), the file descriptors fd would also get copied over to the child process. So basically, a pipe acts like a file with each process having its own references to the read and write end of the pipe. Essentially there are 2 read and 2 write file descriptors, one for each process.
My first question is: In the above code, the child process will close
the write side of fd. If we first reach close(fd[1]), then the parent
process reach write(fd[1], "hi", 2), wouldn't fd[1] already been
closed?
Answer: No. The fd[1] in parent process is the parent's write end. The child has forsaken its right to write on the pipe by closing its fd[1], which does not stop the parent from writing into it.
Before answering the second question, I fixed your code to actually run it and produce some results.
int main()
{
char receive[10];
int fd[2];
pipe(fd);
if(fork() == 0) {
close(fd[1]); <-- Close UNUSED write end
while(read(fd[0], receive, 2) != 0){
printf("got u!\n");
receive[2] = '\0';
printf("%s\n", receive);
}
close(fd[0]); <-- Close read end after reading
} else {
close(fd[0]); <-- Close UNUSED read end
for(int i = 0; i < 2; i++){
write(fd[1], "hi", 2);
}
close(fd[1]); <-- Close write end after writing
wait((int *) 0);
}
exit(0);
}
Result:
got u!
hi
got u!
hi
Note: We (seemingly) lost one hi because we are reading it into same array receive which essentially overrides the first hi. You can use 2D char arrays to retain both the messages.
The second question is: In the above code, would it be possible for us
to reach close(fd[1]) in the parent process before the child process
finish receiving all the contents?
Answer: Yes. Writing to a pipe() is non-blocking (unless otherwise specified) until the pipe buffer is full.
If yes, then what is the correct
way to communicate between parent and child. My understanding here is
that if we do not close fd[1] in the parent, then read will keep being
blocked, and the program won't exit either.
If we close fd[1] in parent, it will signal that parent has closed its write end. However, if the child did not close its fd[1] earlier, it will block on read() as the pipe will not send EOF until all the write ends are closed. So the child will be left expecting itself to write to the pipe, while reading from it simultaneously!
Now what happens if the parent does not close its unused read end? If the file had only one read descriptor (say the one with the child), then once the child closes it, the parent will receive some signal or error while trying to write further to the pipe as there are no readers.
However in this situation, parent also has a read descriptor open and it will be able to write to the buffer until it gets filled, which may cause problems to the next write call, if any.
This probably won't make much sense now, but if you write a program where you need to pass values through pipe again and again, then not closing unused ends will fetch you frustrating bugs often.
what is the correct way to communicate between parent and child[?]
The parent creates the pipe before forking. After the the fork, parent and child each close the pipe end they are not using (pipes should be considered unidirectional; create two if you want bidirectional communication). The processes each have their own copy of each pipe-end file descriptor, so these closures do not affect the other process's ability to use the pipe. Each process then uses the end it holds open appropriately for its directionality -- writing to the write end or reading from the read end.
When the writer finishes writing everything it intends ever to write to the pipe, it closes its end. This is important, and sometimes essential, because the reader will not perceive end-of-file on the read end of the pipe as long as any process has the write end open. This is also one reason why it is important for each process to close the end it is not using, because if the reader also has the write end open then it can block indefinitely trying to read from the pipe, regardless of what any other process does.
Of course, the reader should also close the read end when it is done with it (or terminate, letting the system handle that). Failing to do so constitutes excess resource consumption, but whether that is a serious problem depends on the circumstances.
Inside a while(1) I'm trying to:
spawn a child process with fork();
redirect the child process stdout so that the parent process can see it
print the result in the terminal from the parent process
repeat
Strangely, the output from the child process seems to be printed twice
// parentToChild and childToParent are the pipes I'm using
while(1) {
int pid = fork();
if(pid < 0) {
// error, get out
exit(0);
} else if(pid != 0) {
// parent process
close(parentToChild[0]); // don't need read end of parentToChild
close(childToParent[1]); // don't need write end of childToParent
sleep(4);
char respBuffer[400];
int respBufferLength = read(childToParent[0], respBuffer, sizeof(respBuffer));
printf("before\n");
printf("parent tried to read something from its child and got: %s\n", respBuffer);
printf("after\n");
} else if (pid == 0) {
if(dup2(childToParent[1], STDOUT_FILENO) < 0) {
// printf("dup2 error");
};
close(childToParent[1]);
close(childToParent[0]);
close(parentToChild[1]); // write end of parentToChild not used
printf("child message");
// if we don't exit here, we run the risk of repeatedly creating more processes in a loop
exit(0);
}
}
I would expect the ouput of the following loop at each iteration to be:
before
parent tried to read something from its child and got: child message
after
But instead, at each iteration I get:
before
parent tried to read something from its child and got: child message
after
child message
What's the reason behind the second print of "child message"?
Flushing the stdout buffers before calling fork() doesn't seem to solve the issue
Interestingly, removing the while loop and keeping everything else intact seems to work fine
In the first iteration of the loop, you close childToParent[1] in the parent, and you don't recreate the pipes, so in the second iteration of the loop, its trying to reuse those closed pipes, so the child's dup2 call fails, so its printf goes to the terminal. Meanwhile, in the parent, the read call returns 0 without writing anything to the buffer, so you just print the old contents.
Well. I kinda understand how pipes work and why is dup/dup2 used before an exec in any child process.
But I need help with the 'close(int fd)' thing.
To make it clear I would like to ask you for any pseudocode or any C code example which does the following:
Parent gets a fd from a file using open().
Parent creates a child which execs to another program which reads data from the open() func fd used before and writes the output in a pipe. (So parent should wait it to end before continuing).
Same parent then creates another child which is going to exec and read from that write end of the pipe created before and write the output in the stdo.
Is it even posible to do this with only one pipe?
The tricky thing here for me is not creating the pipe and redirecting channels with dup2 and stuff, it is knowing where and when to close() all the fd channels.
If you could explain me how to do a thing like that and when and where to close the channels with an example I think I would definetly understand it all.
Thanks a lot guys.
Below is a complete example. WhozCraig already told that there's no need for parent to wait for child 1 to end before continuing, since child 2 has to read the pipe until EOF. (On the contrary, the parent must not wait, because the pipe buffer might not be large enough to hold all the file data.) Of course there's only one pipe needed, where child 1 writes to one end and child 2 reads from the other. And for that no dup is needed.
When and where does the parent have to close the pipe channels?
The parent may close the pipe ends as soon as it doesn't need them any longer, provided that a child which needs a pipe end has it open, i. e. in our case, the parent may close (its descriptor of) the write end after child 1 has been forked, and the parent may close the read end after child 2 has been forked, since the children inherit the pipe descriptors, and they remain usable until the children close them at exit.
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
main(int argc, char *argv[])
{
if (argc <= 1) return 1;
int fd = open(argv[1], O_RDONLY); // Parent gets a fd from a file.
if (fd < 0) return perror(argv[1]), 1;
int pipefd[2];
if (pipe(pipefd) < 0) return perror("pipe"), 1;
char in[8], out[8];
sprintf(in, "FD:%d", fd); // reads data from the open() func fd
sprintf(out, "FD:%d", pipefd[1]); // writes the output in a pipe
switch (fork())
{ // Parent creates a child which execs to another program, e. g. "socat"
case -1: return perror("fork 1"), 1;
case 0: execlp("socat", "socat", "-u", in, out, NULL);
return perror("execlp 1"), 1;
}
close(pipefd[1]); // parent may close write end, since child has it open
sprintf(in, "FD:%d", pipefd[0]); // read from the pipe created before
sprintf(out, "FD:%d", 1); // write the output in the stdo
switch (fork())
{ // Same parent then creates another child which is going to exec
case -1: return perror("fork 2"), 1;
case 0: execlp("socat", "socat", "-u", in, out, NULL);
return perror("execlp 2"), 1;
}
close(pipefd[0]); // parent may close read end, since child has it open
}
I am creating the child process in my code. When i call the fork(), child process should start its execution from next statement, but in my code, child process executes the statement before the fork call.
#include<stdio.h>
int main()
{
int pid;
FILE *fp;
fp = fopen("oh.txt","w");
fprintf(fp,"i am before fork\n");
pid = fork();
if(pid == 0)
{
fprintf(fp,"i am inside child block\n");
}
else{
fprintf(fp,"i inside parent block\n");
}
fprintf(fp,"i am inside the common block to both parent and child\n");
fclose(fp);
return 0;
}
This is the output which i get
OUTPUT:
i am before fork
i inside parent block
i am inside the common block to both parent and child
i am before fork
i am inside child block
i am inside the common block to both parent and child
The line "i am before fork" should be written once in the file but it is written twice by child and parent.
Why it is so?
Thank you.
This is probably a buffering issue. fprintf doesn't write to the file immediately, but buffers the output. When you fork, you end up with two copies of the buffer.
Try doing an fflush(fp) before forking and see if that solves the issue.
I guess this is because you print with fprintf, it gets buffered but not printed, and then it gets printed in child process when buffer is flushed.
I have the main process forking two times and thus creating two children. The two children are piped with each other like this:
ls | more
Now the problem is that the second child never dies. Why is that? When does the last child in a pipe die really?
Removing one wait() call shows the expected result of ls | more but gives some further weird behaviours(stuck terminal etc).
Here is my code:
int main(){
printf("[%d] main\n", getpid());
int pip[2], i;
pipe(pip);
/* CHILDREN*/
for (i=0; i<2; i++){
if (fork()==0){
/* First child */
if (i==0){
printf("[%d] child1\n", getpid());
close(1); dup(pip[1]);
close(pip[0]);
execlp("ls", "ls", NULL);}
/* Second child */
if (i==1){
printf("[%d] child2\n", getpid());
close(0); dup(pip[0]);
close(pip[1]);
execlp("more", "more", NULL);}
}
}
wait(NULL); // wait for first child
wait(NULL); // wait for second child
return 0;
}
The read end of the pipe won't get an EOF mark until the write end has been closed by all its users. The parent of both children still has both ends of the pipe open, so more doesn't see an EOF (a return of 0 from read()), and keeps waiting for more input.
Check with ps axw that it's really the more that isn't dying. Then read the man page for wait. Look at the returned status for wait.
(Hint: look at what causes a 'zombie' process. I don't think the waits are doing what you think they are.)