Pipes II
Tags: linux, tutorial, c, pipes
Published: 2021-05-03 21:01:56

# Pipes II

### Introduction

In this part of the tutorial we will be creating and using a **pipe**, wish me luck :sweat_smile:, don't worry we can do it.

## Prerequisites

1. Been on a Linux OS.
2. A C/C++ compiler to compile a code of more than 10 lines😬.

## Creating a pipe in C

Now how do we create a **pipe** with code? Mmm :thinking:, here's a snippet.

```c
#include <unistd.h>

int main(void) {
    //file descriptors
    int pipefd[2];
    int pipe(pipefd);
}
```

Let's see part of the documentation of the **pipe()** on the manpage:

> **pipe()**  creates a **pipe**, a **unidirectional** data channel that can be used for interprocess communication.  The array `pipefd` is used to return two file descriptors referring to the ends of the pipe.  `pipefd[0]` refers to the read end of the pipe.  `pipefd[1]` refers  to the  write end of the pipe.

This argument that takes contains the file descriptors needed, one for the *read end of the pipe* and another for the *write end of the pipe*.  So ideally with one you push the data on the **pipe** and with the other you access that data. Very ingenious, right? 

Let's see if we could do something with this, I will tested on a very simple program the will behave like this:

* There's going to be **two process**.
* One process will write sequentially the numbers from **0 to 9**, waiting one second on each write.
* The other process will read the data from the **pipe**, and it will printed on the console.

I recommend you to play around with this by yourself. I mean you could try exactly as I put it here but you would understand better if you create a simple example by yourself. This steps will help you also:

* Watch first this example, learn from it.
* Try to explain it to someone else.
* Get your hands dirty with code.

Let's see the code and break it in pieces, it would be better that way, because you know, it is **C** :roll_eyes: .

```c
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>

int main(int argc, char *argv[]) {
  // file descriptors to use on the write end
  // and read end of the pipe
  int filedes[2];

  if (pipe(filedes) == -1) {
    perror("pipe couldn't be created");
    return -1;
  }
  // after this both file descriptors are open

  // creating a child process
  pid_t process = fork();
  switch (process) {
  case -1:
    perror("fork failed");
    return -1;

  // we are in the child process
  case 0:
    // closing write end pipe in child
    if (close(filedes[1]) == -1) {
      perror("closing in child");
      return -1;
    }
    break;

  // we are in the parent process
  default:
    if (close(filedes[0]) == -1) {
      perror("closing in parent");
      return -1;
    } // closing read end pipe in parent
  }

  if (passingMsg(process, filedes) == -1) {
    perror("passingMsg");
    return -1;
  }

  // cleaning
  if (process == 0)
    close(filedes[0]);
  else
    close(filedes[1]);

  return 0;
}

```

In this first snippet we can see that I made use of **fork()** to create a new process, go to its manpage, with `man fork` and you will see its documentation. If the documentation is too heavy don't worry I got your back, this :point_right: [tutorial](https://www.geeksforgeeks.org/fork-system-call/) from Geeks for Geeks, is precisely about **fork()**, take a look at it is very concise. 

If you read the code you notice that I created a **pipe**, a new child process with **fork()** and then I made some magic there on the switch statement, right? What just happened there?  Let me draw you the situation:

**After the creation of the pipe**

```bash
	+-[Calling process]<-+   f0: read end of the pipe
	| f1          f0     |   f1: write end of the pipe
	|                    |
	+--->| Pipe  |-------+
```

This is with a single process because we haven't used **fork()** yet both file descriptors are on the calling process, we will call it **parent process**.

**After the creation of the new process**

```bash
    +-[Calling process]<-+   f0: read end of the pipe
	| f1          f0     |   f1: write end of the pipe
	|      _______       |
    \+--->|       |-----+/
	      | Pipe  |
	/+--->|_______|-----+\
	| f1          f0     |
	|                    |
	+---[Child process]<-+
```

Now here is the trick, after the creation of the **child** process, both processes can write and read from the pipe. Which may be the behavior needed by your application but in our case, I want to write from the **parent or calling process** and read from the **child process**. That's the reason why I closed on the switch statement first the **write end** file descriptor of the **child** process, and later also closed the **read end** file descriptor on the **parent** process. It seems like a tongue twister, but my next draw will help you understand:

**After the switch statement**

```bash
    +-[Calling process]      f0: read end of the pipe
	| f1                   f1: write end of the pipe
	|      _______       
    \+--->|       |
	      | Pipe  |
	      |_______|-----+\
	              f0     |
	                     |
	    [Child process]<-+
```



After the switch statement I'm ready to pass messages from the **parent** to the **child**, here is the part of the code corresponding to the function **passingMsg**. 

```c
int passingMsg(pid_t process, int *filedes) {
  char buff[3];
  int BUF_SIZE = 3;
  int bRead;
  char msg[3];

  switch (process) {
  //child process
  case 0:
    printf("MSG From Parent:\n");
    for (;;) {
      bRead = read(filedes[0], buff, BUF_SIZE); //reading from the pipe
      if (bRead == -1) {
        perror("read on child");
        return -1;
      }
      if (bRead == 0) // end of steam data
        break;
      // writing msg to stdout
      if (write(STDOUT_FILENO, buff, bRead) != bRead) { //writing to the console
        perror("write to stdout");
        return -1;
      }
    }
    break;
  //parent process
  default:
    for (int i = 0; i < 10; i++) {
      sleep(1);
      snprintf(msg, 3, "%d\n", i);
      if (write(filedes[1], msg, strlen(msg)) == -1) { //writing on the pipe
        perror("write from parrent");
        return -1;
      }
    }
  }
  return 0;
}
```



## What about other language apart from C

I will leave you two links of **pipes** in **Go** and **Python**.

**Python:** 

* [Official doc](https://docs.python.org/3/library/os.html#os.pipe)
* [Python os.pipe method](https://www.geeksforgeeks.org/python-os-pipe-method/)

**Go:**

* [Pipes in Go](https://golang.org/pkg/os/#Pipe)

No idea how is this for other programming languages go and find how are pipes on other programming language that you like :smiley:.

## Conclusion

Well it is a pretty long article I admit it, but if you read it with calm you will learn a cool stuff. For me this is also new so don't worry if you don't understand it at first. There's a lot of information about pipe.

That's all folks :wave:.

## Bibliography

1. **The Linux Programming Interface** by Michael Kerrisk.
2. [fork() in C](https://www.geeksforgeeks.org/fork-system-call/)
3. manpage for **fork()**