C pipe fork fd question - simple xv6 pingpong question - c

C newbie here. I am trying to write a simple program in xv6 that utilizes pipes, forks, and file descriptors.
int
main(int argc, char *argv[])
{
int p[2]; // file descriptors for pipe
char recv_buf[5];
pipe(p);
if (fork() == 0) { // child
read(p[0], recv_buf, 5);
printf("%d: received %s\n", getpid(), recv_buf);
close(p[0]);
write(p[1], "pong", 5);
close(p[1]);
} else { // parent
write(p[1], "ping", 5);
close(p[1]);
read(p[0], recv_buf, 5);
printf("%d: received %s\n", getpid(), recv_buf);
close(p[0]);
}
exit(0);
}
I thought that the program would succeed in printing
$ ./pingpong
$ "3: received ping"
$ "4: received pong"
to the terminal output.
Instead the output comes out as:
$ ./pingpong
$ "3: received ping"
Can anyone explain what's happening here? I thought that each process had its own file descriptor copy, and that read/write would hang the parent process until there is output on the other end of the pipe. So why is it that that the child process is not receiving the "pong" call?
Note that if I add a wait(0) in the parent, the problem goes away.
int
main(int argc, char *argv[])
{
int p[2]; // file descriptors for pipe
char recv_buf[5];
pipe(p);
if (fork() == 0) { // child
read(p[0], recv_buf, 5);
printf("%d: received %s\n", getpid(), recv_buf);
close(p[0]);
write(p[1], "pong", 5);
close(p[1]);
} else { // parent
write(p[1], "ping", 5);
wait(0); // this fixes the problem. but why?
close(p[1]);
read(p[0], recv_buf, 5);
printf("%d: received %s\n", getpid(), recv_buf);
close(p[0]);
}
exit(0);
}
$ ./pingpong
$ "3: received ping"
$ "4: received pong"
Can anyone explain why the wait(0) causes the program to succeed?

I actually figured out the answer shortly after typing this.
The program with the first program is that inside the parent block I am writing to the output file descriptor, then reading from the input file descriptor immediately after, which means the parent process doesn't hang.
The result is that the parent process exits before the child process is able to make any read/writes.
if (fork() == 0) { // child process never reached
read(p[0], recv_buf, 5);
printf("%d: received %s\n", getpid(), recv_buf);
close(p[0]);
write(p[1], "pong", 5);
close(p[1]);
} else { // parent
write(p[1], "ping", 5); // write to output fd
close(p[1]); // close output fd
read(p[0], recv_buf, 5); // read "ping" from input fd
printf("%d: received %s\n", getpid(), recv_buf); // print "ping"
close(p[0]); // close input fd
}
exit(0); // parent process exists immediately
wait(0) fixes this by allow the child process to read/write prior to the parent reaching the read block

Related

How to print both execl in fork using pipe (two children)?

