NEXT UP previous
Next: FIFOs

I/O Redirection

With the aid of one additional system call, all the I/O redirection functionality available through the shell can be programmed in your own code. The additional system call is called dup(). The prototype for dup() is:

	#include <unistd.h>

	int dup(int fd);

What the dup() call does is to take a file descriptor as its parameter and return another file descriptor that is a duplicate copy of the first. This means that both file descriptors are associated with the same open file description. The dup() call searches through the file descriptor array from the beginning and makes its copy into the first free file descriptor it encounters. This is illustrated in Figure 1, which shows the effect of using the dup() system call on file descriptor 2. The file descriptor is duplicated into the first free slot in the file descriptor array. In this example, file descriptor 0 is free to be used and, consequently, the value 0 will be returned from the dup( call.

I/O redirection and pipes between processes, such as are used by the shell, are now easy to set up. Looking at pipes first, the scenario is that you enter a pipeline as a command to the shell such as:

	$ ls | wc -w

Remember that the ls and wc commands do not need to be aware that the redirection is taking place; they just continue to use their standard input and standard output files for all their input and output requirements. This means that the shell has to create a pipe and then fork() the two child processes, manipulating their file descriptors to make the pipe correspond to the output of the first process in the pipeline and to the input of the second.

The following listing is a simple program which executes the pipeline ls | wc -w from the previous example:

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

	nain()
	{
		int fda[2];

		if (pipe(fda)==-1)					/* Create pipe */
			fatal("creating pipe");

		switch (fork())/* Create child */
		{
			case -1:
				fatal("forking child"); 
				break;

			case 0:
				close(1);                           /* Run ls in child */
				dup(fda[1]);                  /* Close standard output */
				close (fda[1]);            /* Duplicate pipe write end */
				close(fda[0]);                 /* Close pipe write end */
				execlp("ls", "ls", 0);          /* Close pipe read end */
				fatal("trying to exec ls");      /* Execute ls command */
				break;

			default:                                   /* Run wc in parent */
				close(0);                      /* Close standard input */
				dup(fda[0]);                /* Duplicate pipe read end */
				close (fda[0]);                 /* Close pipe read end */
   		}
		close(fda[1]);	                               /* Close pipe write end */
		execlp( "wc", "wc", "-w", 0);                    /* Execute wc command */ 
		fatal("trying to exec wc");
		break;
	}




	fatal(char *mess)
	{
		fprintf (stderr, "Error: %s\n", mess); 
		exit(1)
	}

Notice how the redirection into and out of the pipe is accomplished before the exec() calls are made to execute the ls and wc commands.

The child process will be used to execute the ls command. In this case, it is necessary to set things up so that when ls writes to the standard output device it will actually be writing to the pipe. To do this, the first step is to close() the current standard output device and then use dup() to duplicate the write end of the pipe. Remember, dup() will make its copy in the lowest numbered free file descriptor, which in this case will be the standard output file descriptor that we just closed. As standard output is now associated with the write end of the pipe, any write() calls to this file descriptor will automatically send the data to the pipe.

After this, both of the original file descriptors to the pipe can be closed and it is this arrangement of file descriptors that will then be passed on to the ls command by the execlp() call.

Similarly, the parent process will be used to execute the wc command, meaning that it must be re-arranged to take its input from the pipe. This is achieved using the same method as in the previous case. The current standard input file descriptor is closed so that, using dup(,it can be reallocated as the read end of the pipe. Then both the original pipe file descriptors can be closed before the execlp() call executes the wc command with this arrangement of file descriptors set up.

The ls and wc commands will then both execute as normal, reading and writing their standard input and output file descriptors, unaware that they are in communication.

Standard input and output redirection to files is achieved in similar ways, as the following example code shows. The code is designed to execute the command:

	$ cat <file1 >file2

In this case the program will close each of its standard input and output file descriptors in turn and use open() to open the required files and have them associated with the standard input and output file descriptors:

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

	#define WRFLAGS (O_WRONLY | O_CREAT | O_TRUNC) 
	#define MODE6OO (S_IRUSR | S_IWUSR)

	main()
	{
		close (0);				/* Close standard input */

		if (open("file1", O_RDONLY)==-1) 
			fatal("opening input file");
		close(1);			       /* Close standard output */

		if (open("file2", WRFLAGS, MODE6OO)==-l) 
			fatal("opening output file");

		execlp("cat", "cat", 0);                 /* Execute cat command */
		fatal("trying to exec cat");
	}



	fatal(char *mess)
	{
		fprintf(stderr, "Error: %s\n", mess); 
		exit(1);
	}

There are two other system calls which can duplicate file descriptors. the first is dup2(), with the prototype:

	#include <unistd.h>

	int dup2(int oldfd, int newfd);

This call makes newfd a copy of oldfd. It will also close newfd before the duplication operation if it is already open.

The other way to duplicate file descriptors is to use another of the miscellaneous facilities of the fcntl() system call:

	#include <fcntl.h>

	newfd = fcntl(oldfd, F_DUPFD minfd);

This provides a facility which is a cross between dup() and dup2(). The fcntl() call looks for a free file descriptor starting with minfd. When it finds one it uses it to duplicate oldfd. The duplicate file descriptor is then returned by fcntl() as newfd.


NEXT UP previous
Next: FIFOs