shell (part 2). example r what if we want to support something like this: m ps –le | sort r one...
TRANSCRIPT
Shell (Part 2)
Example What if we want to support something like
this: ps –le | sort
One process should execute ps –le and another should execute sort
By default a command like ps requires that its output goes to standard output i.e., the terminal (stdout)
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
Why doesn’t this work? The output of the ps -le goes to the terminal
We want it to be the input to the sort The diagram on the next page shows
the status of the file descriptor tables after the fork
Fork and Files
Parent File Descriptor table
01234
stdinstdoutstderr
System file table
Terminal info
01234
stdinstdoutstderr
Child File Descriptor table
Terminal info
Terminal info
What is Needed?
Assume process P1 is to execute ps –le and that P2 is to execute sort
There is a need for shared memory that allows P1 to write the results of ps –le to the shared memory
P2 should be able to read the results from the shared memory and provide the results to the sort command
Pipes
The pipe function can be used to provide the shared memory
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
Pipes can be used between processes that have a common ancestor
Typical use: Pipe created by a process Process calls fork() Pipe used between parent and child Allows for communication between
processes
Creating a Pipe
#include <unistd.h> int pipe(int filedes[2]);
Returns 0 if ok, -1 on error Returns two file descriptors
filedes[0] is open for reading filedes[1] is open for writing
Example #include <unistd.h>#include <stdio.h>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
A fork copies the file descriptor table to the child
The parent should close one of the file descriptors while the child should close the other
Example code on the two previous slides: Parent closes fd[0] since it does not read
from it Child closes fd[1] since it does not write
Fork and Pipes
Parent File Descriptor table
01234
stdinstdoutstderr fd[0]
System file table
pipe info e.g., read offset
01234
stdinstdoutstderrfd[0]
Child File Descriptor table
fd[1]
fd[1]
pipe info e.g., write offset
After Fork
Fork and Pipes
Parent File Descriptor table
01234
stdinstdoutstderr
fd[0] = NULL
System file table
pipe info e.g., read offset
01234
stdinstdoutstderrfd[0]
Child File Descriptor table
fd[1]
fd[1] = NULL
pipe info e.g., write offset
After Closing Ends
Pipes 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
Likewise, if a read is attempted on an empty pipe, the process will block until data is available
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
The OS has a limit on the buffer space used by the pipe If you hit the limit, write will block
Example
We will now show how pipes can be used for supporting the execution of ps –le | sort
Example First let us
Create shared memory that is to be used by the parent and child processes
This is done using the pipe function The pipe function is executed before the fork
function The results of ps –le should be put into the
shared memory to be used by the child process for sort
See next slide for code 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
01234
fds[0] fds[1]
stdinstdoutstderr
System file table
Terminal info
Terminal info
Terminal info
Shared mem. info: read Shared mem. Info:write
Example
System file table
Terminal info
Terminal info
Terminal info
Shared mem. Info: read
Shared mem. Info:write
Shared Memory
Example
Each entry in the system file table has information about the “file” which could be the terminal, disk file or pipe (shared memory)
For shared memory created by the pipe function: The read descriptor includes information
about the last location read from The write descriptor includes information
about the last location written to.
Example
Let us now add the code for the fork 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
01234
fds[0] fds[1]
stdinstdoutstderr
System file table
01234
fds[0] fds[1]
stdinstdoutstderr
Child File Desc.table
Terminal info
Terminal info
Terminal info
Shared mem. Info: read
Shared mem. Info:write
Fork and Pipes
A fork copies the file descriptor table to the child
The parent should close one of the file descriptors while the child should close the other
Fork and Pipes
Parent File Descriptor table
01234
stdinstdoutstderr fd[0]
System file table
pipe info e.g., read offset
01234
stdinstdoutstderrfd[0]
Child File Descriptor table
fd[1]
fd[1]
pipe info e.g., write offset
After Fork
Fork and Pipes
Parent File Descriptor table
01234
stdinstdoutstderr
fd[0] = NULL
System file table
pipe info e.g., read offset
01234
stdinstdoutstderrfd[0]
Child File Descriptor table
fd[1]
fd[1] = NULL
pipe info e.g., write offset
After Closing Ends
Example
We want the output of the ps –le command to be put into the shared memory
The sort command should read from the shared memory
Two issues: The sort command assumes that it receives its
input from stdin The ps command assumes that it outputs to
stdout We need to “reroute”
This can be done using the dup() function
dup() and dup2
#include <unistd.h>int dup(int filedes1);int dup2(int filedes1, int filedes2);
Both will duplicate an existing file descriptor dup() returns lowest available file descriptor,
now referring to whatever filedes1 refers to dup2() - filedes2 (if open) will be closed and
then set to refer to whatever filedes1 refers to
Example Now we want what would normally go to
the standard output to go to the shared memory
This is done with the following code:if ( dup2(fds[1],STDOUT_FILENO)<0) {
perror("can't dup"); exit(1);}
The new parent file descriptor table is on the next page
Example
Parent File Desc. table
01234
fds[0]=NULL
fds[1]
stdinstdoutstderr
System file table
01234
fds[0] fds[1]=NULL
stdinstdoutstderr
Child File Desc.table
Terminal info
Terminal info
Terminal info
Shared mem. info: read
Shared mem. info:write
Example Now want to set it up so that the child
reads from the shared memory This is done with the following code:
if ( dup2(fds[0],STDIN_FILENO)<0) { perror("can't dup"); exit(1);}
The new child file descriptor is on the next page
Example
Parent File Desc. table
01234
fds[0]=NULL
fds[1]
stdinstdoutstderr
System file table
01234
fds[0] fds[1]=NULL
stdinstdoutstderr
Child File Desc.table
Terminal info
Terminal info
Terminal info
Shared mem. info: read
Shared mem. info:write
Example
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);}