- Nice example that uses various OS abstractions. See UNIX
paper if you are unfamiliar with the shell's implementation.
- Final lab is a simple shell.
- Basic structure:
while (1) {
printf ("$");
readcommand (command, args);
if ((pid = fork ()) == 0) { // child?
exec (command, args, 0);
} else if (pid > 0) { // parent?
wait (0); // wait for child to terminate
} else {
printf ("Unable to fork\n");
}
}
- Example:
$ ls
- why call "wait"? if child finishes, it becomes a zombie
- I/O: file descriptors. Every process starts with:
- How does the shell implement:
$ls > tmp1
just before exec insert:
close (1);
fd = open ("tmp1", O_CREAT|O_WRONLY); // fd will be 1!
- How does the shell implement sharing an output file:
$ls 2> tmp1
replace last code with:
close (1);
close (2);
fd1 = open ("tmp1", O_CREAT|O_WRONLY); // fd will be 1!
fd2 = dup (fd);
both file descriptors share offset
- how do programs communicate?
$ sort file.txt | uniq | wc
or
$ sort file.txt > tmp1
$ uniq tmp1 > tmp2
$ wc tmp2
$ rm tmp1 tmp2
or
$ kill signal
- A pipe is an one-way communication channel. Here is an example
where the parent is the writer and the child is the reader:
int fd[2];
if (pipe(fd) < 0) panic ("error");
if ((pid = fork()) < 0) panic ("error");
else if (pid > 0) {
close(fd[0]);
write(fd[1], "hello world\n", 12);
} else {
close(fd[1]);
n = read (fd[0], buf, MAXBUF);
write (1, buf, n);
}
- How does the shell implement pipelines (i.e., cmd 1 | cmd 2 | ...)?
Here is a sketch of the modificaitons to our shell that sets up
a pipe between cmd1 and cmd2:
int fd[2];
if (pipe(fd) < 0) panic ("error");
if ((pid = fork ()) == 0) {
close (1);
tmp = dup (fd[1]); // fd[1] is the write end
close (fd[0]); // close read end
exec (command1, args1, 0);
} else if (pid > 0) {
close (0);
tmp = dup (fd[0]); // fd[0] is the read end
close (fd[1]); // close write end
exec (command2, args2, 0);
} else {
printf ("Unable to fork\n");
}
- Why close read-end and write-end? multiple reasons: maintain that
every process starts with 3 file descriptors and reading from an empty
pipe blocks reader, while reading from a closed pipe returns end of file.
- How do you background jobs?
$ compute &
- How does the shell implement "&", backgrounding? (Don't call wait
immediately).
- More details at: /v6/usr/source/s2/sh.c