Shell (Part 2)
Example r What if we want to support something like this: m ps –le | sort r One process should execute ps –le and another should execute sort r By default a command like ps requires that its output goes to standard output i.e., the terminal (stdout) r The sort command requires that a file be provided as a command line argument from standard input (stdin)
First Attempt pid = fork(); if (pid<0) { perror("Problem forking"); exit(1); } else if (pid>0) { /* parent process */ execlp("ps","ps","-le", NULL); perror("exec problem"); exit(1); } else { /* child process */ } execlp("sort","sort",NULL); perror("exec problem"); exit(1); } return(0); }
Example r Why doesn’t this work? m The output of the ps -le goes to the terminal r We want it to be the input to the sort r The diagram on the next page shows the status of the file descriptor tables after the fork
Fork and Files Parent File Descriptor table stdin stdout stderr System file table Terminal info stdin stdout stderr Child File Descriptor table Terminal info
What is Needed? r Assume process P 1 is to execute ps –le and that P 2 is to execute sort r There is a need for shared memory that allows P 1 to write the results of ps –le to the shared memory r P 2 should be able to read the results from the shared memory and provide the results to the sort command
Pipes r The pipe function can be used to provide the shared memory r We will first provide a general discussion of the pipe function which is to be followed by a discussion of how it applies to executing ps –le | sort
Pipes r Pipes can be used between processes that have a common ancestor r Typical use: m Pipe created by a process m Process calls fork() m Pipe used between parent and child m Allows for communication between processes
Creating a Pipe #include int pipe(int filedes[2]); r Returns 0 if ok, -1 on error r Returns two file descriptors m filedes[0] is open for reading m filedes[1] is open for writing
Example #include int main(void){ int n; // track of num bytes read int fd[2]; // hold fds of both ends of pipe pid_t pid; // pid of child process char line[80]; // hold text read/written
Continued … if (pipe(fd) < 0) // create the pipe perror("pipe error"); if ((pid = fork()) < 0) { // fork off a child perror("fork error"); } else if (pid > 0) { // parent process close(fd[0]); // close read end write(fd[1], "hello world\n", 12); // write to it wait(NULL); }…
Continued… else { // child process close(fd[1]); // close write end n = read(fd[0], line, 80); // read from pipe write(1, line, n); // echo to screen } exit(0); }
Fork and Pipes r A fork copies the file descriptor table to the child r The parent should close one of the file descriptors while the child should close the other r Example code on the two previous slides: m Parent closes fd[0] since it does not read from it m Child closes fd[1] since it does not write
Fork and Pipes Parent File Descriptor table stdin stdout stderr fd[0] System file table pipe info e.g., read offset stdin stdout stderr fd[0] Child File Descriptor table fd[1] pipe info e.g., write offset After Fork
Fork and Pipes Parent File Descriptor table stdin stdout stderr fd[0] = NULL System file table pipe info e.g., read offset stdin stdout stderr fd[0] Child File Descriptor table fd[1] fd[1] = NULL pipe info e.g., write offset After Closing Ends
Pipes r By default, if a writing process attempts to write to a full pipe, the system will automatically block the process until the pipe is able to receive the data r Likewise, if a read is attempted on an empty pipe, the process will block until data is available r In addition, the process will block if a specified pipe has been opened for reading, but another process has not opened the pipe for writing
Pipe Capacity r The OS has a limit on the buffer space used by the pipe m If you hit the limit, write will block
Example r We will now show how pipes can be used for supporting the execution of ps –le | sort
Example r First let us m Create shared memory that is to be used by the parent and child processes m This is done using the pipe function r The pipe function is executed before the fork function r The results of ps –le should be put into the shared memory to be used by the child process for sort r See next slide for code r The slide after code slide depicts the file descriptor table and System File table
Example int main(int argc, char **argv) { int fds[2]; pid_t pid; /* attempt to create a pipe */ if (pipe(fds)<0) { perror("Fatal Error"); exit(1); }
Example Parent File Desc. table fds[0] fds[1] stdin stdout stderr System file table Terminal info Shared mem. info: read Shared mem. Info: write
Example System file table Terminal info Shared mem. Info: read Shared mem. Info: write Shared Memory
Example r Each entry in the system file table has information about the “file” which could be the terminal, disk file or pipe (shared memory) r For shared memory created by the pipe function: m The read descriptor includes information about the last location read from m The write descriptor includes information about the last location written to.
Example r Let us now add the code for the fork r See next slide for the code
Example /* create another process */ pid = fork(); if (pid<0) { perror("Problem forking"); exit(1); } …………….. What is the status of the file descriptor table
Example Parent File Desc. table fds[0] fds[1] stdin stdout stderr System file table fds[0] fds[1] stdin stdout stderr Child File Desc. table Terminal info Shared mem. Info: read Shared mem. Info: write
Fork and Pipes r A fork copies the file descriptor table to the child r The parent should close one of the file descriptors while the child should close the other
Fork and Pipes Parent File Descriptor table stdin stdout stderr fd[0] System file table pipe info e.g., read offset stdin stdout stderr fd[0] Child File Descriptor table fd[1] pipe info e.g., write offset After Fork
Fork and Pipes Parent File Descriptor table stdin stdout stderr fd[0] = NULL System file table pipe info e.g., read offset stdin stdout stderr fd[0] Child File Descriptor table fd[1] fd[1] = NULL pipe info e.g., write offset After Closing Ends
Example r We want the output of the ps –le command to be put into the shared memory r The sort command should read from the shared memory r Two issues: m The sort command assumes that it receives its input from stdin m The ps command assumes that it outputs to stdout r We need to “reroute” m This can be done using the dup() function
dup() and dup2 #include int dup(int filedes1); int dup2(int filedes1, int filedes2); r Both will duplicate an existing file descriptor r dup() returns lowest available file descriptor, now referring to whatever filedes1 refers to r dup2() - filedes2 (if open) will be closed and then set to refer to whatever filedes1 refers to
Example r Now we want what would normally go to the standard output to go to the shared memory r This is done with the following code: if ( dup2(fds[1],STDOUT_FILENO)<0) { perror("can't dup"); exit(1); } r The new parent file descriptor table is on the next page
Example Parent File Desc. table fds[0]=NULL fds[1] stdin stdout stderr System file table fds[0] fds[1]=NULL stdin stdout stderr Child File Desc. table Terminal info Shared mem. info: read Shared mem. info: write
Example r Now want to set it up so that the child reads from the shared memory r This is done with the following code: if ( dup2(fds[0],STDIN_FILENO)<0) { perror("can't dup"); exit(1); } r The new child file descriptor is on the next page
Example Parent File Desc. table fds[0]=NULL fds[1] stdin stdout stderr System file table fds[0] fds[1]=NULL stdin stdout stderr Child File Desc. table Terminal info Shared mem. info: read Shared mem. info: write
Example r Let us now put it together
Example /* create another process */ pid = fork(); if (pid<0) { perror("Problem forking"); exit(1); } else if (pid>0) { /* parent process */ close(fds[0]); /* close stdout, reconnect to the writing end of the pipe */ if ( dup2(fds[1],STDOUT_FILENO)<0) { perror("can't dup"); exit(1); } execlp("ps","ps","-le", NULL); perror("exec problem"); exit(1);
Example } else { /* child process */ close(fds[1]); if (dup2(fds[0],STDIN_FILENO) < 0) { perror("can't dup"); exit(1); } execlp("sort","sort",NULL); perror("exec problem"); exit(1); } return(0); }