288 lines
7.8 KiB
HTML
288 lines
7.8 KiB
HTML
<title>L1</title>
|
|
<html>
|
|
<head>
|
|
</head>
|
|
<body>
|
|
|
|
<h1>OS overview</h1>
|
|
|
|
<h2>Overview</h2>
|
|
|
|
<ul>
|
|
<li>Goal of course:
|
|
|
|
<ul>
|
|
<li>Understand operating systems in detail by designing and
|
|
implementing miminal OS
|
|
<li>Hands-on experience with building systems ("Applying 6.033")
|
|
</ul>
|
|
|
|
<li>What is an operating system?
|
|
<ul>
|
|
<li>a piece of software that turns the hardware into something useful
|
|
<li>layered picture: hardware, OS, applications
|
|
<li>Three main functions: fault isolate applications, abstract hardware,
|
|
manage hardware
|
|
</ul>
|
|
|
|
<li>Examples:
|
|
<ul>
|
|
<li>OS-X, Windows, Linux, *BSD, ... (desktop, server)
|
|
<li>PalmOS Windows/CE (PDA)
|
|
<li>Symbian, JavaOS (Cell phones)
|
|
<li>VxWorks, pSOS (real-time)
|
|
<li> ...
|
|
</ul>
|
|
|
|
<li>OS Abstractions
|
|
<ul>
|
|
<li>processes: fork, wait, exec, exit, kill, getpid, brk, nice, sleep,
|
|
trace
|
|
<li>files: open, close, read, write, lseek, stat, sync
|
|
<li>directories: mkdir, rmdir, link, unlink, mount, umount
|
|
<li>users + security: chown, chmod, getuid, setuid
|
|
<li>interprocess communication: signals, pipe
|
|
<li>networking: socket, accept, snd, recv, connect
|
|
<li>time: gettimeofday
|
|
<li>terminal:
|
|
</ul>
|
|
|
|
<li>Sample Unix System calls (mostly POSIX)
|
|
<ul>
|
|
<li> int read(int fd, void*, int)
|
|
<li> int write(int fd, void*, int)
|
|
<li> off_t lseek(int fd, off_t, int [012])
|
|
<li> int close(int fd)
|
|
<li> int fsync(int fd)
|
|
<li> int open(const char*, int flags [, int mode])
|
|
<ul>
|
|
<li> O_RDONLY, O_WRONLY, O_RDWR, O_CREAT
|
|
</ul>
|
|
<li> mode_t umask(mode_t cmask)
|
|
<li> int mkdir(char *path, mode_t mode);
|
|
<li> DIR *opendir(char *dirname)
|
|
<li> struct dirent *readdir(DIR *dirp)
|
|
<li> int closedir(DIR *dirp)
|
|
<li> int chdir(char *path)
|
|
<li> int link(char *existing, char *new)
|
|
<li> int unlink(char *path)
|
|
<li> int rename(const char*, const char*)
|
|
<li> int rmdir(char *path)
|
|
<li> int stat(char *path, struct stat *buf)
|
|
<li> int mknod(char *path, mode_t mode, dev_t dev)
|
|
<li> int fork()
|
|
<ul>
|
|
<li> returns childPID in parent, 0 in child; only
|
|
difference
|
|
</ul>
|
|
<li>int getpid()
|
|
<li> int waitpid(int pid, int* stat, int opt)
|
|
<ul>
|
|
<li> pid==-1: any; opt==0||WNOHANG
|
|
<li> returns pid or error
|
|
</ul>
|
|
<li> void _exit(int status)
|
|
<li> int kill(int pid, int signal)
|
|
<li> int sigaction(int sig, struct sigaction *, struct sigaction *)
|
|
<li> int sleep (int sec)
|
|
<li> int execve(char* prog, char** argv, char** envp)
|
|
<li> void *sbrk(int incr)
|
|
<li> int dup2(int oldfd, int newfd)
|
|
<li> int fcntl(int fd, F_SETFD, int val)
|
|
<li> int pipe(int fds[2])
|
|
<ul>
|
|
<li> writes on fds[1] will be read on fds[0]
|
|
<li> when last fds[1] closed, read fds[0] retursn EOF
|
|
<li> when last fds[0] closed, write fds[1] kills SIGPIPE/fails
|
|
EPIPE
|
|
</ul>
|
|
<li> int fchown(int fd, uind_t owner, gid_t group)
|
|
<li> int fchmod(int fd, mode_t mode)
|
|
<li> int socket(int domain, int type, int protocol)
|
|
<li> int accept(int socket_fd, struct sockaddr*, int* namelen)
|
|
<ul>
|
|
<li> returns new fd
|
|
</ul>
|
|
<li> int listen(int fd, int backlog)
|
|
<li> int connect(int fd, const struct sockaddr*, int namelen)
|
|
<li> void* mmap(void* addr, size_t len, int prot, int flags, int fd,
|
|
off_t offset)
|
|
<li> int munmap(void* addr, size_t len)
|
|
<li> int gettimeofday(struct timeval*)
|
|
</ul>
|
|
</ul>
|
|
|
|
<p>See the <a href="../reference.html">reference page</a> for links to
|
|
the early Unix papers.
|
|
|
|
<h2>Class structure</h2>
|
|
|
|
<ul>
|
|
<li>Lab: minimal OS for x86 in an exokernel style (50%)
|
|
<ul>
|
|
<li>kernel interface: hardware + protection
|
|
<li>libOS implements fork, exec, pipe, ...
|
|
<li>applications: file system, shell, ..
|
|
<li>development environment: gcc, bochs
|
|
<li>lab 1 is out
|
|
</ul>
|
|
|
|
<li>Lecture structure (20%)
|
|
<ul>
|
|
<li>homework
|
|
<li>45min lecture
|
|
<li>45min case study
|
|
</ul>
|
|
|
|
<li>Two quizzes (30%)
|
|
<ul>
|
|
<li>mid-term
|
|
<li>final's exam week
|
|
</ul>
|
|
|
|
</ul>
|
|
|
|
<h2>Case study: the shell (simplified)</h2>
|
|
|
|
<ul>
|
|
<li>interactive command execution and a programming language
|
|
<li>Nice example that uses various OS abstractions. See <a
|
|
href="../readings/ritchie74unix.pdf">Unix
|
|
paper</a> if you are unfamiliar with the shell.
|
|
<li>Final lab is a simple shell.
|
|
<li>Basic structure:
|
|
<pre>
|
|
|
|
while (1) {
|
|
printf ("$");
|
|
readcommand (command, args); // parse user input
|
|
if ((pid = fork ()) == 0) { // child?
|
|
exec (command, args, 0);
|
|
} else if (pid > 0) { // parent?
|
|
wait (0); // wait for child to terminate
|
|
} else {
|
|
perror ("Failed to fork\n");
|
|
}
|
|
}
|
|
</pre>
|
|
<p>The split of creating a process with a new program in fork and exec
|
|
is mostly a historical accident. See the <a
|
|
href="../readings/ritchie79evolution.html">assigned paper</a> for today.
|
|
<li>Example:
|
|
<pre>
|
|
$ ls
|
|
</pre>
|
|
<li>why call "wait"? to wait for the child to terminate and collect
|
|
its exit status. (if child finishes, child becomes a zombie until
|
|
parent calls wait.)
|
|
<li>I/O: file descriptors. Child inherits open file descriptors
|
|
from parent. By convention:
|
|
<ul>
|
|
<li>file descriptor 0 for input (e.g., keyboard). read_command:
|
|
<pre>
|
|
read (1, buf, bufsize)
|
|
</pre>
|
|
<li>file descriptor 1 for output (e.g., terminal)
|
|
<pre>
|
|
write (1, "hello\n", strlen("hello\n")+1)
|
|
</pre>
|
|
<li>file descriptor 2 for error (e.g., terminal)
|
|
</ul>
|
|
<li>How does the shell implement:
|
|
<pre>
|
|
$ls > tmp1
|
|
</pre>
|
|
just before exec insert:
|
|
<pre>
|
|
close (1);
|
|
fd = open ("tmp1", O_CREAT|O_WRONLY); // fd will be 1!
|
|
</pre>
|
|
<p>The kernel will return the first free file descriptor, 1 in this case.
|
|
<li>How does the shell implement sharing an output file:
|
|
<pre>
|
|
$ls 2> tmp1 > tmp1
|
|
</pre>
|
|
replace last code with:
|
|
<pre>
|
|
|
|
close (1);
|
|
close (2);
|
|
fd1 = open ("tmp1", O_CREAT|O_WRONLY); // fd will be 1!
|
|
fd2 = dup (fd1);
|
|
</pre>
|
|
both file descriptors share offset
|
|
<li>how do programs communicate?
|
|
<pre>
|
|
$ sort file.txt | uniq | wc
|
|
</pre>
|
|
or
|
|
<pre>
|
|
$ sort file.txt > tmp1
|
|
$ uniq tmp1 > tmp2
|
|
$ wc tmp2
|
|
$ rm tmp1 tmp2
|
|
</pre>
|
|
or
|
|
<pre>
|
|
$ kill -9
|
|
</pre>
|
|
<li>A pipe is an one-way communication channel. Here is an example
|
|
where the parent is the writer and the child is the reader:
|
|
<pre>
|
|
|
|
int fdarray[2];
|
|
|
|
if (pipe(fdarray) < 0) panic ("error");
|
|
if ((pid = fork()) < 0) panic ("error");
|
|
else if (pid > 0) {
|
|
close(fdarray[0]);
|
|
write(fdarray[1], "hello world\n", 12);
|
|
} else {
|
|
close(fdarray[1]);
|
|
n = read (fdarray[0], buf, MAXBUF);
|
|
write (1, buf, n);
|
|
}
|
|
</pre>
|
|
<li>How does the shell implement pipelines (i.e., cmd 1 | cmd 2 |..)?
|
|
We want to arrange that the output of cmd 1 is the input of cmd 2.
|
|
The way to achieve this goal is to manipulate stdout and stdin.
|
|
<li>The shell creates processes for each command in
|
|
the pipeline, hooks up their stdin and stdout correctly. To do it
|
|
correct, and waits for the last process of the
|
|
pipeline to exit. A sketch of the core modifications to our shell for
|
|
setting up a pipe is:
|
|
<pre>
|
|
int fdarray[2];
|
|
|
|
if (pipe(fdarray) < 0) panic ("error");
|
|
if ((pid = fork ()) == 0) { child (left end of pipe)
|
|
close (1);
|
|
tmp = dup (fdarray[1]); // fdarray[1] is the write end, tmp will be 1
|
|
close (fdarray[0]); // close read end
|
|
close (fdarray[1]); // close fdarray[1]
|
|
exec (command1, args1, 0);
|
|
} else if (pid > 0) { // parent (right end of pipe)
|
|
close (0);
|
|
tmp = dup (fdarray[0]); // fdarray[0] is the read end, tmp will be 0
|
|
close (fdarray[0]);
|
|
close (fdarray[1]); // close write end
|
|
exec (command2, args2, 0);
|
|
} else {
|
|
printf ("Unable to fork\n");
|
|
}
|
|
</pre>
|
|
<li>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.
|
|
<li>How do you background jobs?
|
|
<pre>
|
|
$ compute &
|
|
</pre>
|
|
<li>How does the shell implement "&", backgrounding? (Don't call wait
|
|
immediately).
|
|
<li>More details in the shell lecture later in the term.
|
|
|
|
</body>
|
|
|
|
|