Wednesday, May 25, 2022

[SOLVED] how to use pipe to connect two child process in C

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 sure stdout buffer is written into STDOUT_FILENO. stdout is line-buffered when connected to a terminal, and block-buffered otherwise (the determination is made before main is invoked), so that puts behaves differently when stdout 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)