Operating Systems Lecture 11 UNIX Pipes Read Handout "An Introduction to Concurrency..." 4.1 File Redirection in UNIX Everything in UNIX is a file. Commands are files. Pipes read and write to a buffer that is a file. Many UNIX commands read and write to stdin and stdout. These are also treated as files. stdin has a special file descriptor: 0 stdout has a special file descriptor: 1 To redirect input or output we attach the file descriptors for stdin or stdout to the file we want to redirect the input (or output) to. 4.2 Steps for File Redirection To redirect the input (or output) from stdin (or stdout) to a file we must carry out the following steps: 1. Open the file for reading (or writing). 2. Attach the file descriptor for stdin (or stdout) to the newly opened file. 3. Close the file descriptor for the opened file. 4.3 Opening a File Use the command, open( ) to open a file for reading or writing. int fd; // file descriptor fd = open( fileName, FLAGS, permissions); open( ) opens the file whose name is given by the string, fileName, and returns an integer file descriptor to be used to access the file. fileName is a string, giving the name of the file. FLAGS: a list of flags that indicate read/write status of file. permissions: Indicates read/write permissions for the file (only needed if creating a new file). 4.4 FLAGS for open( ) The flags indicate the read/write status of the file: O_RDONLY Open the file for reading. O_WRONLY Open the file for writing. O_CREAT Create the file if it doesn't exist. O_APPEND Open the file for appending O_TRUNC Truncate the length of the file to zero. Flags can be combined by a bitwise OR (|): fd = open(myFile, O_WRONLY | O_APPEND); Other flags are described on the man page for open. 4.5 File Permissions File read/write permission can be indicated as a 3 digit integer, with each digit indicating the permissions for the user/group/others. Each digit represents a 3 bit binary number, with each bit representing read, write or execute permission. 1 indicates permission for that function 0 indicates no permission for that function Example: Permission: rwxr-xr-111101100 7 5 4 fd = open(myFile, O_WRONLY | O_CREAT, 754); 4.6 Attaching the File Descriptor to stdin (or stdout) Use the function dup2( ) to attach the file descriptor for our open file to stdin (or stdout): dup2( fd, 0); // redirect stdin to file with descriptor fd dup2( ) causes the second file descriptor (in this case the descriptor for stdin) to refer to the same file as the first file descriptor (in this case our open file). To redirect to stdout, use dup2(fd, 1). Once the input or output is redirected, you should close the new file descriptor (it is no longer needed): close(fd); 4.7 Example: Redirect Output //The preliminaries #include <iostream.h> #include <sys/wait.h> #include <fcntl.h> #include <stdlib.h> #include <unistd.h> int main(void) { int cpid; char myFile[20]; int myFileD; int done, status; 4.8 Redirect output (continued) cout << "Enter output file name: "; cin >> myFile; myFileD = open(myFile, O_WRONLY | O_CREAT | O_TRUNC, 0700); cpid = fork( ); if (cpid == 0) { //Child process dup2(myFileD, 1); //Attach stdout to file close(myFileD); //myFileD no longer needed execlp("ls", "ls", "-l", NULL); //Execute ls command } else { //Parent process done = wait(&status); //wait for child to finish cout << "All done!" << endl; } return 0; } Demo 1 4.9 Redirect input cout << "Enter input file name: "; cin >> myFile; myFileD = open(myFile, O_RDONLY); cpid = fork( ); if (cpid == 0) { //Child process dup2(myFileD, 0); //Attach stdin to file close(myFileD); //myFileD no longer needed execlp("wc", "wc", "-l", NULL); //Execute wc command } else { //Parent process done = wait(&status); //wait for child to finish cout << "All done!" << endl; } return 0; } Demo 2 4.10 UNIX Pipes Unix pipes allow communication between two processes using a buffer. One process writes to the buffer. One process reads from the buffer. The communication is unidirectional. The buffer is accessed using a file descriptor. (Recall that everything in UNIX is a file). 4.11 Creating a pipe A pipe is represented by an integer array of length 2. int fd[2]; fd[0]: file descriptor for reading from the pipe. fd[1]: file descriptor for writing to the pipe. Use the pipe( ) function to initialize the pipe: pipe(fd); (This should be done by the parent process). To communicate using a pipe: One process opens fd[0] (for reading) and closes fd[1]. The other process opens fd[1] (for writing) and closes fd[0]. 4.12 #define MAX 25 int main(void) { int cpid; int fd[2]; char line[MAX]; int done, status; pipe(fd); cpid = fork( ); if (cpid == 0) { cout << "The child process is active." << endl; close(fd[1]); //close writing end of pipe read(fd[0], line, MAX); //read a max of 25 chars into line cout << "The string received is " << line << endl; } else { cout << "The parent is active." << endl; close(fd[0]); //close reading end of pipe write(fd[1], "Your parent is calling", 23); //write to pipe done = wait(&status); cout << "All done!" << endl; } return 0; Demo 3 } 4.13 Using the pipes to redirect stdin and stdout In UNIX, pipes are often used to direct the output of one command to the input of a second command. Example: who | wc -l who normally directs the output to stdout. We want to redirect the output to the writing end of the pipe. wc normally reads input from stdin. We want to redirect that input to come from the reading end of the pipe. We redirect the input and output the same way we did for file redirection. 4.14 Example: The first child int main(void) { int cpid, cpid2; int fd[2]; int done, status; pipe(fd); cpid = fork( ); if (cpid == 0) { cout << "The first child process is active." << endl; close(fd[1]); //close writing end of pipe dup2(fd[0], 0); //redirect std in to reading end of pipe close(fd[0]); execlp("wc", "wc", "-l", NULL); 4.15 Example (cont'd): The second child } else { cpid2 = fork(); if ( cpid2 == 0){ cout << "The second child is active." << endl; close(fd[0]); //close reading end of pipe dup2(fd[1], 1); //redirect stdout to writing end of pipe close(fd[1]); execlp("who", "who", NULL); 4.16 Example (cont'd): The parent } else { cout << "The parent is active." << endl; close(fd[0]); //close reading end of pipe close(fd[1]); //close writing end of pipe done = waitpid(cpid, &status, 0); //wait for children done = waitpid(cpid2, &status, 0); cout << "All done!" << endl; } } return 0; } Demo 4 4.17