I am trying using pipe() to execute ls | wc. The fork part successfully prints as I expected (first parent --> first child --> second parent --> second child), but it does not print out the second child's part (wc). I put the exactly same codes for both first child and second child, and I have no idea how I can successfully edit the codes. The entire codes are written below:
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/types.h>
#include<string.h>
#include<sys/wait.h>
/* NOTE: place a new child in parents. ALWAYS! */
int main()
{
// We use two pipes
// First pipe to send input string from parent
// Second pipe to send concatenated string from child
int fd1[2]; // Used to store two ends of first pipe
int fd2[2]; // Used to store two ends of second pipe
char fixed_str[] = "forgeeks.org";
char input_str[100];
pid_t child_a, child_b, child_c; //three children to run three commands
//first pipe
if (pipe(fd1) == -1)
{
fprintf(stderr, "Failed to pipe" );
return 1;
}
//second pipe
if (pipe(fd2) == -1)
{
fprintf(stderr, "Failed to pipe" );
return 1;
}
//scanf("%s", input_str);
child_a = fork(); //first fork
//first child
if (child_a == 0)
{
printf("first child\n");
close(fd1[1]); // Close writing end of first pipe
// Read a string using first pipe
char concat_str[100];
read(fd1[0], concat_str, 100);
execlp("ls", "ls", NULL); //ls | wc222 | wc //FIXME: Changed with concat_str somehow
// We only get here if exec() fails
perror("exec ls");
exit(1);
//concat_str[k] = '\0'; // string ends with '\0'
// Close both reading ends
close(fd1[0]);
close(fd2[0]);
// Write concatenated string and close writing end
write(fd2[1], concat_str, strlen(concat_str)+1);
close(fd2[1]);
exit(0);
}
//first parent
else if (child_a > 0)
{
printf("first parent\n");
child_b = fork(); //second fork
//second child
if (child_b == 0)
{
printf("second child\n");
close(fd1[1]); // Close writing end of first pipe
// Read a string using first pipe
char concat_str[100];
read(fd1[0], concat_str, 100);
execlp("wc", "wc", NULL); //ls | wc222 | wc //FIXME: Changed with concat_str somehow
// We only get here if exec() fails
perror("exec wc");
exit(1);
//concat_str[k] = '\0'; // string ends with '\0'
// Close both reading ends
close(fd1[0]);
close(fd2[0]);
// Write concatenated string and close writing end
write(fd2[1], concat_str, strlen(concat_str)+1);
close(fd2[1]);
exit(0);
}
//second parent
else if (child_b > 0)
{
printf("second parent\n");
char concat_str[100];
close(fd1[0]); // Close reading end of first pipe
// Write input string and close writing end of first
// pipe.
write(fd1[1], input_str, strlen(input_str)+1);
close(fd1[1]);
// Wait for child to send a string
wait(NULL);
close(fd2[1]); // Close writing end of second pipe
// Read string from child, print it and close
// reading end.
read(fd2[0], concat_str, 100);
printf("Concatenated string %s\n", concat_str);
close(fd2[0]);
}
//second error
else
{
fprintf(stderr, "Failed to fork" );
return 1;
}
char concat_str[100];
close(fd1[0]); // Close reading end of first pipe
// Write input string and close writing end of first
// pipe.
write(fd1[1], input_str, strlen(input_str)+1);
close(fd1[1]);
// Wait for child to send a string
wait(NULL);
close(fd2[1]); // Close writing end of second pipe
// Read string from child, print it and close
// reading end.
read(fd2[0], concat_str, 100);
printf("Concatenated string %s\n", concat_str);
close(fd2[0]);
}
//first error
else
{
fprintf(stderr, "Failed to fork" );
return 1;
}
}

Synchronizing parent and child when using 2 pipes

Here is what my code is intended to do :
The child should:
1. Sends a character to the parent
2. Receive integer from parent and print it
The parent should:
1. Read the character sent from the child and print it
2. Cast it to an integer and send the result to the child
Here is the code I wrote:
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/types.h>
#include<string.h>
#include<sys/wait.h>
int main()
{
int fd1[2];
int fd2[2];
pid_t p;
if (pipe(fd1)==-1)
{
fprintf(stderr, "Pipe Failed" );
return 1;
}
if (pipe(fd2)==-1)
{
fprintf(stderr, "Pipe Failed" );
return 1;
}
p = fork();
if (p<0)
{
fprintf(stderr, "fork Failed" );
return 1;
}
if (p==0){
char c='a';
int received;
close(fd1[0]);
write(fd1[1], c, sizeof(char));
close(fd1[1]);
close(fd2[1]);
read(fd2[0], received, sizeof(int));
printf("Printing from child ");
printf(" ");
printf("%d", received);
close(fd2[0]);
}
if (p >0)
{
char received;
close(fd1[1]);
read(fd1[0], received, sizeof(char));
printf("Printing from parent ");
printf(" ");
printf("%c", received);
close(fd1[0]);
close(fd2[0]);
int test=(int)received;
write(fd2[1], test, sizeof(test));
close(fd2[1]);
}
}
My current output is the following: Printing from parent Printing from child 0
I am assuming the parent is reading from the pipe before the child writes to it, how to fix that?
I am assuming the parent is reading from the pipe before the child writes to it, how to fix that?
This assumption is false. The error is one that a good compiler should have warned about - you missed that not the values of the variables c, received and test have to be passed to write and read, but their addresses:
write(fd1[1], &c, sizeof(char));
…
read(fd2[0], &received, sizeof(int));
…
read(fd1[0], &received, sizeof(char));
…
write(fd2[1], &test, sizeof(test));
May I ask how the computer ensures the scenario I assumed doesn't happen?
The read from the pipe, just as with a terminal device, simply blocks until there's something to read (provided that the file descriptor hasn't explicitly been set to non-blocking).

