Search code examples
cpipefork

Use IPC Mechanism of pipe to complete the program so the child and parent print the same output


So I recently had an exam in a class about operating systems and it had asked me to write a program using pipe(). This program was intended to send and receive data through the pipe so that regardless of which file was run, the output would be the same.

The output should be the following.

Hello (from child) 1

Hello (from parent)

Hello (from child) 2

The template code was given as follows. (I could not change this code, I was only to insert code to make it work). No creativity... I know.

#include <unistd.h>
#include <stdio.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <signal.h>

int main() {
    pid_t pid;
    char buf[32];



    if ((pid = fork()) < 0) {
        puts("Error");
    } else if(pid == 0) { //child


        fprintf(stdout, "Hello (from child) 1\n");



        fprintf(stdout, "Hello (from child) 2\n");

        fflush(stdout);

    }
    else {


        fprintf(stdout, "Hello (from parent)\n");


    }
}

After some time my conclusion was the following. But after executing nothing printed. (I couldn't test it because it was a paper exam but I tested it after). Also, I was under a time crunch so I know the mistakes are probably frequent.

I added the User Start and User End to show where I could change the code. Any help would be appreciated.

#include <unistd.h>
#include <stdio.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <signal.h>

int main() {
    pid_t pid;
    char buf[32];

    // User Start
    int fds[2];
    pipe(fds);
    close(1);
    dup(fds[1]);
    // User End

    if ((pid = fork()) < 0) {
        puts("Error");
    } else if(pid == 0) { //child

        // User Start

        // User End
        fprintf(stdout, "Hello (from child) 1\n");

        // User Start
        write(fds[1],"Hello (from child) 1\n",21);
        read(fds[0],buf,21);
        write(fds[1],"Hello (from child) 2\n",21);
        // User End

        fprintf(stdout, "Hello (from child) 2\n");
        // User Start

        // User End

    }
    else {
        // User Start
        read(fds[0],buf,21);
        // User End

        fprintf(stdout, "Hello (from parent)\n");
        // User Start
        write(fds[1],"Hello (from parent)\n",21);
        read(fds[0],buf,21);

        // User End

    }
}

UPDATE

#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <stdint.h>


int main()
{
    pid_t pid;
    char buf[32];
    int returnstatus1;
    int fds[2];
    
    returnstatus1 = pipe(fds);
    printf("Return status = %d\n",returnstatus1);  //check if pipe is created or not

    if(returnstatus1 == -1)
  {
    printf("Pipe 1 could not be created\n");
    return 1;
  }

    if((pid = fork()) < 0)
    {
        puts("ERROR");
    }
    //child
    else if(pid == 0)
    {

        fprintf(stdout,"hello (from child) 1\n");
        close(fds[0]);
        write(fds[1],"hello  (from child) 1\n",21);
        close(fds[1]);
        read(fds[0],buf,21);
        fprintf(stdout,"hello (from child) 2\n");
        close(fds[0]);
        write(fds[1],"hello  (from child) 2",21);
    }
    //parent
    else
    {
        close(fds[1]);
        read(fds[0],buf,21);
        fprintf(stdout,"hello (from parent)\n");
        close(fds[0]);
        write(fds[1],"hello (from parent)\n",21);
        close(fds[1]);
        read(fds[0],buf,21);
    }
    
    return 0;
}

Solution

  • The use of pipes for synchronization is slightly unorthodox, but it's perfectly doable. You have to remember that reading from an empty pipe is blocking — it waits until something has written to it.

    Since it's clearer that way, I'll show the parent first, not the child. Of course it doesn't matter in which order you put the if branches — you can adapt it to your exam's format, and I strongly encourage you to do so in order to get a hang of it.

    With that in mind, we go ahead:

    #include <stdio.h>
    #include <unistd.h>
    
    int main (void) {
      pid_t pid;
      char buf;
      int parent_pipes[2];
      int child_pipes[2];
      pipe(child_pipes);
      pipe(parent_pipes);
      if ((pid = fork()) < 0)
          puts("Error");
      else if (pid) {
        // parent first!
        // wait for the child to write something into the pipe...
        read(*child_pipes, &buf, 1);
        // and now write to stdout, and tell the child that we're ready
        fprintf(stdout, "Hello (from parent)\n");
        fflush(stdout);
        write(parent_pipes[1], "R", 1); // it doesn't matter what we write; we have to write anything
      } else {
        // and now the child
        // output and flush...
        fprintf(stdout, "Hello (from child) 1\n");
        fflush(stdout);
        // ...and tell the parent that we're ready
        write(child_pipes[1], "R", 1); // write end of the pipe
        // now wait!
        read(*parent_pipes, &buf, 1); // read end of the pipe
        fprintf(stdout, "Hello (from child) 2\n");
      }
      return 0;
    }