Issue
I am self-studying an OS textbook and doing its homework:
Write a program that creates two children, and connects the standard output of one to the standard input of the other, using the pipe() system call.
then I try to write my code like this:
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
int main()
{
printf("parent in pid: (%d)\n", (int)getpid());
fflush(stdout);
int p1[2], p2[2];
pipe(p1);
pipe(p2);
char buff1[100];
char buff2[100];
int rc = fork();
if (rc < 0)
{
fprintf(stderr, "fork failed");
exit(1);
}
else if (rc == 0)
{
int fd = open("test1.txt", O_CREAT|O_WRONLY|O_TRUNC, S_IRWXU);
printf("%d\n", fd);
printf("entering child1 process at pid : (%d)\n", (int)getpid());
dup2(p1[0], STDIN_FILENO);
dup2(p2[1], STDOUT_FILENO);
int n = read(STDIN_FILENO, buff1, sizeof buff1);
puts("this is a message from child 1");
write(fd, buff1, n);
}
else
{
int rc2 = fork();
if (rc2 == 0)
{
int fd2 = open("test2.txt", O_CREAT|O_WRONLY|O_TRUNC, S_IRWXU);
printf("%d\n", fd2);
printf("entering child2 process at pid : (%d)\n", (int)getpid());
dup2(p2[0], STDIN_FILENO);
dup2(p1[1], STDOUT_FILENO);
int n = read(STDIN_FILENO, buff2, sizeof buff2);
puts("this is a message from child 2");
write(fd2, buff2, n);
}
else
{
printf("final parent in pid: (%d)\n", (int)getpid());
}
}
return 0;
}
if all went correctly. I should see messages from child1 or child2 in the txt files. But nothing happened when I run this code. But if I delete any one of the read statement like this:
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
int main()
{
printf("parent in pid: (%d)\n", (int)getpid());
fflush(stdout);
int p1[2], p2[2];
pipe(p1);
pipe(p2);
char buff1[100];
char buff2[100];
int rc = fork();
if (rc < 0)
{
fprintf(stderr, "fork failed");
exit(1);
}
else if (rc == 0)
{
int fd = open("test1.txt", O_CREAT|O_WRONLY|O_TRUNC, S_IRWXU);
printf("%d\n", fd);
printf("entering child1 process at pid : (%d)\n", (int)getpid());
dup2(p1[0], STDIN_FILENO);
dup2(p2[1], STDOUT_FILENO);
//int n = read(STDIN_FILENO, buff1, sizeof buff1);
puts("this is a message from child 1");
//write(fd, buff1, n);
}
else
{
int rc2 = fork();
if (rc2 == 0)
{
int fd2 = open("test2.txt", O_CREAT|O_WRONLY|O_TRUNC, S_IRWXU);
printf("%d\n", fd2);
printf("entering child2 process at pid : (%d)\n", (int)getpid());
dup2(p2[0], STDIN_FILENO);
dup2(p1[1], STDOUT_FILENO);
int n = read(STDIN_FILENO, buff2, sizeof buff2);
puts("this is a message from child 2");
write(fd2, buff2, n);
}
else
{
printf("final parent in pid: (%d)\n", (int)getpid());
}
}
return 0;
}
or like this
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
int main()
{
printf("parent in pid: (%d)\n", (int)getpid());
fflush(stdout);
int p1[2], p2[2];
pipe(p1);
pipe(p2);
char buff1[100];
char buff2[100];
int rc = fork();
if (rc < 0)
{
fprintf(stderr, "fork failed");
exit(1);
}
else if (rc == 0)
{
int fd = open("test1.txt", O_CREAT|O_WRONLY|O_TRUNC, S_IRWXU);
printf("%d\n", fd);
printf("entering child1 process at pid : (%d)\n", (int)getpid());
dup2(p1[0], STDIN_FILENO);
dup2(p2[1], STDOUT_FILENO);
int n = read(STDIN_FILENO, buff1, sizeof buff1);
puts("this is a message from child 1");
write(fd, buff1, n);
}
else
{
int rc2 = fork();
if (rc2 == 0)
{
int fd2 = open("test2.txt", O_CREAT|O_WRONLY|O_TRUNC, S_IRWXU);
printf("%d\n", fd2);
printf("entering child2 process at pid : (%d)\n", (int)getpid());
dup2(p2[0], STDIN_FILENO);
dup2(p1[1], STDOUT_FILENO);
//int n = read(STDIN_FILENO, buff2, sizeof buff2);
puts("this is a message from child 2");
//write(fd2, buff2, n);
}
else
{
printf("final parent in pid: (%d)\n", (int)getpid());
}
}
return 0;
}
I could see correct output in the txt file from both sides. I'm wondering what happened in my code and what went wrong in the first case?
Solution
A few issues in the code:
Both of your child processes block on reading from the pipes, but nothing ever gets written into those, so they block forever. A fix is to write into the pipes before reading.
fflush
is required to make surestdout
buffer is written intoSTDOUT_FILENO
.stdout
is line-buffered when connected to a terminal, and block-buffered otherwise (the determination is made beforemain
is invoked), so thatputs
behaves differently whenstdout
is not connected to a terminal at program start-up.The parent process should wait for children to terminate. If/when the parent process is a session leader and it terminates, the kernel sends
SIGHUP
to all (child) processes in its group. More details.
Code with fixes:
int main() {
printf("parent in pid: (%d)\n", (int)getpid());
fflush(stdout);
int p1[2], p2[2];
pipe(p1);
pipe(p2);
char buff1[100];
char buff2[100];
int rc = fork();
if (rc == 0)
{
int fd = open("test1.txt", O_CREAT|O_WRONLY|O_TRUNC, S_IRWXU);
printf("%d\n", fd);
printf("entering child1 process at pid : (%d)\n", (int)getpid());
fflush(stdout);
dup2(p1[0], STDIN_FILENO);
dup2(p2[1], STDOUT_FILENO);
puts("this is a message from child 1");
fflush(stdout);
int n = read(STDIN_FILENO, buff1, sizeof buff1);
write(fd, buff1, n);
}
else
{
int rc2 = fork();
if (rc2 == 0)
{
int fd2 = open("test2.txt", O_CREAT|O_WRONLY|O_TRUNC, S_IRWXU);
printf("%d\n", fd2);
printf("entering child2 process at pid : (%d)\n", (int)getpid());
fflush(stdout);
dup2(p2[0], STDIN_FILENO);
dup2(p1[1], STDOUT_FILENO);
puts("this is a message from child 2");
fflush(stdout);
int n = read(STDIN_FILENO, buff2, sizeof buff2);
write(fd2, buff2, n);
}
else
{
printf("final parent in pid: (%d)\n", (int)getpid());
waitpid(rc, &rc, 0);
waitpid(rc2, &rc2, 0);
}
}
return 0;
}
Answered By - Maxim Egorushkin Answer Checked By - Willingham (WPSolving Volunteer)