Can I use pipe as read at parent and write in child?

I'm now learning how to use pipes correctly. I found examples only for write in parent and read in child, but I want to know how can I do it reverse. I tried like this:
#include <stdlib.h>
#include <stdio.h>
#include <signal.h>
#include <sys/types.h>
int main(){
int p1[2], p2[2];
char original[]="This is a string\n ";
pid_t child2;
pid_t child=fork();
if (child>0)
{
child2 = fork();
if(child2>0){
wait();
printf("I'm the parrent.\n");
close(p1[1]);
close(p2[1]);
printf("Parrent read p1:\n");
if (read(p1[0], original, sizeof(original)) == -1)
perror("read() error in parent p1");
else printf("parent read '%s' from pipe 1\n", original);
printf("Parrent read p2:\n");
if (read(p2[0], original, sizeof(original)) == -1)
perror("read() error in parent p2");
else printf("parent read '%s' from pipe 2\n", original);
}
else{
printf("Child2 \n");
pipe(p2);
close(p2[0]);
if (write(p2[1], original, sizeof(original)+1) == -1)
perror("write() error in child2");
//close(p2[1]);
}
}
else
{
printf("Child1 \n");
pipe(p1);
close(p1[0]);
if (write(p1[1], original, sizeof(original)+1) == -1)
perror("write() error in child1");
//close(p1[1]);
}
return 0;
}
But this way give to me error at reading in parent. read() error in parent p1: Bad file descriptor both times, at p1 and also at p2. So, can I do this this way or not? Or this is just something trivial error?
As already said you just need to invert the indexes for closing the pipes and the read/write.
Your code was almost correct. Two things were wrong: on the parent you were first closing the pipes and then calling pipe(): it should be the other way round: first you create both of the pipes and then close the corresponding element, all in the parent.
Secondly you should call wait after setting the pipes otherwise it does not work. Guessing you want to wait for all the children you should call wait(NULL). I do not know what you meant with wait().
The complete code:
#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>
int main(){
int p1[2], p2[2];
char original[]="This is a string\n ";
pid_t child2;
pid_t child=fork();
if (child>0)
{
child2 = fork();
if(child2>0){
printf("I'm the parrent.\n");
pipe(p1); //ADDED
pipe(p2); //ADDED
close(p1[1]);
close(p2[1]);
wait(NULL); //DON'T GET IT WHY? if u want to wait all children,
//wait after setting the pipes.
printf("Parrent read p1:\n");
if (read(p1[0], original, sizeof(original)) == -1)
perror("read() error in parent p1");
else printf("parent read '%s' from pipe 1\n", original);
printf("Parrent read p2:\n");
if (read(p2[0], original, sizeof(original)) == -1)
perror("read() error in parent p2");
else printf("parent read '%s' from pipe 2\n", original);
}
else{
printf("Child2 \n");
//pipe(p2); ERROR HERE
close(p2[0]);
if (write(p2[1], original, sizeof(original)+1) == -1)
perror("write() error in child2");
//close(p2[1]);
}
}
}

How can I write strings into a file using a parent and two child processes?

