Transcript ppt
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
0
1
2
3
4
stdin
stdout
stderr
Parent File
Descriptor table
0
1
2
3
4
stdin
stdout
stderr
Child File
Descriptor table
Terminal
info
Terminal
info
Terminal
info
System file table
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;
int fd[2];
pid_t pid;
char line[80];
// track of num bytes read
// hold fds of both ends of pipe
// pid of child process
// hold text read/written
Continued …
if (pipe(fd) < 0)
perror("pipe error");
// create the pipe
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 {
close(fd[1]);
n = read(fd[0], line, 80);
write(1, line, n);
}
exit(0);
}
// child process
// close write end
// read from pipe
// echo to screen
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
0
1
2
3
4
stdin
stdout
stderr
fd[0]
fd[1]
Parent File Descriptor table
0
1
2
3
4
stdin
stdout
stderr
fd[0]
pipe info e.g.,
read offset
pipe info e.g.,
write offset
System file table
fd[1]
Child File Descriptor table
After Fork
Fork and Pipes
0
1
2
3
4
stdin
stdout
stderr
fd[0] = NULL
fd[1]
Parent File Descriptor table
0
1
2
3
4
stdin
stdout
stderr
fd[0]
fd[1] = NULL
Child File Descriptor table
pipe info e.g.,
read offset
pipe info e.g.,
write offset
System file table
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
Terminal info
Parent
File
Desc.
table
0
1
2
3
4
stdin
stdout
stderr
fds[0]
fds[1]
Terminal info
Terminal info
Shared mem. info:
read
Shared mem. Info:
write
System file table
Terminal info
Example
Terminal info
Terminal info
Shared mem. Info:
read
Shared mem. Info:
write
System file table
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
Child
File
Desc.
table
0
1
2
3
4
0
1
2
3
4
stdin
stdout
stderr
fds[0]
fds[1]
stdin
stdout
stderr
fds[0]
fds[1]
Terminal info
Terminal info
Terminal info
Shared mem. Info:
read
Shared mem. Info:
write
System file table
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
0
1
2
3
4
stdin
stdout
stderr
fd[0]
fd[1]
Parent File Descriptor table
0
1
2
3
4
stdin
stdout
stderr
fd[0]
pipe info e.g.,
read offset
pipe info e.g.,
write offset
System file table
fd[1]
Child File Descriptor table
After Fork
Fork and Pipes
0
1
2
3
4
stdin
stdout
stderr
fd[0] = NULL
fd[1]
Parent File Descriptor table
0
1
2
3
4
stdin
stdout
stderr
fd[0]
fd[1] = NULL
Child File Descriptor table
pipe info e.g.,
read offset
pipe info e.g.,
write offset
System file table
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
Terminal info
Parent
File
Desc.
table
Child
File
Desc.
table
stdin
0
stdout
1
stderr
2
3 fds[0]=NULL
fds[1]
4
stdin
stdout
stderr
fds[0]
0
1
2
3
4 fds[1]=NULL
Terminal info
Terminal info
Shared mem. info:
read
Shared mem. info:
write
System file table
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
Terminal info
Parent
File
Desc.
table
Child
File
Desc.
table
stdin
0
stdout
1
stderr
2
3 fds[0]=NULL
fds[1]
4
stdin
stdout
stderr
fds[0]
0
1
2
3
4 fds[1]=NULL
Terminal info
Terminal info
Shared mem. info:
read
Shared mem. info:
write
System file table
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);