Named Pipes in Linux C

The mkfifo() function is used to create a named piped in the filesystem. The pipe() system call creates an anonymous pipe that can only be used by related processes. A named pipe, in contrast, can be used by any process, since the pipes are visible in the filesystem.

The mkfifo() call takes two arguments, the first being the pathname of the named pipe that we wish to create, and the second represents the read/write permissions for this pipe. The mkfifo() call returns 0 on success or -1 on error.

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

int main(void){

    //read, write, execute permissions for owner.
    printf("S_IRWXU = %d\n", S_IRWXU);

    int returnValue;

    //should return 0, if we are running this program
    //for the first time.
    returnValue = mkfifo("FIFOpipe", S_IRWXU);

    printf("The mkfifo() call returned %d\n", returnValue);

    return 0;


}

If we run this program twice (or more) the return value will be -1, as the named pipe will have already been created. We can see the error mkfifo() has encountered via errno, which is defined in the errno.h header file.

#include <stdio.h>
#include <sys/stat.h>
#include <errno.h>

int main(void){

    mode_t theMode = S_IRWXU;

    int returnValue = mkfifo("FIFOpipe", theMode);

    if(returnValue < 0){
        printf("mkfifo() failed.\n");
        printf("errno = %d\n", errno);
        if(errno==EEXIST){
            printf("That file already exists.\n");
            printf("(or we passed in a symbolic link, which we did not.)\n");
        }
    }

    return 0;

}

The mode_t permissions are delineated in the sys/stat.h header file.

To remove a FIFO, we can use the unlink() function which is included in the unistd.h header file.

#include <stdio.h>
#include <sys/stat.h>
#include <unistd.h>
//note: unistd.h included
//so we can call unlink() to delete the FIFO

int main(void){

    //S_IRWXU is the same value as 
    //'OR'-ing S_IRUSR, S_IWUSR, S_IXUSR
    //(i.e. all permissions for file creator)
    mode_t theMode = S_IRWXU;
    int returnValue;

    printf("S_IRUSR = %d\n", S_IRUSR);
    printf("S_IWUSR = %d\n", S_IWUSR);
    printf("S_IXUSR = %d\n", S_IXUSR);

    printf("S_IRUSR | S_IWUSR | S_IXUSR = %d\n", S_IRUSR | S_IWUSR | S_IXUSR);
    printf("S_IRWXU = %d\n", S_IRWXU);

    returnValue = mkfifo("AnotherPipe", S_IRWXU);
    if(returnValue==0){
        printf("FIFO created successfully.\n");
        printf("Now let's delete it!\n");
        returnValue = unlink("AnotherPipe");
        if(returnValue==0){
            printf("FIFO deleted.\n");
        }
    }


    return 0;
}

Once a named pipe has been created, we can read and write to it just as with any other file.

Implementing Pipes in C

Pipes are an older but still useful mechanism for interprocess communication. Basically, pipes connect one process’s output to another process’s input. Data passes through a pipe sequentially, with bytes being read from the pipe in the same order they were written to the pipe.

There are both named and unnamed pipes. Unnamed pipes never have a pathname.

We use the pipe() function to create a pipe. The pipe() system call returns -1 if an error occurs. 

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

int main(void){

    int myPipe[2];

    if((pipe(myPipe))<0){
        printf("Error creating pipe.\n");
        exit(EXIT_FAILURE);
    }    

    printf("Descriptors are %d and %d\n", myPipe[0], myPipe[1]);

    return 0;

}

The pipe() system call opens two file descriptors and stores them in an int array. The first descriptor is stored in the first element of the array, and is used for reading. The second descriptor is located in the last element of the int array, and is used for writing.

Data travels in one direction through a pipe. One end is used for writing, and the other is used for reading. We can use the read() and write() call to read and write to pipes.

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


#define BUFFER_MAX 255

int main(void){

    char s[BUFFER_MAX+1] = {"With sufficient thrust, pigs fly just fine."};
    char buffer[BUFFER_MAX+1];

    int readLength, myPipe[2];

    if((pipe(myPipe))<0){
        printf("Error creating pipe.\n");
        exit(EXIT_FAILURE);
    }

    printf("writing '%s' to the pipe\n", s);
    
    //write message into the pipe
    write(myPipe[1], s, strlen(s));

    //read the message from the pipe
    readLength = read(myPipe[0], buffer, BUFFER_MAX);
    
    buffer[readLength]='';

    printf("%s\n", buffer);
    
close(myPipe[0]);
close(myPipe[1]);
    
    return 0;

}

Typically, an unnamed path is created in a parent process and then handed to a child process so that the two processes can communicate.  Because a child process inherits any open file descriptors from its parent, it can communicate with its parent. While it is possible for both parent and child processes to both read and write from the pipe, typically one process closes the write file descriptor, the reference to which is stored at index 1, and the other process closes the read file descriptor, the reference to which is stored at index 0.

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

int main(void){

    int fileDescription[2];
    
    int returnVal;

    if(pipe(fileDescription)<0){
        printf("Error opening pipe.\n");
        exit(EXIT_FAILURE);
    }

    if((returnVal=fork())<0){
        printf("Error forking.\n");
        exit(EXIT_FAILURE);
    }

    if(returnVal==0){
        //close write end
        close(fileDes[1]);
    }

    if(returnVal>0){
        //close read end
        close(fileDes[0]);
    }


    return 0;

}

Remember, to connect two processes using a pipe, we follow the pipe() call with a fork() call. Remember as well that the write file descriptor is located at index 1 of the array, and the read file descriptor is located at index 0 of the array.

For our final program, we will send a simple string of characters from our parent process to our child process.

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

#define BUFFER_MAX 255

int main(void){

    int fd[2], messageLength, returnVal;

    char buffer[BUFFER_MAX+1];
    char message[BUFFER_MAX+1];

    if((pipe(fd)<0)){
        printf("Error creating pipe.\n");
        exit(EXIT_FAILURE);
    }

    if((returnVal=fork())<0){
        printf("Error forking.\n");
        exit(EXIT_FAILURE);    
    }
    
    //in child process
    if(returnVal==0){
        //close write descriptor
        close(fd[1]);
        messageLength = read(fd[0], buffer, BUFFER_MAX);
        buffer[messageLength] = '';
        printf("received: '%s'\n", buffer);    
        //close read descriptor
        close(fd[0]);
    }
    if(returnVal>0){
        //in parent process
        //close read descriptor
        close(fd[0]);
        //copy message
        char *s = "A strange game. The only winning move is not to play.";
        strcpy(message, s);
        //send message
        write(fd[1], message, strlen(message));
        //close write descriptor
        close(fd[1]);
        //wait for child process to finish
        waitpid(returnVal, NULL, 0);

        printf("Exiting parent process.\n");
        
        return 0;
    }
    

}


If you’re interested in learning standard C, take a look at my book http://www.amazon.com/Big-Als-C-Standard-ebook/dp/B00A4JGE0M/