I'm trying to solve this problem but I cannot.
Here is a short description:
We have a parent with two child processes (child_a, child_b) and N strings.
Current data: data[i] (0...N)
Parent starts and waiting for signal from child_b. Child_a is waiting.
Child_b sends a signal to parent and waiting for data.
Parent write data[i] into pipe and waiting
Child_b reads data[i] from pipe and printf(). Then waiting for Child_a
Child_a generates a random number (between 1-5) and write into pipe.
Child_b reads rand from pipe and sends to Parent.
Parent write "data[i] - rand" into a file.
And start from the begining with next data...
Here is my code:
FILE *fp2;
fp2 = fopen("bill.dat" , "a");
pid_t child_a, child_b;
int pipefd_a[2], pipefd_b[2];
char msg[100];
char sleep_time[10];
int stat;
signal(SIGUSR1, handler);
signal(SIGUSR2, handler);
if(pipe(pipefd_a) == -1 || pipe(pipefd_b) == -1){
perror("Error\n");
exit(EXIT_FAILURE);
}
int i;
for(i = 0; i < n; i++){
child_a = fork();
if(child_a < 0){
perror("Error\n");
}
if(child_a == 0){
sleep(1);
printf("Child_a-----\n");
srand(time(NULL));
int r = rand()%5+1;
char rand[2];
sprintf(rand, "%d", r);
printf("Child_a rand: %s\n", rand);
write(pipefd_b[1], rand, strlen(rand)+1);
printf("Child_a end-----\n");
exit(0);
}
else{
child_b = fork();
if(child_b == 0){
printf("Child_b sends a signal to parent\n");
kill(getppid(), SIGUSR1);
close(pipefd_a[1]);
read(pipefd_a[0], msg, sizeof(msg));
close(pipefd_a[0]);
printf("Child_b reads from pipe (from parent): %s\n", msg);
kill(child_a, SIGUSR2);
sleep(2);
read(pipefd_b[0], sleep_time, 10);
printf("Child_b reads from pipe (from child_a): %s\n", sleep_time);
fflush(NULL);
write(pipefd_b[1], sleep_time, sizeof(sleep_time));
close(pipefd_b[1]);
printf("Child_b end-----\n");
exit(0);
}
printf("============== %d ============== \n", i);
printf("Parent waiting for signal...\n");
pause();
printf("Signal received\n");
printf("Parent write into pipe\n");
close(pipefd_a[0]);
write(pipefd_a[1], data[i].address, 100);
kill(child_b, SIGUSR2);
waitpid(child_b, &stat, 0);
read(pipefd_b[0], msg, sizeof(msg));
fprintf(fp2, "%s - %s\n", data[i].address, msg);
printf("Parent writes into file: %s\n", msg);
}
}
fclose(fp2);
and my output (n = 2):
data1 - 1
data1 - 1
data1 - 1
data2 - 3
There are always 2^n lines in the file.
The assignment requires two child processes to achieve this and I suspect that the problem is with the "fork()" within the loop, but don't understand what I'm doing wrong.
The first problem is that you put your process creation in for loop.
That is why you get 2n lines. If you want to read some data form file N times you don't make N processes.
Second thing you need 3 pipes for your work. Pipe is unidirectional meaning one process can only write into pipe and the other can only read from it. One end is for writing and the other end is for reading, so you must close unused descriptors!
first pipe is used when parent writes data[i] into pipe and child_b reads
second pipe is used when child_a writes a random number into pipe and child_b reads
third pipe is used when child_b writes into pipe and parent performs reading
If you could write the whole program it would be easier form me to understand it and to help you.
This is skeleton how i would try to make this work
int pipe_parent_to_childB[2], pipe_childB_to_parent[2], pipe_childA_to_childB;
if(pipe(pipe_parent_to_childB) == -1 || pipe(pipe_childB_to_parent) == -1 ||pipe(pipe_childA_to_childB) == -1)
{
perror("Error\n");
exit(EXIT_FAILURE);
}
//close read end because parent will write to pipe
close(pipe_parent_to_childB[0]);
switch(fork()) //create child b
{
case -1:
//error
case 0:
//now you are in child_b
close(pipe_parent_to_childB[1]);
close(pipe_childA_to_childB[1]);
//perform some action
default:
break;
}
}
switch(fork()) //crete child a
{
case -1:
//error
case 0:
//now you are in child_a
close(pipe_childA_to_childB[0]);
//perform some action
default:
break;
}
//here you are in parent process again. Send signals, wait for signals and write to pipe here
//from parent you send some data through pipe to process child_b N times
//after this you close write end of the pipe descriptor.

