Programming with FIFO: mkfifo(), mknod()

FIFOs can be created from the shell. But they can also be created using programs. There are two ways by which FIFOs can be created from the program

Creating a FIFO

1. mknod

Syntax

int mknod(const char *pathname, mode_t mode, dev_t dev);

where pathname corresponds to the fifo name, mode corresponds to the file permissions. Since mknod can be used to create a regular file, block or charcter special files and fifo. We have to specify the file type. Corresponding to FIFO: the file type is S_IFIFO.

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

int main(int argc, char *argv[]){
     int result;
     if (argc != 2) {
             fprintf(stderr, "Usage: ./a.out fifoname\n");
             exit (1);
     }

     result = mknod (argv[1], S_IRUSR| S_IWUSR|S_IFIFO, 0);
     if (result < 0) {
          perror ("mknod");
          exit (2);
       }
  }

Here we wish to give both read and write permissions to the user. So we specified S_IRUSR | S_IWUSR| S_IFIFO in the mode. If the file type is S_IFCHR or S_IFBLK then dev is checked; otherwise it is ignored. So we passed 0 as the argument.

2. mkfifo

Syntax

int mkfifo(const char *pathname, mode_t mode);

where pathname correspnds to fifo name and mode corresponds to file mode.

 # include <stdio.h>
 # include <stdlib.h>
 # include <sys/types.h>
 # include <sys/stat.h>

int main(int argc, char *argv[]){
     int result;

     if (argc != 2) {
          fprintf(stderr, "Usage: ./a.out fifoname\n");
          exit (1);
     }

    result = mkfifo (argv[1], S_IRUSR| S_IWUSR);
     if (result < 0) {
          perror ("mkfifo");
          exit (2);
      }
  }

Here we need not explicitly specify the file type S_IFIFO. You can (if you wish) specify S_IFIFO in the mode.

 

Operations on Pipe (Advanced Pipe Usage)

we discussed how we can work with pipe() system call, where it is used(Linux shell), how to use pipes in threads(processes).

Here we can discuss on the topic ‘Number of readers and writers accessing a pipe’
A pipe can have more than one reader and more than one writer. Only one thing that must be kept in mind is that writer writes into fd[1] and reader reads from fd[0], where fd is the filedescriptor passed for the pipe() call.

int pipe(int fd[2]);

The program below demonstrates this with two writers and one reader. The writer Writer_ABC writes the capital Alphabet and the Writer_abc writes the small alphabet into the pipe. The reader reads in the order of the letters written by the two writers

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

int fd[2];//File descriptor for creating a pipe

//This function continously reads fd[0] for any input data byte
//If available, prints

void *reader()
{
    while(1){
       char    ch;
       int     result;

       result = read (fd[0],&ch,1);
       if (result != 1) {
            perror("read");
            exit(3);
    }    printf ("Reader: %c\n", ch);  }
}

//This function continously writes Capital Alphabet into fd[1]
//Waits if no more space is available

void *writer_ABC()
{
     int     result;
     char    ch='A';

     while(1){
           result = write (fd[1], &ch,1);
           if (result != 1){
               perror ("write");
               exit (2);
           }

           printf ("Writer_ABC: %c\n", ch);
           if(ch == 'Z')
              ch = 'A'-1;

           ch++;
      }
}

//This function continously writes small Alphabet into fd[1]
//Waits if no more space is available

void *writer_abc()
{
  int     result;  char    ch='a';

  while(1){
      result = write (fd[1], &ch,1);
      if (result != 1){
            perror ("write");
            exit (2);
      }

      printf ("Writer_abc: %c\n", ch);
      if(ch == 'z')
            ch = 'a'-1;

     ch++;
  }
}

int main()
{
   pthread_t       tid1,tid2,tid3;
   int             result;

   result = pipe (fd);
   if (result < 0){
       perror("pipe ");
       exit(1);
   }

 pthread_create(&tid1,NULL,reader,NULL);
 pthread_create(&tid2,NULL,writer_ABC,NULL);
 pthread_create(&tid3,NULL,writer_abc,NULL);

 pthread_join(tid1,NULL);
 pthread_join(tid2,NULL);
 pthread_join(tid3,NULL);
}

Output

