The working of Piping in a Linux Shell

We use piping to utilize the output of one command as an input to another. For example , if we wish to count the number of files in a directory(not recursive)

$ls -n| wc -l

The question is how does piping work.
I would like to use the same code to explain piping.

The basic idea of piping works on the system calls like dup, close and pipe.
pipe()

int pipe(int fd[2]);

pipe() creates a pair of file descriptors, pointing to a pipe inode, and places them in the array pointed to by filedes. fd[0] is for reading, fd[1] is for writing.

close()

int close(int fd);

close() closes a file descriptor, so that it no longer refers to any file and may be reused.

dup()

int dup(int fd);

dup() create a copy of the file descriptor fd

Let’s see how using these system calls, the piping works. For this let’s check a piece of code

int some_fd=open("abc",O_RDONLY);
close(0);
fd=dup(some_fd);
printf("%d\n",fd);

The output of this code is

0

how??
This is because as soon as we close the file descriptor 0, the slot 0 in the file table becomes vacant. So when we issue the dup system call, it duplicates the file descriptor and puts into the slot 0. So from now whatever we write to stdin is written to the file ‘abc’

This idea is utilized in piping in the shell. We create a pipe and create a new process. In the child process, we close stdin and in parent stdout (or vice versa). In both of the processes we also issue the dup call immediately and at the same time close the write end in one and read end in another

Let’s check the program. The program on execution expects two commands given as

$ cmd1 | cmd2

The program

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

#define CMD_LEN 1024    //Maximum length of Command
#define MAX_LEN 1024    //Maximum Length of the input
#define MAX_CMD 2       //Maximum number of Commands
#define ARG_LEN 128     //Maximum Length of Argument
#define ARG_COUNT 64    //Maximum number of arguments

int main(int argc, char *argv[], char *envp[])
{
        while(1){
                char *arg[MAX_CMD][ARG_COUNT];
                char cmd[MAX_LEN];

                char *command[MAX_CMD+1];
                char *pos=&cmd[0];
                int cmd_len;  int pid;  int status;

                printf("\n$ ");           //Displaying the prompt
                fgets(cmd,MAX_LEN,stdin); //Reading user input/
                cmd_len=strnlen(cmd,MAX_LEN);

                int i=0,k;
                do{
                        int len;
                        char a[CMD_LEN];
                        sscanf(pos,"%[^|]",a);//Parsing out the commands
                        len=strnlen(a,CMD_LEN);

                        command[i]=malloc(len);
                        if(!command[i]){
                                printf("Unable to allocate memory\n");
                                exit(1);
                        }

                        strncpy(command[i++],a,CMD_LEN);
                        pos+=(len+1);    //Repositioning for next command
                }while(pos<cmd+cmd_len);

                command[i]=NULL;

                i=0;

                while(command[i]){
                        pos=command[i];
                        int arg_count=0;
                        cmd_len=strnlen(command[i],CMD_LEN);

                        do{
                                int len;
                                char a[ARG_LEN];
                                int count=sscanf(pos,"%s",a);

                                //Parsing out the arguments
                                if(count < 1)
                                        break;

                                len=strnlen(a,ARG_LEN);

                                arg[i][arg_count]=malloc(len);
                                if(!arg[i][arg_count]){
                                        printf("Unable to allocate memory\n");
                                        exit(1);
                                }

                                strncpy(arg[i][arg_count++],a,ARG_LEN);
                                pos+=(len+1);  //Repositioning for next argument
                        }while(pos<command[i]+cmd_len);

                        arg[i][arg_count]=NULL;   i++;
                }

                if(i!=2){
                        printf("Two commands required");
                        continue;
                }

                //Obtained the arguments

                pid=fork();                     //Creating a process to execute command 2

                if(pid==0){
                        int fd[2];
                        if(pipe(fd)==-1){       //Creating a Pipe
                                perror("pipe:");
                                exit(1);
                        }

                        pid=fork();             //Creating process to execute command 1

                        if(pid==0){
                                close(fd[0]);
                                close(1);        //closing stdout

                                if(dup(fd[1])==-1){      //stdout now points to fd[1]
                                        perror("dup");
                                        exit(1);
                                }

                                //In child process
                                if(execve(arg[0][0],&arg[0][0],envp)==-1){
                                        perror("execve");
                                }

                                exit(1); //Reached if Child failed to execute process
                        }

                        else{
                                close(fd[1]);//close the write end
                                close(0);//closing the stdin

                                if(dup(fd[0])==-1){//stdin now points to fd[0]
                                        perror("dup:");
                                        exit(1);
                                }

                                //In parent process
                                if(execve(arg[1][0],&arg[1][0],envp)==-1){
                                        perror("execve");
                                }
                                exit(1); //Reached if failed to execute process
                        }
                }
                wait(&status);    //Wait for process to terminate //continue
        }
        return 0;
}

Output

$  /bin/ls / | /bin/grep root
root

$
Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s