Creating two pipes with three execlp small error somewhere C System Programming

I'm trying to create three child processes and two pipes that will execute three execlp(). When my program runs, however, the output is not what I expect.
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main()
{
pid_t pid = getpid();
printf("STARTING PROCESSXXX %d\n",pid);
int c1Toc2[2];
int c2Toc3[2];
if(pipe(c1Toc2) == -1)
{
perror("pipe");
exit(EXIT_FAILURE);
}
if(pipe(c2Toc3) == -1)
{
perror("pipe");
exit(EXIT_FAILURE);
}
int rValue = fork();
if(rValue == -1)
{
perror("Child_1");
}
else if (rValue == 0)
{
printf("CHILD 1: ");
printf("PROCESS ID IS: %ld \tMY PARENT ID IS: %ld\trValue IS: %d\n", (long) getpid(), (long) getppid(), rValue);
dup2(c1Toc2[1], STDOUT_FILENO);
close(c1Toc2[0]);
close(c2Toc3[0]);
execlp("ps", "ps", "-ef", NULL);
exit(0);
}
rValue = fork();
if(rValue == -1)
{
perror("Child_2");
}
else if (rValue == 0)
{
printf("CHILD 2: ");
printf("PROCESS ID IS: %ld \tMY PARENT ID IS: %ld\trValue IS: %d\n",
(long) getpid(), (long) getppid(), rValue);
dup2(c1Toc2[0], STDIN_FILENO);
close(c1Toc2[1]);
dup2(c2Toc3[1], STDOUT_FILENO);
close(c2Toc3[0]);
printf("CHILD 2 : goodbye\n");
execlp("grep","grep","root",NULL);
printf("CHILD 2 : goodbye\n");
exit(0);
}
rValue = fork();
if(rValue == -1)
{
perror("Child_3");
}
else if (rValue == 0)
{
printf("CHILD 3: ");
printf("PROCESS ID IS: %ld \tMY PARENT ID IS: %ld\trValue IS: %d\n",
(long) getpid(), (long) getppid(), rValue);
dup2(c2Toc3[0], STDIN_FILENO);
close(c2Toc3[1]);
close(c2Toc3[0]);
printf("CHILD 3 : \n");
execlp("sort","sort","-n", "-k4",NULL);
printf("CHILD 3 : goodbye\n");
exit(0);
}
close(c1Toc2[1]);
close(c1Toc2[0]);
close(c2Toc3[1]);
close(c2Toc3[0]);
// Add the code for the two children here
sleep(3);
printf("PARENT: PROCESS Waiting on children to complete\n");
printf("Final Print Statement before exit\n");
exit(0);
}
The problem is that you didn't close the pipe file descriptors in the children and so, because you dup'ed them over to STDOUT_FILENO (e.g. - grep child), even though the program was done its regular output it didn't fully exit and post an EOF to the next child process (i.e. - sort) and so those children hung around waiting forever.
I'm not entirely sure why your grep process refused to exit like normal because it does seem that the ps child exits, which should post an EOF to the grep child's stdin.
Here's a version that does what you want:
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main()
{
pid_t pid = getpid();
printf("PARENT: STARTING PROCESSXXX %d\n",pid);
int c1Toc2[2];
int c2Toc3[2];
if (pipe(c1Toc2) == -1)
{
perror("pipe");
exit(EXIT_FAILURE);
}
if (pipe(c2Toc3) == -1)
{
perror("pipe");
exit(EXIT_FAILURE);
}
int rValue = fork();
if (rValue == -1)
{
perror("Child_1");
exit(EXIT_FAILURE);
}
else if (rValue == 0)
{
printf("CHILD 1: PROCESS ID IS: %ld \tMY PARENT ID IS: %ld\trValue IS: %d\n", (long) getpid(), (long) getppid(), rValue);
dup2(c1Toc2[1], STDOUT_FILENO);
close(c1Toc2[0]);
close(c1Toc2[1]);
close(c2Toc3[0]);
close(c2Toc3[1]);
execlp("ps", "ps", "-ef", NULL);
perror("CHILD 1: execlp");
exit(EXIT_FAILURE);
}
rValue = fork();
if (rValue == -1)
{
perror("Child_2");
exit(EXIT_FAILURE);
}
else if (rValue == 0)
{
printf("CHILD 2: PROCESS ID IS: %ld \tMY PARENT ID IS: %ld\trValue IS: %d\n", (long) getpid(), (long) getppid(), rValue);
dup2(c1Toc2[0], STDIN_FILENO);
dup2(c2Toc3[1], STDOUT_FILENO);
close(c1Toc2[0]);
close(c1Toc2[1]);
close(c2Toc3[0]);
close(c2Toc3[1]);
execlp("grep","grep","root",NULL);
perror("CHILD 2: execlp");
exit(EXIT_FAILURE);
}
rValue = fork();
if (rValue == -1)
{
perror("Child_3");
exit(EXIT_FAILURE);
}
else if (rValue == 0)
{
printf("CHILD 3: PROCESS ID IS: %ld \tMY PARENT ID IS: %ld\trValue IS: %d\n", (long) getpid(), (long) getppid(), rValue);
dup2(c2Toc3[0], STDIN_FILENO);
close(c1Toc2[0]);
close(c1Toc2[1]);
close(c2Toc3[0]);
close(c2Toc3[1]);
execlp("sort","sort","-n", "-k4",NULL);
perror("CHILD 3: execlp");
exit(EXIT_FAILURE);
}
close(c1Toc2[0]);
close(c1Toc2[1]);
close(c2Toc3[0]);
close(c2Toc3[1]);
printf("PARENT: PROCESS Waiting on children to complete\n");
sleep(3);
printf("PARENT: Final Print Statement before exit\n");
return 0;
}
Your code looks more or less right, but it does seem to reflect an imperfect understanding of fork / exec and child process management.
For one thing, note that the exec-family functions, including execlp(), do not return except on failure. Your code seems to assume otherwise. In particular, the child processes will not print "goodbye" messages after calling execlp() unless the execlp() call fails. You should handle the (failure) case in which execlp() does return, but normally you would do so with error-handling / error-reporting code.
Also, each process, including the parent, should close those file descriptors it isn't going to use. That includes closing a FD after duplicating it onto one of the standard file numbers, and closing all file descriptors intended exclusively for other processes. You miss several of those:
Child 1 fails to close file descriptors c1Toc2[1] (after duplicating it) and c2Toc3[1].
Child 2 fails to close file descriptors c1Toc2[0] and c2Toc3[1] (after duplicating them).
Furthermore, it is pointless for the parent process to sleep() to wait on its children. If it wanted to ensure that they completed and/or to obtain their exit statuses, then it should use wait() or waitpid(). If it were not going to exit immediately after, then it should definitely use one of those functions, else the child processes will hang around as zombie processes until the parent exits. No amount of sleep() is sufficient to ensure that the child processes finish. If you don't care about any of those things, on the other hand, then you can just let the parent exit. There's no special advantage in that case to keeping it around.
I don't know whether any of those explains the unexpected behavior you see, because you haven't explained what that is.

Resources