Writer_abc: o
Writer_abc: p
Writer_abc: q
Writer_abc: r
Writer_abc: s
Writer_abc: t
Writer_abc: u
Reader: A
Reader: B
Reader: C
Reader: D
Reader: E
Reader: F
Reader: G
Reader: H
Reader: I
Reader: J
Reader: K
Reader: L
Reader: M
Reader: N
...

Piping in threads

Pipes are quite useful in threads. Imagine a scenario, where one thread is continously monitoring for new user input and another thread performs some processing on this data input. This can be achieved with the help of shared memory. But in case of shared memory, one has to think about data synchronization. Pipes can be made use of here. One thread waits and reads the user input and writes it into the pipe. The second thread can read from the pipe and perform processing.

Let’s take a simple example. The program below shows the READER-WRITER threads. The WRITER thread continously writes the alphabets into the pipe. and the READER thread reads it. See clearly that fd[2] is declared globally so that both threads can access it. Also note that the pipe() system call was called before threads creation.

To compile a program with threads, we must specify -lpthread to link with the POSIX threads library
$ gcc program -lpthread
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <pthread.h>

int fd[2];//File descriptor for creating a pipe

//This function continously reads fd[0] for any input data byte
//If available, prints

void *reader()
{
   while(1){
      char    ch;
      int     result;

      result = read (fd[0],&ch,1);
      if (result != 1) {
        perror("read");
        exit(3);
      }

      printf ("Reader: %c\n", ch);   }
}

//This function continously writes Alphabet into fd[1]
//Waits if no more space is available

void *writer()
{
   int     result;
   char    ch='A';

   while(1){
       result = write (fd[1], &ch,1);
       if (result != 1){
           perror ("write");
           exit (2);
       }

       printf ("Writer: %c\n", ch);
       if(ch == 'Z')
         ch = 'A'-1;

       ch++;
   }
}

int main()
{
   pthread_t       tid1,tid2;
   int             result;

   result = pipe (fd);
   if (result < 0){
       perror("pipe ");
       exit(1);
   }

   pthread_create(&tid1,NULL,reader,NULL);
   pthread_create(&tid2,NULL,writer,NULL);

   pthread_join(tid1,NULL);
   pthread_join(tid2,NULL);
}

Output

...
Reader: Y
Reader: Z
Reader: A
Reader: B
Reader: C
Reader: D
Reader: E
Reader: F
Reader: G
Reader: H
Reader: I
Reader: J
Reader: K
Reader: L
Reader: M
Reader: N
Reader: O
Reader: P
Reader: Q
Reader: R
Reader: S
Reader: T
Reader: U
Reader: V
Reader: W
Reader: X
Reader: Y
Reader: Z
Reader: A
Reader: B
Reader: C
...

After doing this, let’s check what’s the capacity of a pipe. Anyway pipe also utilizes memory, so there must be an upper limit to the capacity of a pipe. A writer thread keeps on writing to the pipe hoping that the READER thread is reading it. Let’s put some delay in READER thread (sleep() call). so that write will at some point of time stop writing the data to the pipe.

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

int fd[2];//File descriptor for creating a pipe

//This function continously reads fd[0] for any input data byte
//If available, prints

void *reader()
{

 int     count = 0;
 sleep (25);
  //Delay in starting the reading from the pipe

  while(1){
      char    ch;
      int     result;

      result = read (fd[0],&ch,1);
      if (result != 1) {
          perror("read");
          exit(3);
      } printf ("Reader: %d %c\n",++count,ch);
  }
}

//This function continously writes Alphabet into fd[1]

void *writer()
{
    int     result;
    char    ch='A'; int     count = 0;

    while(1){

       result = write (fd[1], &ch,1);
       if (result != 1){
           perror ("write");
           exit (2);
       }

       printf ("Writer: %d %c\n", ++count, ch);

       if(ch == 'Z')
          ch = 'A'-1;

        ch++;
   }
}

int main()
{
   pthread_t       tid1,tid2;
   int             result;

   result = pipe (fd);

   if (result < 0){
        perror("pipe ");
        exit(1);
   }

   pthread_create(&tid1,NULL,reader,NULL);
   pthread_create(&tid2,NULL,writer,NULL);

   pthread_join(tid1,NULL);
   pthread_join(tid2,NULL);
}

Output
...
Writer: 65524 D
Writer: 65525 E
Writer: 65526 F
Writer: 65527 G
Writer: 65528 H
Writer: 65529 I
Writer: 65530 J
Writer: 65531 K
Writer: 65532 L
Writer: 65533 M
Writer: 65534 N
Writer: 65535 O
Writer: 65536 P

On executing the above program, the WRITER thread stopped at the count = 65536 waiting for READER thread to read the data already written. After the specified delay (here 25 seconds), the READER thread resumes its operation and starts reading. Then both WRITER and READER thread works normally.So pipe() is useful for implementing QUEUE mechanism of sending the data from one thread(process) to another keeping in mind that the pipe also has a capacity limit. So if the reader is blocked or if there is no reader, the writer will block at some point of time if it reached the capacity of the pipe.

 

Using Pipes in Linux Programming

Pipes are one of the most commonly used mechanisms for IPC (inter process communication). IPC is the mechanism by which two or more processes communicate with each other. The commonly used IPC techniques includes shared memory, message queues, pipes and FIFOs (and sometimes even files). Pipes are helpful in the unidirectional form of communication between processes.The pipe() creates a pipe.Syntax
int pipe(fd[2])pipe() creates a pair of file descriptors, pointing to a pipe inode, and places them in the array pointed to by fd. fd[0] is for reading, fd[1] is for writing.Let’s see a simple program how to use piping.
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

#define MSG_LEN 64

int main(){

      int     result;
      int     fd[2];
      char    *message="Linux World!!!";
      char    recvd_msg[MSG_LEN];

      result = pipe (fd); //Creating a pipe fd[0] is for reading and fd[1] is for writing

      if (result < 0) {
          perror("pipe ");
          exit(1);
      }

      //writing the message into the pipe

      result=write(fd[1],message,strlen(message));
      if (result < 0) {
          perror("write");
          exit(2);
      }

      //Reading the message from the pipe

      result=read (fd[0],recvd_msg,MSG_LEN);

      if (result < 0) {
          perror("read");
          exit(3);
      }

      printf("%s\n",recvd_msg);
      return 0;
}

Output

Linux World!!!

As seen above , the pipe() system call creates a pipe. Now normal system calls like read() or write() can be used for reading and writing data to the pipe. As you have noticed the fd[0] can only be used for reading from the pipe and does not permit writing. Similarly fd[1] can only be used to perform writing to the pipe.

Seeing the above program, one would feel

what is the neccessity of piping?.

You are writing into the pipe and reading from it. That one can do even from a file.

Now let’s see another scenario. If we add more writes in the above program before reading anything and then start reading, you will find that the first read call will fetch you the first written message and so on. So

pipe() is helpful in Implementing a QUEUE strategy(First in First out)

FIFO: the message which was written first will be available for the first read, then the message which came second, for the second read.

Note: The first read itself can get all of the messages written by different write, if it specifies a large size in the size field (a size which can span the size of one or more writes ) as demonstrated here

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

#define MSG_LEN 64

int main(){
   int     result;
   int     fd[2];
   char    message[MSG_LEN];
   char    recvd_msg[MSG_LEN];

   result = pipe (fd); //Creating a pipe//fd[0] is for reading and fd[1] is for writing

   if (result < 0) {
     perror("pipe ");
     exit(1);
   }

   strncpy(message,"Linux World!! ",MSG_LEN);

   result=write(fd[1],message,strlen(message));
   if (result < 0) {
      perror("write");
      exit(2);
   }

   strncpy(message,"Understanding ",MSG_LEN);

   result=write(fd[1],message,strlen(message));
   if (result < 0) {
      perror("write");
      exit(2);
  }

  strncpy(message,"Concepts of ",MSG_LEN);

  result=write(fd[1],message,strlen(message));
  if (result < 0) {
      perror("write");
      exit(2);
 }

 strncpy(message,"Piping ", MSG_LEN);
 result=write(fd[1],message,strlen(message));

 if (result < 0) {
     perror("write");
     exit(2);
 }

 result=read (fd[0],recvd_msg,MSG_LEN);
 if (result < 0) {
      perror("read");
      exit(3);
 }

 printf("%s\n",recvd_msg);
 return 0;

}

Output:

Linux World!! Understanding Concepts of Piping

In the above program, The read() call reads all the messages in a single stretch, because of the MSG_LEN(64) which is greater than sum of the size of the messages written in four different writes. The only thing that we must remember that the read() reads in the order of messages written.