From f53494c28e362fb7752bbc83417b9ba47cff0bf5 Mon Sep 17 00:00:00 2001 From: rsc Date: Wed, 3 Sep 2008 04:50:04 +0000 Subject: [PATCH] DO NOT MAIL: xv6 web pages --- web/Makefile | 3 + web/index.html | 353 ++++++++++ web/index.txt | 335 +++++++++ web/l-bugs.html | 187 +++++ web/l-coordination.html | 354 ++++++++++ web/l-fs.html | 222 ++++++ web/l-interrupt.html | 174 +++++ web/l-lock.html | 322 +++++++++ web/l-mkernel.html | 262 +++++++ web/l-name.html | 181 +++++ web/l-okws.txt | 249 +++++++ web/l-plan9.html | 249 +++++++ web/l-scalablecoord.html | 202 ++++++ web/l-schedule.html | 340 +++++++++ web/l-threads.html | 316 +++++++++ web/l-vm.html | 462 +++++++++++++ web/l-xfi.html | 246 +++++++ web/l1.html | 288 ++++++++ web/l13.html | 245 +++++++ web/l14.txt | 247 +++++++ web/l19.txt | 1412 ++++++++++++++++++++++++++++++++++++++ web/l2.html | 494 +++++++++++++ web/l3.html | 334 +++++++++ web/l4.html | 518 ++++++++++++++ web/l5.html | 210 ++++++ web/mkhtml | 70 ++ web/x86-intr.html | 53 ++ web/x86-intro.html | 18 + web/x86-mmu.html | 33 + web/x86-mmu1.pdf | Bin 0 -> 134308 bytes web/x86-mmu2.pdf | 55 ++ web/xv6-disk.html | 63 ++ web/xv6-intro.html | 163 +++++ web/xv6-lock.html | 100 +++ web/xv6-names.html | 78 +++ web/xv6-sched.html | 96 +++ web/xv6-sleep.html | 100 +++ 37 files changed, 9034 insertions(+) create mode 100644 web/Makefile create mode 100644 web/index.html create mode 100644 web/index.txt create mode 100644 web/l-bugs.html create mode 100644 web/l-coordination.html create mode 100644 web/l-fs.html create mode 100644 web/l-interrupt.html create mode 100644 web/l-lock.html create mode 100644 web/l-mkernel.html create mode 100644 web/l-name.html create mode 100644 web/l-okws.txt create mode 100644 web/l-plan9.html create mode 100644 web/l-scalablecoord.html create mode 100644 web/l-schedule.html create mode 100644 web/l-threads.html create mode 100644 web/l-vm.html create mode 100644 web/l-xfi.html create mode 100644 web/l1.html create mode 100644 web/l13.html create mode 100644 web/l14.txt create mode 100644 web/l19.txt create mode 100644 web/l2.html create mode 100644 web/l3.html create mode 100644 web/l4.html create mode 100644 web/l5.html create mode 100755 web/mkhtml create mode 100644 web/x86-intr.html create mode 100644 web/x86-intro.html create mode 100644 web/x86-mmu.html create mode 100644 web/x86-mmu1.pdf create mode 100644 web/x86-mmu2.pdf create mode 100644 web/xv6-disk.html create mode 100644 web/xv6-intro.html create mode 100644 web/xv6-lock.html create mode 100644 web/xv6-names.html create mode 100644 web/xv6-sched.html create mode 100644 web/xv6-sleep.html diff --git a/web/Makefile b/web/Makefile new file mode 100644 index 0000000..7b49773 --- /dev/null +++ b/web/Makefile @@ -0,0 +1,3 @@ +index.html: index.txt mkhtml + mkhtml index.txt >_$@ && mv _$@ $@ + diff --git a/web/index.html b/web/index.html new file mode 100644 index 0000000..d5f940c --- /dev/null +++ b/web/index.html @@ -0,0 +1,353 @@ + + + +Xv6, a simple Unix-like teaching operating system + + + +

Xv6, a simple Unix-like teaching operating system

+

+Xv6 is a teaching operating system developed +in the summer of 2006 for MIT's operating systems course, +“6.828: Operating Systems Engineering.” +We used it for 6.828 in Fall 2006 and Fall 2007 +and are using it this semester (Fall 2008). +We hope that xv6 will be useful in other courses too. +This page collects resources to aid the use of xv6 +in other courses. + +

History and Background

+For many years, MIT had no operating systems course. +In the fall of 2002, Frans Kaashoek, Josh Cates, and Emil Sit +created a new, experimental course (6.097) +to teach operating systems engineering. +In the course lectures, the class worked through Sixth Edition Unix (aka V6) +using John Lions's famous commentary. +In the lab assignments, students wrote most of an exokernel operating +system, eventually named Jos, for the Intel x86. +Exposing students to multiple systems–V6 and Jos–helped +develop a sense of the spectrum of operating system designs. +In the fall of 2003, the experimental 6.097 became the +official course 6.828; the course has been offered each fall since then. +

+V6 presented pedagogic challenges from the start. +Students doubted the relevance of an obsolete 30-year-old operating system +written in an obsolete programming language (pre-K&R C) +running on obsolete hardware (the PDP-11). +Students also struggled to learn the low-level details of two different +architectures (the PDP-11 and the Intel x86) at the same time. +By the summer of 2006, we had decided to replace V6 +with a new operating system, xv6, modeled on V6 +but written in ANSI C and running on multiprocessor +Intel x86 machines. +Xv6's use of the x86 makes it more relevant to +students' experience than V6 was +and unifies the course around a single architecture. +Adding multiprocessor support also helps relevance +and makes it easier to discuss threads and concurrency. +(In a single processor operating system, concurrency–which only +happens because of interrupts–is too easy to view as a special case. +A multiprocessor operating system must attack the problem head on.) +Finally, writing a new system allowed us to write cleaner versions +of the rougher parts of V6, like the scheduler and file system. +

+6.828 substituted xv6 for V6 in the fall of 2006. +Based on that experience, we cleaned up rough patches +of xv6 for the course in the fall of 2007. +Since then, xv6 has stabilized, so we are making it +available in the hopes that others will find it useful too. +

+6.828 uses both xv6 and Jos. +Courses taught at UCLA, NYU, and Stanford have used +Jos without xv6; we believe other courses could use +xv6 without Jos, though we are not aware of any that have. + +

Xv6 sources

+The latest xv6 is xv6-rev2.tar.gz. +We distribute the sources in electronic form but also as +a printed booklet with line numbers that keep everyone +together during lectures. The booklet is available as +xv6-rev2.pdf. +

+xv6 compiles using the GNU C compiler, +targeted at the x86 using ELF binaries. +On BSD and Linux systems, you can use the native compilers; +On OS X, which doesn't use ELF binaries, +you must use a cross-compiler. +Xv6 does boot on real hardware, but typically +we run it using the Bochs emulator. +Both the GCC cross compiler and Bochs +can be found on the 6.828 tools page. + +

Lectures

+In 6.828, the lectures in the first half of the course +introduce the PC hardware, the Intel x86, and then xv6. +The lectures in the second half consider advanced topics +using research papers; for some, xv6 serves as a useful +base for making discussions concrete. +This section describe a typical 6.828 lecture schedule, +linking to lecture notes and homework. +A course using only xv6 (not Jos) will need to adapt +a few of the lectures, but we hope these are a useful +starting point. + +

Lecture 1. Operating systems +

+The first lecture introduces both the general topic of +operating systems and the specific approach of 6.828. +After defining “operating system,” the lecture +examines the implementation of a Unix shell +to look at the details the traditional Unix system call interface. +This is relevant to both xv6 and Jos: in the final +Jos labs, students implement a Unix-like interface +and culminating in a Unix shell. +

+lecture notes + +

Lecture 2. PC hardware and x86 programming +

+This lecture introduces the PC architecture, the 16- and 32-bit x86, +the stack, and the GCC x86 calling conventions. +It also introduces the pieces of a typical C tool chain–compiler, +assembler, linker, loader–and the Bochs emulator. +

+Reading: PC Assembly Language +

+Homework: familiarize with Bochs +

+lecture notes +homework + +

Lecture 3. Operating system organization +

+This lecture continues Lecture 1's discussion of what +an operating system does. +An operating system provides a “virtual computer” +interface to user space programs. +At a high level, the main job of the operating system +is to implement that interface +using the physical computer it runs on. +

+The lecture discusses four approaches to that job: +monolithic operating systems, microkernels, +virtual machines, and exokernels. +Exokernels might not be worth mentioning +except that the Jos labs are built around one. +

+Reading: Engler et al., Exokernel: An Operating System Architecture +for Application-Level Resource Management +

+lecture notes + +

Lecture 4. Address spaces using segmentation +

+This is the first lecture that uses xv6. +It introduces the idea of address spaces and the +details of the x86 segmentation hardware. +It makes the discussion concrete by reading the xv6 +source code and watching xv6 execute using the Bochs simulator. +

+Reading: x86 MMU handout, +xv6: bootasm.S, bootother.S, bootmain.c, main.c, init.c, and setupsegs in proc.c. +

+Homework: Bochs stack introduction +

+lecture notes +homework + +

Lecture 5. Address spaces using page tables +

+This lecture continues the discussion of address spaces, +examining the other x86 virtual memory mechanism: page tables. +Xv6 does not use page tables, so there is no xv6 here. +Instead, the lecture uses Jos as a concrete example. +An xv6-only course might skip or shorten this discussion. +

+Reading: x86 manual excerpts +

+Homework: stuff about gdt +XXX not appropriate; should be in Lecture 4 +

+lecture notes + +

Lecture 6. Interrupts and exceptions +

+How does a user program invoke the operating system kernel? +How does the kernel return to the user program? +What happens when a hardware device needs attention? +This lecture explains the answer to these questions: +interrupt and exception handling. +

+It explains the x86 trap setup mechanisms and then +examines their use in xv6's SETGATE (mmu.h), +tvinit (trap.c), idtinit (trap.c), vectors.pl, and vectors.S. +

+It then traces through a call to the system call open: +init.c, usys.S, vector48 and alltraps (vectors.S), trap (trap.c), +syscall (syscall.c), +sys_open (sysfile.c), fetcharg, fetchint, argint, argptr, argstr (syscall.c), +

+The interrupt controller, briefly: +pic_init and pic_enable (picirq.c). +The timer and keyboard, briefly: +timer_init (timer.c), console_init (console.c). +Enabling and disabling of interrupts. +

+Reading: x86 manual excerpts, +xv6: trapasm.S, trap.c, syscall.c, and usys.S. +Skim lapic.c, ioapic.c, picirq.c. +

+Homework: Explain the 35 words on the top of the +stack at first invocation of syscall. +

+lecture notes +homework + +

Lecture 7. Multiprocessors and locking +

+This lecture introduces the problems of +coordination and synchronization on a +multiprocessor +and then the solution of mutual exclusion locks. +Atomic instructions, test-and-set locks, +lock granularity, (the mistake of) recursive locks. +

+Although xv6 user programs cannot share memory, +the xv6 kernel itself is a program with multiple threads +executing concurrently and sharing memory. +Illustration: the xv6 scheduler's proc_table_lock (proc.c) +and the spin lock implementation (spinlock.c). +

+Reading: xv6: spinlock.c. Skim mp.c. +

+Homework: Interaction between locking and interrupts. +Try not disabling interrupts in the disk driver and watch xv6 break. +

+lecture notes +homework + +

Lecture 8. Threads, processes and context switching +

+The last lecture introduced some of the issues +in writing threaded programs, using xv6's processes +as an example. +This lecture introduces the issues in implementing +threads, continuing to use xv6 as the example. +

+The lecture defines a thread of computation as a register +set and a stack. A process is an address space plus one +or more threads of computation sharing that address space. +Thus the xv6 kernel can be viewed as a single process +with many threads (each user process) executing concurrently. +

+Illustrations: thread switching (swtch.S), scheduler (proc.c), sys_fork (sysproc.c) +

+Reading: proc.c, swtch.S, sys_fork (sysproc.c) +

+Homework: trace through stack switching. +

+lecture notes (need to be updated to use swtch) +homework + +

Lecture 9. Processes and coordination +

+This lecture introduces the idea of sequence coordination +and then examines the particular solution illustrated by +sleep and wakeup (proc.c). +It introduces and refines a simple +producer/consumer queue to illustrate the +need for sleep and wakeup +and then the sleep and wakeup +implementations themselves. +

+Reading: proc.c, sys_exec, sys_sbrk, sys_wait, sys_exec, sys_kill (sysproc.c). +

+Homework: Explain how sleep and wakeup would break +without proc_table_lock. Explain how devices would break +without second lock argument to sleep. +

+lecture notes +homework + +

Lecture 10. Files and disk I/O +

+This is the first of three file system lectures. +This lecture introduces the basic file system interface +and then considers the on-disk layout of individual files +and the free block bitmap. +

+Reading: iread, iwrite, fileread, filewrite, wdir, mknod1, and + code related to these calls in fs.c, bio.c, ide.c, and file.c. +

+Homework: Add print to bwrite to trace every disk write. +Explain the disk writes caused by some simple shell commands. +

+lecture notes +homework + +

Lecture 11. Naming +

+The last lecture discussed on-disk file system representation. +This lecture covers the implementation of +file system paths (namei in fs.c) +and also discusses the security problems of a shared /tmp +and symbolic links. +

+Understanding exec (exec.c) is left as an exercise. +

+Reading: namei in fs.c, sysfile.c, file.c. +

+Homework: Explain how to implement symbolic links in xv6. +

+lecture notes +homework + +

Lecture 12. High-performance file systems +

+This lecture is the first of the research paper-based lectures. +It discusses the “soft updates” paper, +using xv6 as a concrete example. + +

Feedback

+If you are interested in using xv6 or have used xv6 in a course, +we would love to hear from you. +If there's anything that we can do to make xv6 easier +to adopt, we'd like to hear about it. +We'd also be interested to hear what worked well and what didn't. +

+Russ Cox (rsc@swtch.com)
+Frans Kaashoek (kaashoek@mit.edu)
+Robert Morris (rtm@mit.edu) +

+You can reach all of us at 6.828-staff@pdos.csail.mit.edu. +

+

+ + diff --git a/web/index.txt b/web/index.txt new file mode 100644 index 0000000..41d42a4 --- /dev/null +++ b/web/index.txt @@ -0,0 +1,335 @@ +** Xv6, a simple Unix-like teaching operating system +Xv6 is a teaching operating system developed +in the summer of 2006 for MIT's operating systems course, +``6.828: Operating Systems Engineering.'' +We used it for 6.828 in Fall 2006 and Fall 2007 +and are using it this semester (Fall 2008). +We hope that xv6 will be useful in other courses too. +This page collects resources to aid the use of xv6 +in other courses. + +* History and Background + +For many years, MIT had no operating systems course. +In the fall of 2002, Frans Kaashoek, Josh Cates, and Emil Sit +created a new, experimental course (6.097) +to teach operating systems engineering. +In the course lectures, the class worked through Sixth Edition Unix (aka V6) +using John Lions's famous commentary. +In the lab assignments, students wrote most of an exokernel operating +system, eventually named Jos, for the Intel x86. +Exposing students to multiple systems--V6 and Jos--helped +develop a sense of the spectrum of operating system designs. +In the fall of 2003, the experimental 6.097 became the +official course 6.828; the course has been offered each fall since then. + +V6 presented pedagogic challenges from the start. +Students doubted the relevance of an obsolete 30-year-old operating system +written in an obsolete programming language (pre-K&R C) +running on obsolete hardware (the PDP-11). +Students also struggled to learn the low-level details of two different +architectures (the PDP-11 and the Intel x86) at the same time. +By the summer of 2006, we had decided to replace V6 +with a new operating system, xv6, modeled on V6 +but written in ANSI C and running on multiprocessor +Intel x86 machines. +Xv6's use of the x86 makes it more relevant to +students' experience than V6 was +and unifies the course around a single architecture. +Adding multiprocessor support also helps relevance +and makes it easier to discuss threads and concurrency. +(In a single processor operating system, concurrency--which only +happens because of interrupts--is too easy to view as a special case. +A multiprocessor operating system must attack the problem head on.) +Finally, writing a new system allowed us to write cleaner versions +of the rougher parts of V6, like the scheduler and file system. + +6.828 substituted xv6 for V6 in the fall of 2006. +Based on that experience, we cleaned up rough patches +of xv6 for the course in the fall of 2007. +Since then, xv6 has stabilized, so we are making it +available in the hopes that others will find it useful too. + +6.828 uses both xv6 and Jos. +Courses taught at UCLA, NYU, and Stanford have used +Jos without xv6; we believe other courses could use +xv6 without Jos, though we are not aware of any that have. + + +* Xv6 sources + +The latest xv6 is [xv6-rev2.tar.gz]. +We distribute the sources in electronic form but also as +a printed booklet with line numbers that keep everyone +together during lectures. The booklet is available as +[xv6-rev2.pdf]. + +xv6 compiles using the GNU C compiler, +targeted at the x86 using ELF binaries. +On BSD and Linux systems, you can use the native compilers; +On OS X, which doesn't use ELF binaries, +you must use a cross-compiler. +Xv6 does boot on real hardware, but typically +we run it using the Bochs emulator. +Both the GCC cross compiler and Bochs +can be found on the [../../2007/tools.html | 6.828 tools page]. + + +* Lectures + +In 6.828, the lectures in the first half of the course +introduce the PC hardware, the Intel x86, and then xv6. +The lectures in the second half consider advanced topics +using research papers; for some, xv6 serves as a useful +base for making discussions concrete. +This section describe a typical 6.828 lecture schedule, +linking to lecture notes and homework. +A course using only xv6 (not Jos) will need to adapt +a few of the lectures, but we hope these are a useful +starting point. + + +Lecture 1. Operating systems + +The first lecture introduces both the general topic of +operating systems and the specific approach of 6.828. +After defining ``operating system,'' the lecture +examines the implementation of a Unix shell +to look at the details the traditional Unix system call interface. +This is relevant to both xv6 and Jos: in the final +Jos labs, students implement a Unix-like interface +and culminating in a Unix shell. + +[l1.html | lecture notes] + + +Lecture 2. PC hardware and x86 programming + +This lecture introduces the PC architecture, the 16- and 32-bit x86, +the stack, and the GCC x86 calling conventions. +It also introduces the pieces of a typical C tool chain--compiler, +assembler, linker, loader--and the Bochs emulator. + +Reading: PC Assembly Language + +Homework: familiarize with Bochs + +[l2.html | lecture notes] +[x86-intro.html | homework] + + +Lecture 3. Operating system organization + +This lecture continues Lecture 1's discussion of what +an operating system does. +An operating system provides a ``virtual computer'' +interface to user space programs. +At a high level, the main job of the operating system +is to implement that interface +using the physical computer it runs on. + +The lecture discusses four approaches to that job: +monolithic operating systems, microkernels, +virtual machines, and exokernels. +Exokernels might not be worth mentioning +except that the Jos labs are built around one. + +Reading: Engler et al., Exokernel: An Operating System Architecture +for Application-Level Resource Management + +[l3.html | lecture notes] + + +Lecture 4. Address spaces using segmentation + +This is the first lecture that uses xv6. +It introduces the idea of address spaces and the +details of the x86 segmentation hardware. +It makes the discussion concrete by reading the xv6 +source code and watching xv6 execute using the Bochs simulator. + +Reading: x86 MMU handout, +xv6: bootasm.S, bootother.S, bootmain.c, main.c, init.c, and setupsegs in proc.c. + +Homework: Bochs stack introduction + +[l4.html | lecture notes] +[xv6-intro.html | homework] + + +Lecture 5. Address spaces using page tables + +This lecture continues the discussion of address spaces, +examining the other x86 virtual memory mechanism: page tables. +Xv6 does not use page tables, so there is no xv6 here. +Instead, the lecture uses Jos as a concrete example. +An xv6-only course might skip or shorten this discussion. + +Reading: x86 manual excerpts + +Homework: stuff about gdt +XXX not appropriate; should be in Lecture 4 + +[l5.html | lecture notes] + + +Lecture 6. Interrupts and exceptions + +How does a user program invoke the operating system kernel? +How does the kernel return to the user program? +What happens when a hardware device needs attention? +This lecture explains the answer to these questions: +interrupt and exception handling. + +It explains the x86 trap setup mechanisms and then +examines their use in xv6's SETGATE (mmu.h), +tvinit (trap.c), idtinit (trap.c), vectors.pl, and vectors.S. + +It then traces through a call to the system call open: +init.c, usys.S, vector48 and alltraps (vectors.S), trap (trap.c), +syscall (syscall.c), +sys_open (sysfile.c), fetcharg, fetchint, argint, argptr, argstr (syscall.c), + +The interrupt controller, briefly: +pic_init and pic_enable (picirq.c). +The timer and keyboard, briefly: +timer_init (timer.c), console_init (console.c). +Enabling and disabling of interrupts. + +Reading: x86 manual excerpts, +xv6: trapasm.S, trap.c, syscall.c, and usys.S. +Skim lapic.c, ioapic.c, picirq.c. + +Homework: Explain the 35 words on the top of the +stack at first invocation of syscall. + +[l-interrupt.html | lecture notes] +[x86-intr.html | homework] + + +Lecture 7. Multiprocessors and locking + +This lecture introduces the problems of +coordination and synchronization on a +multiprocessor +and then the solution of mutual exclusion locks. +Atomic instructions, test-and-set locks, +lock granularity, (the mistake of) recursive locks. + +Although xv6 user programs cannot share memory, +the xv6 kernel itself is a program with multiple threads +executing concurrently and sharing memory. +Illustration: the xv6 scheduler's proc_table_lock (proc.c) +and the spin lock implementation (spinlock.c). + +Reading: xv6: spinlock.c. Skim mp.c. + +Homework: Interaction between locking and interrupts. +Try not disabling interrupts in the disk driver and watch xv6 break. + +[l-lock.html | lecture notes] +[xv6-lock.html | homework] + + +Lecture 8. Threads, processes and context switching + +The last lecture introduced some of the issues +in writing threaded programs, using xv6's processes +as an example. +This lecture introduces the issues in implementing +threads, continuing to use xv6 as the example. + +The lecture defines a thread of computation as a register +set and a stack. A process is an address space plus one +or more threads of computation sharing that address space. +Thus the xv6 kernel can be viewed as a single process +with many threads (each user process) executing concurrently. + +Illustrations: thread switching (swtch.S), scheduler (proc.c), sys_fork (sysproc.c) + +Reading: proc.c, swtch.S, sys_fork (sysproc.c) + +Homework: trace through stack switching. + +[l-threads.html | lecture notes (need to be updated to use swtch)] +[xv6-sched.html | homework] + + +Lecture 9. Processes and coordination + +This lecture introduces the idea of sequence coordination +and then examines the particular solution illustrated by +sleep and wakeup (proc.c). +It introduces and refines a simple +producer/consumer queue to illustrate the +need for sleep and wakeup +and then the sleep and wakeup +implementations themselves. + +Reading: proc.c, sys_exec, sys_sbrk, sys_wait, sys_exec, sys_kill (sysproc.c). + +Homework: Explain how sleep and wakeup would break +without proc_table_lock. Explain how devices would break +without second lock argument to sleep. + +[l-coordination.html | lecture notes] +[xv6-sleep.html | homework] + + +Lecture 10. Files and disk I/O + +This is the first of three file system lectures. +This lecture introduces the basic file system interface +and then considers the on-disk layout of individual files +and the free block bitmap. + +Reading: iread, iwrite, fileread, filewrite, wdir, mknod1, and + code related to these calls in fs.c, bio.c, ide.c, and file.c. + +Homework: Add print to bwrite to trace every disk write. +Explain the disk writes caused by some simple shell commands. + +[l-fs.html | lecture notes] +[xv6-disk.html | homework] + + +Lecture 11. Naming + +The last lecture discussed on-disk file system representation. +This lecture covers the implementation of +file system paths (namei in fs.c) +and also discusses the security problems of a shared /tmp +and symbolic links. + +Understanding exec (exec.c) is left as an exercise. + +Reading: namei in fs.c, sysfile.c, file.c. + +Homework: Explain how to implement symbolic links in xv6. + +[l-name.html | lecture notes] +[xv6-names.html | homework] + + +Lecture 12. High-performance file systems + +This lecture is the first of the research paper-based lectures. +It discusses the ``soft updates'' paper, +using xv6 as a concrete example. + + +* Feedback + +If you are interested in using xv6 or have used xv6 in a course, +we would love to hear from you. +If there's anything that we can do to make xv6 easier +to adopt, we'd like to hear about it. +We'd also be interested to hear what worked well and what didn't. + +Russ Cox (rsc@swtch.com)
+Frans Kaashoek (kaashoek@mit.edu)
+Robert Morris (rtm@mit.edu) + +You can reach all of us at 6.828-staff@pdos.csail.mit.edu. + + diff --git a/web/l-bugs.html b/web/l-bugs.html new file mode 100644 index 0000000..493372d --- /dev/null +++ b/web/l-bugs.html @@ -0,0 +1,187 @@ +OS Bugs + + + + + +

OS Bugs

+ +

Required reading: Bugs as deviant behavior + +

Overview

+ +

Operating systems must obey many rules for correctness and +performance. Examples rules: +

+ +

In addition, there are standard software engineering rules, like +use function results in consistent ways. + +

These rules are typically not checked by a compiler, even though +they could be checked by a compiler, in principle. The goal of the +meta-level compilation project is to allow system implementors to +write system-specific compiler extensions that check the source code +for rule violations. + +

The results are good: many new bugs found (500-1000) in Linux +alone. The paper for today studies these bugs and attempts to draw +lessons from these bugs. + +

Are kernel error worse than user-level errors? That is, if we get +the kernel correct, then we won't have system crashes? + +

Errors in JOS kernel

+ +

What are unstated invariants in the JOS? +

+ +

Could these errors have been caught by metacompilation? Would +metacompilation have caught the pipe race condition? (Probably not, +it happens in only one place.) + +

How confident are you that your code is correct? For example, +are you sure interrupts are always disabled in kernel mode? How would +you test? + +

Metacompilation

+ +

A system programmer writes the rule checkers in a high-level, +state-machine language (metal). These checkers are dynamically linked +into an extensible version of g++, xg++. Xg++ applies the rule +checkers to every possible execution path of a function that is being +compiled. + +

An example rule from +the OSDI +paper: +

+sm check_interrupts {
+   decl { unsigned} flags;
+   pat enable = { sti(); } | {restore_flags(flags);} ;
+   pat disable = { cli(); };
+   
+   is_enabled: disable ==> is_disabled | enable ==> { err("double
+      enable")};
+   ...
+
+A more complete version found 82 errors in the Linux 2.3.99 kernel. + +

Common mistake: +

+get_free_buffer ( ... ) {
+   ....
+   save_flags (flags);
+   cli ();
+   if ((bh = sh->buffer_pool) == NULL)
+      return NULL;
+   ....
+}
+
+

(Figure 2 also lists a simple metarule.) + +

Some checkers produce false positives, because of limitations of +both static analysis and the checkers, which mostly use local +analysis. + +

How does the block checker work? The first pass is a rule +that marks functions as potentially blocking. After processing a +function, the checker emits the function's flow graph to a file +(including, annotations and functions called). The second pass takes +the merged flow graph of all function calls, and produces a file with +all functions that have a path in the control-flow-graph to a blocking +function call. For the Linux kernel this results in 3,000 functions +that potentially could call sleep. Yet another checker like +check_interrupts checks if a function calls any of the 3,000 functions +with interrupts disabled. Etc. + +

This paper

+ +

Writing rules is painful. First, you have to write them. Second, +how do you decide what to check? Was it easy to enumerate all +conventions for JOS? + +

Insight: infer programmer "beliefs" from code and cross-check +for contradictions. If cli is always followed by sti, +except in one case, perhaps something is wrong. This simplifies +life because we can write generic checkers instead of checkers +that specifically check for sti, and perhaps we get lucky +and find other temporal ordering conventions. + +

Do we know which case is wrong? The 999 times or the 1 time that +sti is absent? (No, this method cannot figure what the correct +sequence is but it can flag that something is weird, which in practice +useful.) The method just detects inconsistencies. + +

Is every inconsistency an error? No, some inconsistency don't +indicate an error. If a call to function f is often followed +by call to function g, does that imply that f should always be +followed by g? (No!) + +

Solution: MUST beliefs and MAYBE beliefs. MUST beliefs are +invariants that must hold; any inconsistency indicates an error. If a +pointer is dereferences, then the programmer MUST believe that the +pointer is pointing to something that can be dereferenced (i.e., the +pointer is definitely not zero). MUST beliefs can be checked using +"internal inconsistencies". + +

An aside, can zero pointers pointers be detected during runtime? +(Sure, unmap the page at address zero.) Why is metacompilation still +valuable? (At runtime you will find only the null pointers that your +test code dereferenced; not all possible dereferences of null +pointers.) An even more convincing example for Metacompilation is +tracking user pointers that the kernel dereferences. (Is this a MUST +belief?) + +

MAYBE beliefs are invariants that are suggested by the code, but +they maybe coincidences. MAYBE beliefs are ranked by statistical +analysis, and perhaps augmented with input about functions names +(e.g., alloc and free are important). Is it computationally feasible +to check every MAYBE belief? Could there be much noise? + +

What errors won't this approach catch? + +

Paper discussion

+ +

This paper is best discussed by studying every code fragment. Most +code fragments are pieces of code from Linux distributions; these +mistakes are real! + +

Section 3.1. what is the error? how does metacompilation catch +it? + +

Figure 1. what is the error? is there one? + +

Code fragments from 6.1. what is the error? how does metacompilation catch +it? + +

Figure 3. what is the error? how does metacompilation catch +it? + +

Section 8.3. what is the error? how does metacompilation catch +it? + + + diff --git a/web/l-coordination.html b/web/l-coordination.html new file mode 100644 index 0000000..b2f9f0d --- /dev/null +++ b/web/l-coordination.html @@ -0,0 +1,354 @@ +L9 + + + + + +

Coordination and more processes

+ +

Required reading: remainder of proc.c, sys_exec, sys_sbrk, + sys_wait, sys_exit, and sys_kill. + +

Overview

+ +

Big picture: more programs than processors. How to share the + limited number of processors among the programs? Last lecture + covered basic mechanism: threads and the distinction between process + and thread. Today expand: how to coordinate the interactions + between threads explicitly, and some operations on processes. + +

Sequence coordination. This is a diferrent type of coordination + than mutual-exclusion coordination (which has its goal to make + atomic actions so that threads don't interfere). The goal of + sequence coordination is for threads to coordinate the sequences in + which they run. + +

For example, a thread may want to wait until another thread + terminates. One way to do so is to have the thread run periodically, + let it check if the other thread terminated, and if not give up the + processor again. This is wasteful, especially if there are many + threads. + +

With primitives for sequence coordination one can do better. The + thread could tell the thread manager that it is waiting for an event + (e.g., another thread terminating). When the other thread + terminates, it explicitly wakes up the waiting thread. This is more + work for the programmer, but more efficient. + +

Sequence coordination often interacts with mutual-exclusion + coordination, as we will see below. + +

The operating system literature has a rich set of primivites for + sequence coordination. We study a very simple version of condition + variables in xv6: sleep and wakeup, with a single lock. + +

xv6 code examples

+ +

Sleep and wakeup - usage

+ +Let's consider implementing a producer/consumer queue +(like a pipe) that can be used to hold a single non-null char pointer: + +
+struct pcq {
+    void *ptr;
+};
+
+void*
+pcqread(struct pcq *q)
+{
+    void *p;
+
+    while((p = q->ptr) == 0)
+        ;
+    q->ptr = 0;
+    return p;
+}
+
+void
+pcqwrite(struct pcq *q, void *p)
+{
+    while(q->ptr != 0)
+        ;
+    q->ptr = p;
+}
+
+ +

Easy and correct, at least assuming there is at most one +reader and at most one writer at a time. + +

Unfortunately, the while loops are inefficient. +Instead of polling, it would be great if there were +primitives saying ``wait for some event to happen'' +and ``this event happened''. +That's what sleep and wakeup do. + +

Second try: + +

+void*
+pcqread(struct pcq *q)
+{
+    void *p;
+
+    if(q->ptr == 0)
+        sleep(q);
+    p = q->ptr;
+    q->ptr = 0;
+    wakeup(q);  /* wake pcqwrite */
+    return p;
+}
+
+void
+pcqwrite(struct pcq *q, void *p)
+{
+    if(q->ptr != 0)
+        sleep(q);
+    q->ptr = p;
+    wakeup(q);  /* wake pcqread */
+    return p;
+}
+
+ +That's better, but there is still a problem. +What if the wakeup happens between the check in the if +and the call to sleep? + +

Add locks: + +

+struct pcq {
+    void *ptr;
+    struct spinlock lock;
+};
+
+void*
+pcqread(struct pcq *q)
+{
+    void *p;
+
+    acquire(&q->lock);
+    if(q->ptr == 0)
+        sleep(q, &q->lock);
+    p = q->ptr;
+    q->ptr = 0;
+    wakeup(q);  /* wake pcqwrite */
+    release(&q->lock);
+    return p;
+}
+
+void
+pcqwrite(struct pcq *q, void *p)
+{
+    acquire(&q->lock);
+    if(q->ptr != 0)
+        sleep(q, &q->lock);
+    q->ptr = p;
+    wakeup(q);  /* wake pcqread */
+    release(&q->lock);
+    return p;
+}
+
+ +This is okay, and now safer for multiple readers and writers, +except that wakeup wakes up everyone who is asleep on chan, +not just one guy. +So some of the guys who wake up from sleep might not +be cleared to read or write from the queue. Have to go back to looping: + +
+struct pcq {
+    void *ptr;
+    struct spinlock lock;
+};
+
+void*
+pcqread(struct pcq *q)
+{
+    void *p;
+
+    acquire(&q->lock);
+    while(q->ptr == 0)
+        sleep(q, &q->lock);
+    p = q->ptr;
+    q->ptr = 0;
+    wakeup(q);  /* wake pcqwrite */
+    release(&q->lock);
+    return p;
+}
+
+void
+pcqwrite(struct pcq *q, void *p)
+{
+    acquire(&q->lock);
+    while(q->ptr != 0)
+        sleep(q, &q->lock);
+    q->ptr = p;
+    wakeup(q);  /* wake pcqread */
+    release(&q->lock);
+    return p;
+}
+
+ +The difference between this an our original is that +the body of the while loop is a much more efficient way to pause. + +

Now we've figured out how to use it, but we +still need to figure out how to implement it. + +

Sleep and wakeup - implementation

+

+Simple implementation: + +

+void
+sleep(void *chan, struct spinlock *lk)
+{
+    struct proc *p = curproc[cpu()];
+    
+    release(lk);
+    p->chan = chan;
+    p->state = SLEEPING;
+    sched();
+}
+
+void
+wakeup(void *chan)
+{
+    for(each proc p) {
+        if(p->state == SLEEPING && p->chan == chan)
+            p->state = RUNNABLE;
+    }	
+}
+
+ +

What's wrong? What if the wakeup runs right after +the release(lk) in sleep? +It still misses the sleep. + +

Move the lock down: +

+void
+sleep(void *chan, struct spinlock *lk)
+{
+    struct proc *p = curproc[cpu()];
+    
+    p->chan = chan;
+    p->state = SLEEPING;
+    release(lk);
+    sched();
+}
+
+void
+wakeup(void *chan)
+{
+    for(each proc p) {
+        if(p->state == SLEEPING && p->chan == chan)
+            p->state = RUNNABLE;
+    }	
+}
+
+ +

This almost works. Recall from last lecture that we also need +to acquire the proc_table_lock before calling sched, to +protect p->jmpbuf. + +

+void
+sleep(void *chan, struct spinlock *lk)
+{
+    struct proc *p = curproc[cpu()];
+    
+    p->chan = chan;
+    p->state = SLEEPING;
+    acquire(&proc_table_lock);
+    release(lk);
+    sched();
+}
+
+ +

The problem is that now we're using lk to protect +access to the p->chan and p->state variables +but other routines besides sleep and wakeup +(in particular, proc_kill) will need to use them and won't +know which lock protects them. +So instead of protecting them with lk, let's use proc_table_lock: + +

+void
+sleep(void *chan, struct spinlock *lk)
+{
+    struct proc *p = curproc[cpu()];
+    
+    acquire(&proc_table_lock);
+    release(lk);
+    p->chan = chan;
+    p->state = SLEEPING;
+    sched();
+}
+void
+wakeup(void *chan)
+{
+    acquire(&proc_table_lock);
+    for(each proc p) {
+        if(p->state == SLEEPING && p->chan == chan)
+            p->state = RUNNABLE;
+    }
+    release(&proc_table_lock);
+}
+
+ +

One could probably make things work with lk as above, +but the relationship between data and locks would be +more complicated with no real benefit. Xv6 takes the easy way out +and says that elements in the proc structure are always protected +by proc_table_lock. + +

Use example: exit and wait

+ +

If proc_wait decides there are children to be waited for, +it calls sleep at line 2462. +When a process exits, we proc_exit scans the process table +to find the parent and wakes it at 2408. + +

Which lock protects sleep and wakeup from missing each other? +Proc_table_lock. Have to tweak sleep again to avoid double-acquire: + +

+if(lk != &proc_table_lock) {
+    acquire(&proc_table_lock);
+    release(lk);
+}
+
+ +

New feature: kill

+ +

Proc_kill marks a process as killed (line 2371). +When the process finally exits the kernel to user space, +or if a clock interrupt happens while it is in user space, +it will be destroyed (line 2886, 2890, 2912). + +

Why wait until the process ends up in user space? + +

What if the process is stuck in sleep? It might take a long +time to get back to user space. +Don't want to have to wait for it, so make sleep wake up early +(line 2373). + +

This means all callers of sleep should check +whether they have been killed, but none do. +Bug in xv6. + +

System call handlers

+ +

Sheet 32 + +

Fork: discussed copyproc in earlier lectures. +Sys_fork (line 3218) just calls copyproc +and marks the new proc runnable. +Does fork create a new process or a new thread? +Is there any shared context? + +

Exec: we'll talk about exec later, when we talk about file systems. + +

Sbrk: Saw growproc earlier. Why setupsegs before returning? diff --git a/web/l-fs.html b/web/l-fs.html new file mode 100644 index 0000000..ed911fc --- /dev/null +++ b/web/l-fs.html @@ -0,0 +1,222 @@ +L10 + + + + + +

File systems

+ +

Required reading: iread, iwrite, and wdir, and code related to + these calls in fs.c, bio.c, ide.c, file.c, and sysfile.c + +

Overview

+ +

The next 3 lectures are about file systems: +

+ +

Users desire to store their data durable so that data survives when +the user turns of his computer. The primary media for doing so are: +magnetic disks, flash memory, and tapes. We focus on magnetic disks +(e.g., through the IDE interface in xv6). + +

To allow users to remember where they stored a file, they can +assign a symbolic name to a file, which appears in a directory. + +

The data in a file can be organized in a structured way or not. +The structured variant is often called a database. UNIX uses the +unstructured variant: files are streams of bytes. Any particular +structure is likely to be useful to only a small class of +applications, and other applications will have to work hard to fit +their data into one of the pre-defined structures. Besides, if you +want structure, you can easily write a user-mode library program that +imposes that format on any file. The end-to-end argument in action. +(Databases have special requirements and support an important class of +applications, and thus have a specialized plan.) + +

The API for a minimal file system consists of: open, read, write, +seek, close, and stat. Dup duplicates a file descriptor. For example: +

+  fd = open("x", O_RDWR);
+  read (fd, buf, 100);
+  write (fd, buf, 512);
+  close (fd)
+
+ +

Maintaining the file offset behind the read/write interface is an + interesting design decision . The alternative is that the state of a + read operation should be maintained by the process doing the reading + (i.e., that the pointer should be passed as an argument to read). + This argument is compelling in view of the UNIX fork() semantics, + which clones a process which shares the file descriptors of its + parent. A read by the parent of a shared file descriptor (e.g., + stdin, changes the read pointer seen by the child). On the other + hand the alternative would make it difficult to get "(data; ls) > x" + right. + +

Unix API doesn't specify that the effects of write are immediately + on the disk before a write returns. It is up to the implementation + of the file system within certain bounds. Choices include (that + aren't non-exclusive): +

+ +

A design issue is the semantics of a file system operation that + requires multiple disk writes. In particular, what happens if the + logical update requires writing multiple disks blocks and the power + fails during the update? For example, to create a new file, + requires allocating an inode (which requires updating the list of + free inodes on disk), writing a directory entry to record the + allocated i-node under the name of the new file (which may require + allocating a new block and updating the directory inode). If the + power fails during the operation, the list of free inodes and blocks + may be inconsistent with the blocks and inodes in use. Again this is + up to implementation of the file system to keep on disk data + structures consistent: +

+ +

Another design issue is the semantics are of concurrent writes to +the same data item. What is the order of two updates that happen at +the same time? For example, two processes open the same file and write +to it. Modern Unix operating systems allow the application to lock a +file to get exclusive access. If file locking is not used and if the +file descriptor is shared, then the bytes of the two writes will get +into the file in some order (this happens often for log files). If +the file descriptor is not shared, the end result is not defined. For +example, one write may overwrite the other one (e.g., if they are +writing to the same part of the file.) + +

An implementation issue is performance, because writing to magnetic +disk is relatively expensive compared to computing. Three primary ways +to improve performance are: careful file system layout that induces +few seeks, an in-memory cache of frequently-accessed blocks, and +overlap I/O with computation so that file operations don't have to +wait until their completion and so that that the disk driver has more +data to write, which allows disk scheduling. (We will talk about +performance in detail later.) + +

xv6 code examples

+ +

xv6 implements a minimal Unix file system interface. xv6 doesn't +pay attention to file system layout. It overlaps computation and I/O, +but doesn't do any disk scheduling. Its cache is write-through, which +simplifies keep on disk datastructures consistent, but is bad for +performance. + +

On disk files are represented by an inode (struct dinode in fs.h), +and blocks. Small files have up to 12 block addresses in their inode; +large files use files the last address in the inode as a disk address +for a block with 128 disk addresses (512/4). The size of a file is +thus limited to 12 * 512 + 128*512 bytes. What would you change to +support larger files? (Ans: e.g., double indirect blocks.) + +

Directories are files with a bit of structure to them. The file +contains of records of the type struct dirent. The entry contains the +name for a file (or directory) and its corresponding inode number. +How many files can appear in a directory? + +

In memory files are represented by struct inode in fsvar.h. What is +the role of the additional fields in struct inode? + +

What is xv6's disk layout? How does xv6 keep track of free blocks + and inodes? See balloc()/bfree() and ialloc()/ifree(). Is this + layout a good one for performance? What are other options? + +

Let's assume that an application created an empty file x with + contains 512 bytes, and that the application now calls read(fd, buf, + 100), that is, it is requesting to read 100 bytes into buf. + Furthermore, let's assume that the inode for x is is i. Let's pick + up what happens by investigating readi(), line 4483. +

+ +

Now let's suppose that the process is writing 512 bytes at the end + of the file a. How many disk writes will happen? +

+ +

Lots of code to implement reading and writing of files. How about + directories? +

+

Reading and writing of directories is trivial. + + diff --git a/web/l-interrupt.html b/web/l-interrupt.html new file mode 100644 index 0000000..363af5e --- /dev/null +++ b/web/l-interrupt.html @@ -0,0 +1,174 @@ + +Lecture 6: Interrupts & Exceptions + + +

Interrupts & Exceptions

+ +

+Required reading: xv6 trapasm.S, trap.c, syscall.c, usys.S. +
+You will need to consult +IA32 System +Programming Guide chapter 5 (skip 5.7.1, 5.8.2, 5.12.2). + +

Overview

+ +

+Big picture: kernel is trusted third-party that runs the machine. +Only the kernel can execute privileged instructions (e.g., +changing MMU state). +The processor enforces this protection through the ring bits +in the code segment. +If a user application needs to carry out a privileged operation +or other kernel-only service, +it must ask the kernel nicely. +How can a user program change to the kernel address space? +How can the kernel transfer to a user address space? +What happens when a device attached to the computer +needs attention? +These are the topics for today's lecture. + +

+There are three kinds of events that must be handled +by the kernel, not user programs: +(1) a system call invoked by a user program, +(2) an illegal instruction or other kind of bad processor state (memory fault, etc.). +and +(3) an interrupt from a hardware device. + +

+Although these three events are different, they all use the same +mechanism to transfer control to the kernel. +This mechanism consists of three steps that execute as one atomic unit. +(a) change the processor to kernel mode; +(b) save the old processor somewhere (usually the kernel stack); +and (c) change the processor state to the values set up as +the “official kernel entry values.” +The exact implementation of this mechanism differs +from processor to processor, but the idea is the same. + +

+We'll work through examples of these today in lecture. +You'll see all three in great detail in the labs as well. + +

+A note on terminology: sometimes we'll +use interrupt (or trap) to mean both interrupts and exceptions. + +

+Setting up traps on the x86 +

+ +

+See handout Table 5-1, Figure 5-1, Figure 5-2. + +

+xv6 Sheet 07: struct gatedesc and SETGATE. + +

+xv6 Sheet 28: tvinit and idtinit. +Note setting of gate for T_SYSCALL + +

+xv6 Sheet 29: vectors.pl (also see generated vectors.S). + +

+System calls +

+ +

+xv6 Sheet 16: init.c calls open("console"). +How is that implemented? + +

+xv6 usys.S (not in book). +(No saving of registers. Why?) + +

+Breakpoint 0x1b:"open", +step past int instruction into kernel. + +

+See handout Figure 9-4 [sic]. + +

+xv6 Sheet 28: in vectors.S briefly, then in alltraps. +Step through to call trap, examine registers and stack. +How will the kernel find the argument to open? + +

+xv6 Sheet 29: trap, on to syscall. + +

+xv6 Sheet 31: syscall looks at eax, +calls sys_open. + +

+(Briefly) +xv6 Sheet 52: sys_open uses argstr and argint +to get its arguments. How do they work? + +

+xv6 Sheet 30: fetchint, fetcharg, argint, +argptr, argstr. + +

+What happens if a user program divides by zero +or accesses unmapped memory? +Exception. Same path as system call until trap. + +

+What happens if kernel divides by zero or accesses unmapped memory? + +

+Interrupts +

+ +

+Like system calls, except: +devices generate them at any time, +there are no arguments in CPU registers, +nothing to return to, +usually can't ignore them. + +

+How do they get generated? +Device essentially phones up the +interrupt controller and asks to talk to the CPU. +Interrupt controller then buzzes the CPU and +tells it, “keyboard on line 1.” +Interrupt controller is essentially the CPU's +secretary administrative assistant, +managing the phone lines on the CPU's behalf. + +

+Have to set up interrupt controller. + +

+(Briefly) xv6 Sheet 63: pic_init sets up the interrupt controller, +irq_enable tells the interrupt controller to let the given +interrupt through. + +

+(Briefly) xv6 Sheet 68: pit8253_init sets up the clock chip, +telling it to interrupt on IRQ_TIMER 100 times/second. +console_init sets up the keyboard, enabling IRQ_KBD. + +

+In Bochs, set breakpoint at 0x8:"vector0" +and continue, loading kernel. +Step through clock interrupt, look at +stack, registers. + +

+Was the processor executing in kernel or user mode +at the time of the clock interrupt? +Why? (Have any user-space instructions executed at all?) + +

+Can the kernel get an interrupt at any time? +Why or why not? cli and sti, +irq_enable. + + + diff --git a/web/l-lock.html b/web/l-lock.html new file mode 100644 index 0000000..eea8217 --- /dev/null +++ b/web/l-lock.html @@ -0,0 +1,322 @@ +L7 + + + + + +

Locking

+ +

Required reading: spinlock.c + +

Why coordinate?

+ +

Mutual-exclusion coordination is an important topic in operating +systems, because many operating systems run on +multiprocessors. Coordination techniques protect variables that are +shared among multiple threads and updated concurrently. These +techniques allow programmers to implement atomic sections so that one +thread can safely update the shared variables without having to worry +that another thread intervening. For example, processes in xv6 may +run concurrently on different processors and in kernel-mode share +kernel data structures. We must ensure that these updates happen +correctly. + +

List and insert example: +

+
+struct List {
+  int data;
+  struct List *next;
+};
+
+List *list = 0;
+
+insert(int data) {
+  List *l = new List;
+  l->data = data;
+  l->next = list;  // A
+  list = l;        // B
+}
+
+ +

What needs to be atomic? The two statements labeled A and B should +always be executed together, as an indivisible fragment of code. If +two processors execute A and B interleaved, then we end up with an +incorrect list. To see that this is the case, draw out the list after +the sequence A1 (statement executed A by processor 1), A2 (statement A +executed by processor 2), B2, and B1. + +

How could this erroneous sequence happen? The varilable list +lives in physical memory shared among multiple processors, connected +by a bus. The accesses to the shared memory will be ordered in some +total order by the bus/memory system. If the programmer doesn't +coordinate the execution of the statements A and B, any order can +happen, including the erroneous one. + +

The erroneous case is called a race condition. The problem with +races is that they are difficult to reproduce. For example, if you +put print statements in to debug the incorrect behavior, you might +change the time and the race might not happen anymore. + +

Atomic instructions

+ +

The programmer must be able express that A and B should be executed +as single atomic instruction. We generally use a concept like locks +to mark an atomic region, acquiring the lock at the beginning of the +section and releasing it at the end: + +

 
+void acquire(int *lock) {
+   while (TSL(lock) != 0) ; 
+}
+
+void release (int *lock) {
+  *lock = 0;
+}
+
+ +

Acquire and release, of course, need to be atomic too, which can, +for example, be done with a hardware atomic TSL (try-set-lock) +instruction: + +

The semantics of TSL are: +

+   R <- [mem]   // load content of mem into register R
+   [mem] <- 1   // store 1 in mem.
+
+ +

In a harware implementation, the bus arbiter guarantees that both +the load and store are executed without any other load/stores coming +in between. + +

We can use locks to implement an atomic insert, or we can use +TSL directly: +

+int insert_lock = 0;
+
+insert(int data) {
+
+  /* acquire the lock: */
+  while(TSL(&insert_lock) != 0)
+    ;
+
+  /* critical section: */
+  List *l = new List;
+  l->data = data;
+  l->next = list;
+  list = l;
+
+  /* release the lock: */
+  insert_lock = 0;
+}
+
+ +

It is the programmer's job to make sure that locks are respected. If +a programmer writes another function that manipulates the list, the +programmer must must make sure that the new functions acquires and +releases the appropriate locks. If the programmer doesn't, race +conditions occur. + +

This code assumes that stores commit to memory in program order and +that all stores by other processors started before insert got the lock +are observable by this processor. That is, after the other processor +released a lock, all the previous stores are committed to memory. If +a processor executes instructions out of order, this assumption won't +hold and we must, for example, a barrier instruction that makes the +assumption true. + + +

Example: Locking on x86

+ +

Here is one way we can implement acquire and release using the x86 +xchgl instruction: + +

+struct Lock {
+  unsigned int locked;
+};
+
+acquire(Lock *lck) {
+  while(TSL(&(lck->locked)) != 0)
+    ;
+}
+
+release(Lock *lck) {
+  lck->locked = 0;
+}
+
+int
+TSL(int *addr)
+{
+  register int content = 1;
+  // xchgl content, *addr
+  // xchgl exchanges the values of its two operands, while
+  // locking the memory bus to exclude other operations.
+  asm volatile ("xchgl %0,%1" :
+                "=r" (content),
+                "=m" (*addr) :
+                "0" (content),
+                "m" (*addr));
+  return(content);
+}
+
+ +

the instruction "XCHG %eax, (content)" works as follows: +

    +
  1. freeze other CPUs' memory activity +
  2. temp := content +
  3. content := %eax +
  4. %eax := temp +
  5. un-freeze other CPUs +
+ +

steps 1 and 5 make XCHG special: it is "locked" special signal + lines on the inter-CPU bus, bus arbitration + +

This implementation doesn't scale to a large number of processors; + in a later lecture we will see how we could do better. + +

Lock granularity

+ +

Release/acquire is ideal for short atomic sections: increment a +counter, search in i-node cache, allocate a free buffer. + +

What are spin locks not so great for? Long atomic sections may + waste waiters' CPU time and it is to sleep while holding locks. In + xv6 we try to avoid long atomic sections by carefully coding (can + you find an example?). xv6 doesn't release the processor when + holding a lock, but has an additional set of coordination primitives + (sleep and wakeup), which we will study later. + +

My list_lock protects all lists; inserts to different lists are + blocked. A lock per list would waste less time spinning so you might + want "fine-grained" locks, one for every object BUT acquire/release + are expensive (500 cycles on my 3 ghz machine) because they need to + talk off-chip. + +

Also, "correctness" is not that simple with fine-grained locks if + need to maintain global invariants; e.g., "every buffer must be on + exactly one of free list and device list". Per-list locks are + irrelevant for this invariant. So you might want "large-grained", + which reduces overhead but reduces concurrency. + +

This tension is hard to get right. One often starts out with + "large-grained locks" and measures the performance of the system on + some workloads. When more concurrency is desired (to get better + performance), an implementor may switch to a more fine-grained + scheme. Operating system designers fiddle with this all the time. + +

Recursive locks and modularity

+ +

When designing a system we desire clean abstractions and good + modularity. We like a caller not have to know about how a callee + implements a particul functions. Locks make achieving modularity + more complicated. For example, what to do when the caller holds a + lock, then calls a function, which also needs to the lock to perform + its job. + +

There are no transparent solutions that allow the caller and callee + to be unaware of which lokcs they use. One transparent, but + unsatisfactory option is recursive locks: If a callee asks for a + lock that its caller has, then we allow the callee to proceed. + Unfortunately, this solution is not ideal either. + +

Consider the following. If lock x protects the internals of some + struct foo, then if the caller acquires lock x, it know that the + internals of foo are in a sane state and it can fiddle with them. + And then the caller must restore them to a sane state before release + lock x, but until then anything goes. + +

This assumption doesn't hold with recursive locking. After + acquiring lock x, the acquirer knows that either it is the first to + get this lock, in which case the internals are in a sane state, or + maybe some caller holds the lock and has messed up the internals and + didn't realize when calling the callee that it was going to try to + look at them too. So the fact that a function acquired the lock x + doesn't guarantee anything at all. In short, locks protect against + callers and callees just as much as they protect against other + threads. + +

Since transparent solutions aren't ideal, it is better to consider + locks part of the function specification. The programmer must + arrange that a caller doesn't invoke another function while holding + a lock that the callee also needs. + +

Locking in xv6

+ +

xv6 runs on a multiprocessor and is programmed to allow multiple +threads of computation to run concurrently. In xv6 an interrupt might +run on one processor and a process in kernel mode may run on another +processor, sharing a kernel data structure with the interrupt routing. +xv6 uses locks, implemented using an atomic instruction, to coordinate +concurrent activities. + +

Let's check out why xv6 needs locks by following what happens when +we start a second processor: +

+ +

Why hold proc_table_lock during a context switch? It protects +p->state; the process has to hold some lock to avoid a race with +wakeup() and yield(), as we will see in the next lectures. + +

Why not a lock per proc entry? It might be expensive in in whole +table scans (in wait, wakeup, scheduler). proc_table_lock also +protects some larger invariants, for example it might be hard to get +proc_wait() right with just per entry locks. Right now the check to +see if there are any exited children and the sleep are atomic -- but +that would be hard with per entry locks. One could have both, but +that would probably be neither clean nor fast. + +

Of course, there is only processor searching the proc table if +acquire is implemented correctly. Let's check out acquire in +spinlock.c: +

+ +

+ +

Locking in JOS

+ +

JOS is meant to run on single-CPU machines, and the plan can be +simple. The simple plan is disabling/enabling interrupts in the +kernel (IF flags in the EFLAGS register). Thus, in the kernel, +threads release the processors only when they want to and can ensure +that they don't release the processor during a critical section. + +

In user mode, JOS runs with interrupts enabled, but Unix user +applications don't share data structures. The data structures that +must be protected, however, are the ones shared in the library +operating system (e.g., pipes). In JOS we will use special-case +solutions, as you will find out in lab 6. For example, to implement +pipe we will assume there is one reader and one writer. The reader +and writer never update each other's variables; they only read each +other's variables. Carefully programming using this rule we can avoid +races. diff --git a/web/l-mkernel.html b/web/l-mkernel.html new file mode 100644 index 0000000..2984796 --- /dev/null +++ b/web/l-mkernel.html @@ -0,0 +1,262 @@ +Microkernel lecture + + + + + +

Microkernels

+ +

Required reading: Improving IPC by kernel design + +

Overview

+ +

This lecture looks at the microkernel organization. In a +microkernel, services that a monolithic kernel implements in the +kernel are running as user-level programs. For example, the file +system, UNIX process management, pager, and network protocols each run +in a separate user-level address space. The microkernel itself +supports only the services that are necessary to allow system services +to run well in user space; a typical microkernel has at least support +for creating address spaces, threads, and inter process communication. + +

The potential advantages of a microkernel are simplicity of the +kernel (small), isolation of operating system components (each runs in +its own user-level address space), and flexibility (we can have a file +server and a database server). One potential disadvantage is +performance loss, because what in a monolithich kernel requires a +single system call may require in a microkernel multiple system calls +and context switches. + +

One way in how microkernels differ from each other is the exact +kernel API they implement. For example, Mach (a system developed at +CMU, which influenced a number of commercial operating systems) has +the following system calls: processes (create, terminate, suspend, +resume, priority, assign, info, threads), threads (fork, exit, join, +detach, yield, self), ports and messages (a port is a unidirectionally +communication channel with a message queue and supporting primitives +to send, destroy, etc), and regions/memory objects (allocate, +deallocate, map, copy, inherit, read, write). + +

Some microkernels are more "microkernel" than others. For example, +some microkernels implement the pager in user space but the basic +virtual memory abstractions in the kernel (e.g, Mach); others, are +more extreme, and implement most of the virtual memory in user space +(L4). Yet others are less extreme: many servers run in their own +address space, but in kernel mode (Chorus). + +

All microkernels support multiple threads per address space. xv6 +and Unix until recently didn't; why? Because, in Unix system services +are typically implemented in the kernel, and those are the primary +programs that need multiple threads to handle events concurrently +(waiting for disk and processing new I/O requests). In microkernels, +these services are implemented in user-level address spaces and so +they need a mechanism to deal with handling operations concurrently. +(Of course, one can argue if fork efficient enough, there is no need +to have threads.) + +

L3/L4

+ +

L3 is a predecessor to L4. L3 provides data persistence, DOS +emulation, and ELAN runtime system. L4 is a reimplementation of L3, +but without the data persistence. L4KA is a project at +sourceforge.net, and you can download the code for the latest +incarnation of L4 from there. + +

L4 is a "second-generation" microkernel, with 7 calls: IPC (of +which there are several types), id_nearest (find a thread with an ID +close the given ID), fpage_unmap (unmap pages, mapping is done as a +side-effect of IPC), thread_switch (hand processor to specified +thread), lthread_ex_regs (manipulate thread registers), +thread_schedule (set scheduling policies), task_new (create a new +address space with some default number of threads). These calls +provide address spaces, tasks, threads, interprocess communication, +and unique identifiers. An address space is a set of mappings. +Multiple threads may share mappings, a thread may grants mappings to +another thread (through IPC). Task is the set of threads sharing an +address space. + +

A thread is the execution abstraction; it belongs to an address +space, a UID, a register set, a page fault handler, and an exception +handler. A UID of a thread is its task number plus the number of the +thread within that task. + +

IPC passes data by value or by reference to another address space. +It also provide for sequence coordination. It is used for +communication between client and servers, to pass interrupts to a +user-level exception handler, to pass page faults to an external +pager. In L4, device drivers are implemented has a user-level +processes with the device mapped into their address space. +Linux runs as a user-level process. + +

L4 provides quite a scala of messages types: inline-by-value, +strings, and virtual memory mappings. The send and receive descriptor +specify how many, if any. + +

In addition, there is a system call for timeouts and controling +thread scheduling. + +

L3/L4 paper discussion

+ + +Why must the parent directory be locked? If two processes try to +create the same name in the same directory, only one should succeed +and the other one, should receive an error (file exist). + +

Link, unlink, chdir, mount, umount could have taken file +descriptors instead of their path argument. In fact, this would get +rid of some possible race conditions (some of which have security +implications, TOCTTOU). However, this would require that the current +working directory be remembered by the process, and UNIX didn't have +good ways of maintaining static state shared among all processes +belonging to a given user. The easiest way is to create shared state +is to place it in the kernel. + +

We have one piece of code in xv6 that we haven't studied: exec. + With all the ground work we have done this code can be easily + understood (see sheet 54). + + diff --git a/web/l-okws.txt b/web/l-okws.txt new file mode 100644 index 0000000..fa940d0 --- /dev/null +++ b/web/l-okws.txt @@ -0,0 +1,249 @@ + +Security +------------------- +I. 2 Intro Examples +II. Security Overview +III. Server Security: Offense + Defense +IV. Unix Security + POLP +V. Example: OKWS +VI. How to Build a Website + +I. Intro Examples +-------------------- +1. Apache + OpenSSL 0.9.6a (CAN 2002-0656) + - SSL = More security! + + unsigned int j; + p=(unsigned char *)s->init_buf->data; + j= *(p++); + s->session->session_id_length=j; + memcpy(s->session->session_id,p,j); + + - the result: an Apache worm + +2. SparkNotes.com 2000: + - New profile feature that displays "public" information about users + but bug that made e-mail addresses "public" by default. + - New program for getting that data: + + http://www.sparknotes.com/getprofile.cgi?id=1343 + +II. Security Overview +---------------------- + +What Is Security? + - Protecting your system from attack. + + What's an attack? + - Stealing data + - Corrupting data + - Controlling resources + - DOS + + Why attack? + - Money + - Blackmail / extortion + - Vendetta + - intellectual curiosity + - fame + +Security is a Big topic + + - Server security -- today's focus. There's some machine sitting on the + Internet somewhere, with a certain interface exposed, and attackers + want to circumvent it. + - Why should you trust your software? + + - Client security + - Clients are usually servers, so they have many of the same issues. + - Slight simplification: people across the network cannot typically + initiate connections. + - Has a "fallible operator": + - Spyware + - Drive-by-Downloads + + - Client security turns out to be much harder -- GUI considerations, + look inside the browser and the applications. + - Systems community can more easily handle server security. + - We think mainly of servers. + +III. Server Security: Offense and Defense +----------------------------------------- + - Show picture of a Web site. + + Attacks | Defense +---------------------------------------------------------------------------- + 1. Break into DB from net | 1. FW it off + 2. Break into WS on telnet | 2. FW it off + 3. Buffer overrun in Apache | 3. Patch apache / use better lang? + 4. Buffer overrun in our code | 4. Use better lang / isolate it + 5. SQL injection | 5. Better escaping / don't interpret code. + 6. Data scraping. | 6. Use a sparse UID space. + 7. PW sniffing | 7. ??? + 8. Fetch /etc/passwd and crack | 8. Don't expose /etc/passwd + PW | + 9. Root escalation from apache | 9. No setuid programs available to Apache +10. XSS |10. Filter JS and input HTML code. +11. Keystroke recorded on sys- |11. Client security + admin's desktop (planetlab) | +12. DDOS |12. ??? + +Summary: + - That we want private data to be available to right people makes + this problem hard in the first place. Internet servers are there + for a reason. + - Security != "just encrypt your data;" this in fact can sometimes + make the problem worse. + - Best to prevent break-ins from happening in the first place. + - If they do happen, want to limit their damage (POLP). + - Security policies are difficult to express / package up neatly. + +IV. Design According to POLP (in Unix) +--------------------------------------- + - Assume any piece of a system can be compromised, by either bad + programming or malicious attack. + - Try to limit the damage done by such a compromise (along the lines + of the 4 attack goals). + + + +What's the goal on Unix? + - Keep processes from communicating that don't have to: + - limit FS, IPC, signals, ptrace + - Strip away unneeded privilege + - with respect to network, FS. + - Strip away FS access. + +How on Unix? + - setuid/setgid + - system call interposition + - chroot (away from setuid executables, /etc/passwd, /etc/ssh/..) + + + +How do you write chroot'ed programs? + - What about shared libraries? + - /etc/resolv.conf? + - Can chroot'ed programs access the FS at all? What if they need + to write to the FS or read from the FS? + - Fd's are *capabilities*; can pass them to chroot'ed services, + thereby opening new files on its behalf. + - Unforgeable - can only get them from the kernel via open/socket, etc. + +Unix Shortcomings (round 1) + - It's bad to run as root! + - Yet, need root for: + - chroot + - setuid/setgid to a lower-privileged user + - create a new user ID + - Still no guarantee that we've cut off all channels + - 200 syscalls! + - Default is to give most/all privileges. + - Can "break out" of chroot jails? + - Can still exploit race conditions in the kernel to escalate privileges. + +Sidebar + - setuid / setuid misunderstanding + - root / root misunderstanding + - effective vs. real vs. saved set-user-ID + +V. OKWS +------- +- Taking these principles as far as possible. +- C.f. Figure 1 From the paper.. +- Discussion of which privileges are in which processes + + + +- Technical details: how to launch a new service +- Within the launcher (running as root): + + + + // receive FDs from logger, pubd, demux + fork (); + chroot ("/var/okws/run"); + chdir ("/coredumps/51001"); + setgid (51001); + setuid (51001); + exec ("login", fds ... ); + +- Note no chroot -- why not? +- Once launched, how does a service get new connections? +- Note the goal - minimum tampering with each other in the + case of a compromise. + +Shortcoming of Unix (2) +- A lot of plumbing involved with this system. FDs flying everywhere. +- Isolation still not fine enough. If a service gets taken over, + can compromise all users of that service. + +VI. Reflections on Building Websites +--------------------------------- +- OKWS interesting "experiment" +- Need for speed; also, good gzip support. +- If you need compiled code, it's a good way to go. +- RPC-like system a must for backend communication +- Connection-pooling for free + +Biggest difficulties: +- Finding good C++ programmers. +- Compile times. +- The DB is still always the problem. + +Hard to Find good Alternatives +- Python / Perl - you might spend a lot of time writing C code / + integrating with lower level languages. +- Have to worry about DB pooling. +- Java -- must viable, and is getting better. Scary you can't peer + inside. +- .Net / C#-based system might be the way to go. + + +======================================================================= + +Extra Material: + +Capabilities (From the Eros Paper in SOSP 1999) + + - "Unforgeable pair made up of an object ID and a set of authorized + operations (an interface) on that object." + - c.f. Dennis and van Horn. "Programming semantics for multiprogrammed + computations," Communications of the ACM 9(3):143-154, Mar 1966. + - Thus: + + - Examples: + "Process X can write to file at inode Y" + "Process P can read from file at inode Z" + - Familiar example: Unix file descriptors + + - Why are they secure? + - Capabilities are "unforgeable" + - Processes can get them only through authorized interfaces + - Capabilities are only given to processes authorized to hold them + + - How do you get them? + - From the kernel (e.g., open) + - From other applications (e.g., FD passing) + + - How do you use them? + - read (fd), write(fd). + + - How do you revoke them once granted? + - In Unix, you do not. + - In some systems, a central authority ("reference monitor") can revoke. + + - How do you store them persistently? + - Can have circular dependencies (unlike an FS). + - What happens when the system starts up? + - Revert to checkpointed state. + - Often capability systems chose a single-level store. + + - Capability systems, a historical prospective: + - KeyKOS, Eros, Cyotos (UP research) + - Never saw any applications + - IBM Systems (System 38, later AS/400, later 'i Series') + - Commercially viable + - Problems: + - All bets are off when a capability is sent to the wrong place. + - Firewall analogy? diff --git a/web/l-plan9.html b/web/l-plan9.html new file mode 100644 index 0000000..a3af3d5 --- /dev/null +++ b/web/l-plan9.html @@ -0,0 +1,249 @@ + + +Plan 9 + + + +

Plan 9

+ +

Required reading: Plan 9 from Bell Labs

+ +

Background

+ +

Had moved away from the ``one computing system'' model of +Multics and Unix.

+ +

Many computers (`workstations'), self-maintained, not a coherent whole.

+ +

Pike and Thompson had been batting around ideas about a system glued together +by a single protocol as early as 1984. +Various small experiments involving individual pieces (file server, OS, computer) +tried throughout 1980s.

+ +

Ordered the hardware for the ``real thing'' in beginning of 1989, +built up WORM file server, kernel, throughout that year.

+ +

Some time in early fall 1989, Pike and Thompson were +trying to figure out a way to fit the window system in. +On way home from dinner, both independently realized that +needed to be able to mount a user-space file descriptor, +not just a network address.

+ +

Around Thanksgiving 1989, spent a few days rethinking the whole +thing, added bind, new mount, flush, and spent a weekend +making everything work again. The protocol at that point was +essentially identical to the 9P in the paper.

+ +

In May 1990, tried to use system as self-hosting. +File server kept breaking, had to keep rewriting window system. +Dozen or so users by then, mostly using terminal windows to +connect to Unix.

+ +

Paper written and submitted to UKUUG in July 1990.

+ +

Because it was an entirely new system, could take the +time to fix problems as they arose, in the right place.

+ + +

Design Principles

+ +

Three design principles:

+ +

+1. Everything is a file.
+2. There is a standard protocol for accessing files.
+3. Private, malleable name spaces (bind, mount). +

+ +

Everything is a file.

+ +

Everything is a file (more everything than Unix: networks, graphics).

+ +
+% ls -l /net
+% lp /dev/screen
+% cat /mnt/wsys/1/text
+
+ +

Standard protocol for accessing files

+ +

9P is the only protocol the kernel knows: other protocols +(NFS, disk file systems, etc.) are provided by user-level translators.

+ +

Only one protocol, so easy to write filters and other +converters. Iostats puts itself between the kernel +and a command.

+ +
+% iostats -xvdfdf /bin/ls
+
+ +

Private, malleable name spaces

+ +

Each process has its own private name space that it +can customize at will. +(Full disclosure: can arrange groups of +processes to run in a shared name space. Otherwise how do +you implement mount and bind?)

+ +

Iostats remounts the root of the name space +with its own filter service.

+ +

The window system mounts a file system that it serves +on /mnt/wsys.

+ +

The network is actually a kernel device (no 9P involved) +but it still serves a file interface that other programs +use to access the network. +Easy to move out to user space (or replace) if necessary: +import network from another machine.

+ +

Implications

+ +

Everything is a file + can share files => can share everything.

+ +

Per-process name spaces help move toward ``each process has its own +private machine.''

+ +

One protocol: easy to build custom filters to add functionality +(e.g., reestablishing broken network connections). + +

File representation for networks, graphics, etc.

+ +

Unix sockets are file descriptors, but you can't use the +usual file operations on them. Also far too much detail that +the user doesn't care about.

+ +

In Plan 9: +

dial("tcp!plan9.bell-labs.com!http");
+
+(Protocol-independent!)

+ +

Dial more or less does:
+write to /net/cs: tcp!plan9.bell-labs.com!http +read back: /net/tcp/clone 204.178.31.2!80 +write to /net/tcp/clone: connect 204.178.31.2!80 +read connection number: 4 +open /net/tcp/4/data +

+ +

Details don't really matter. Two important points: +protocol-independent, and ordinary file operations +(open, read, write).

+ +

Networks can be shared just like any other files.

+ +

Similar story for graphics, other resources.

+ +

Conventions

+ +

Per-process name spaces mean that even full path names are ambiguous +(/bin/cat means different things on different machines, +or even for different users).

+ +

Convention binds everything together. +On a 386, bind /386/bin /bin. + +

In Plan 9, always know where the resource should be +(e.g., /net, /dev, /proc, etc.), +but not which one is there.

+ +

Can break conventions: on a 386, bind /alpha/bin /bin, just won't +have usable binaries in /bin anymore.

+ +

Object-oriented in the sense of having objects (files) that all +present the same interface and can be substituted for one another +to arrange the system in different ways.

+ +

Very little ``type-checking'': bind /net /proc; ps. +Great benefit (generality) but must be careful (no safety nets).

+ + +

Other Contributions

+ +

Portability

+ +

Plan 9 still is the most portable operating system. +Not much machine-dependent code, no fancy features +tied to one machine's MMU, multiprocessor from the start (1989).

+ +

Many other systems are still struggling with converting to SMPs.

+ +

Has run on MIPS, Motorola 68000, Nextstation, Sparc, x86, PowerPC, Alpha, others.

+ +

All the world is not an x86.

+ +

Alef

+ +

New programming language: convenient, but difficult to maintain. +Retired when author (Winterbottom) stopped working on Plan 9.

+ +

Good ideas transferred to C library plus conventions.

+ +

All the world is not C.

+ +

UTF-8

+ +

Thompson invented UTF-8. Pike and Thompson +converted Plan 9 to use it over the first weekend of September 1992, +in time for X/Open to choose it as the Unicode standard byte format +at a meeting the next week.

+ +

UTF-8 is now the standard character encoding for Unicode on +all systems and interoperating between systems.

+ +

Simple, easy to modify base for experiments

+ +

Whole system source code is available, simple, easy to +understand and change. +There's a reason it only took a couple days to convert to UTF-8.

+ +
+  49343  file server kernel
+
+ 181611  main kernel
+  78521    ipaq port (small kernel)
+  20027      TCP/IP stack
+  15365      ipaq-specific code
+  43129      portable code
+
+1326778  total lines of source code
+
+ +

Dump file system

+ +

Snapshot idea might well have been ``in the air'' at the time. +(OldFiles in AFS appears to be independently derived, +use of WORM media was common research topic.)

+ +

Generalized Fork

+ +

Picked up by other systems: FreeBSD, Linux.

+ +

Authentication

+ +

No global super-user. +Newer, more Plan 9-like authentication described in later paper.

+ +

New Compilers

+ +

Much faster than gcc, simpler.

+ +

8s to build acme for Linux using gcc; 1s to build acme for Plan 9 using 8c (but running on Linux)

+ +

IL Protocol

+ +

Now retired. +For better or worse, TCP has all the installed base. +IL didn't work very well on asymmetric or high-latency links +(e.g., cable modems).

+ +

Idea propagation

+ +

Many ideas have propagated out to varying degrees.

+ +

Linux even has bind and user-level file servers now (FUSE), +but still not per-process name spaces.

+ + + diff --git a/web/l-scalablecoord.html b/web/l-scalablecoord.html new file mode 100644 index 0000000..da72c37 --- /dev/null +++ b/web/l-scalablecoord.html @@ -0,0 +1,202 @@ +Scalable coordination + + + + + +

Scalable coordination

+ +

Required reading: Mellor-Crummey and Scott, Algorithms for Scalable + Synchronization on Shared-Memory Multiprocessors, TOCS, Feb 1991. + +

Overview

+ +

Shared memory machines are bunch of CPUs, sharing physical memory. +Typically each processor also mantains a cache (for performance), +which introduces the problem of keep caches coherent. If processor 1 +writes a memory location whose value processor 2 has cached, then +processor 2's cache must be updated in some way. How? +

    + +
  • Bus-based schemes. Any CPU can access "dance with" any memory +equally ("dance hall arch"). Use "Snoopy" protocols: Each CPU's cache +listens to the memory bus. With write-through architecture, invalidate +copy when see a write. Or can have "ownership" scheme with write-back +cache (E.g., Pentium cache have MESI bits---modified, exclusive, +shared, invalid). If E bit set, CPU caches exclusively and can do +write back. But bus places limits on scalability. + +
  • More scalability w. NUMA schemes (non-uniform memory access). Each +CPU comes with fast "close" memory. Slower to access memory that is +stored with another processor. Use a directory to keep track of who is +caching what. For example, processor 0 is responsible for all memory +starting with address "000", processor 1 is responsible for all memory +starting with "001", etc. + +
  • COMA - cache-only memory architecture. Each CPU has local RAM, +treated as cache. Cache lines migrate around to different nodes based +on access pattern. Data only lives in cache, no permanent memory +location. (These machines aren't too popular any more.) + +
+ + +

Scalable locks

+ +

This paper is about cost and scalability of locking; what if you +have 10 CPUs waiting for the same lock? For example, what would +happen if xv6 runs on an SMP with many processors? + +

What's the cost of a simple spinning acquire/release? Algorithm 1 +*without* the delays, which is like xv6's implementation of acquire +and release (xv6 uses XCHG instead of test_and_set): +

+  each of the 10 CPUs gets the lock in turn
+  meanwhile, remaining CPUs in XCHG on lock
+  lock must be X in cache to run XCHG
+    otherwise all might read, then all might write
+  so bus is busy all the time with XCHGs!
+  can we avoid constant XCHGs while lock is held?
+
+ +

test-and-test-and-set +

+  only run expensive TSL if not locked
+  spin on ordinary load instruction, so cache line is S
+  acquire(l)
+    while(1){
+      while(l->locked != 0) { }
+      if(TSL(&l->locked) == 0)
+        return;
+    }
+
+ +

suppose 10 CPUs are waiting, let's count cost in total bus + transactions +

+  CPU1 gets lock in one cycle
+    sets lock's cache line to I in other CPUs
+  9 CPUs each use bus once in XCHG
+    then everyone has the line S, so they spin locally
+  CPU1 release the lock
+  CPU2 gets the lock in one cycle
+  8 CPUs each use bus once...
+  So 10 + 9 + 8 + ... = 50 transactions, O(n^2) in # of CPUs!
+  Look at "test-and-test-and-set" in Figure 6
+
+

Can we have n CPUs acquire a lock in O(n) time? + +

What is the point of the exponential backoff in Algorithm 1? +

+  Does it buy us O(n) time for n acquires?
+  Is there anything wrong with it?
+  may not be fair
+  exponential backoff may increase delay after release
+
+ +

What's the point of the ticket locks, Algorithm 2? +

+  one interlocked instruction to get my ticket number
+  then I spin on now_serving with ordinary load
+  release() just increments now_serving
+
+ +

why is that good? +

+  + fair
+  + no exponential backoff overshoot
+  + no spinning on 
+
+ +

but what's the cost, in bus transactions? +

+  while lock is held, now_serving is S in all caches
+  release makes it I in all caches
+  then each waiters uses a bus transaction to get new value
+  so still O(n^2)
+
+ +

What's the point of the array-based queuing locks, Algorithm 3? +

+    a lock has an array of "slots"
+    waiter allocates a slot, spins on that slot
+    release wakes up just next slot
+  so O(n) bus transactions to get through n waiters: good!
+  anderson lines in Figure 4 and 6 are flat-ish
+    they only go up because lock data structures protected by simpler lock
+  but O(n) space *per lock*!
+
+ +

Algorithm 5 (MCS), the new algorithm of the paper, uses +compare_and_swap: +

+int compare_and_swap(addr, v1, v2) {
+  int ret = 0;
+  // stop all memory activity and ignore interrupts
+  if (*addr == v1) {
+    *addr = v2;
+    ret = 1;
+  }
+  // resume other memory activity and take interrupts
+  return ret;
+}
+
+ +

What's the point of the MCS lock, Algorithm 5? +

+  constant space per lock, rather than O(n)
+  one "qnode" per thread, used for whatever lock it's waiting for
+  lock holder's qnode points to start of list
+  lock variable points to end of list
+  acquire adds your qnode to end of list
+    then you spin on your own qnode
+  release wakes up next qnode
+
+ +

Wait-free or non-blocking data structures

+ +

The previous implementations all block threads when there is + contention for a lock. Other atomic hardware operations allows one + to build implementation wait-free data structures. For example, one + can make an insert of an element in a shared list that don't block a + thread. Such versions are called wait free. + +

A linked list with locks is as follows: +

+Lock list_lock;
+
+insert(int x) {
+  element *n = new Element;
+  n->x = x;
+
+  acquire(&list_lock);
+  n->next = list;
+  list = n;
+  release(&list_lock);
+}
+
+ +

A wait-free implementation is as follows: +

+insert (int x) {
+  element *n = new Element;
+  n->x = x;
+  do {
+     n->next = list;
+  } while (compare_and_swap (&list, n->next, n) == 0);
+}
+
+

How many bus transactions with 10 CPUs inserting one element in the +list? Could you do better? + +

This + paper by Fraser and Harris compares lock-based implementations + versus corresponding non-blocking implementations of a number of data + structures. + +

It is not possible to make every operation wait-free, and there are + times we will need an implementation of acquire and release. + research on non-blocking data structures is active; the last word + isn't said on this topic yet. + + diff --git a/web/l-schedule.html b/web/l-schedule.html new file mode 100644 index 0000000..d87d7da --- /dev/null +++ b/web/l-schedule.html @@ -0,0 +1,340 @@ +Scheduling + + + + + +

Scheduling

+ +

Required reading: Eliminating receive livelock + +

Notes based on prof. Morris's lecture on scheduling (6.824, fall'02). + +

Overview

+ +
    + +
  • What is scheduling? The OS policies and mechanisms to allocates +resources to entities. A good scheduling policy ensures that the most +important entitity gets the resources it needs. This topic was +popular in the days of time sharing, when there was a shortage of +resources. It seemed irrelevant in era of PCs and workstations, when +resources were plenty. Now the topic is back from the dead to handle +massive Internet servers with paying customers. The Internet exposes +web sites to international abuse and overload, which can lead to +resource shortages. Furthermore, some customers are more important +than others (e.g., the ones that buy a lot). + +
  • Key problems: +
      +
    • Gap between desired policy and available mechanism. The desired +policies often include elements that not implementable with the +mechanisms available to the operation system. Furthermore, often +there are many conflicting goals (low latency, high throughput, and +fairness), and the scheduler must make a trade-off between the goals. + +
    • Interaction between different schedulers. One have to take a +systems view. Just optimizing the CPU scheduler may do little to for +the overall desired policy. +
    + +
  • Resources you might want to schedule: CPU time, physical memory, +disk and network I/O, and I/O bus bandwidth. + +
  • Entities that you might want to give resources to: users, +processes, threads, web requests, or MIT accounts. + +
  • Many polices for resource to entity allocation are possible: +strict priority, divide equally, shortest job first, minimum guarantee +combined with admission control. + +
  • General plan for scheduling mechanisms +
      +
    1. Understand where scheduling is occuring. +
    2. Expose scheduling decisions, allow control. +
    3. Account for resource consumption, to allow intelligent control. +
    + +
  • Simple example from 6.828 kernel. The policy for scheduling +environments is to give each one equal CPU time. The mechanism used to +implement this policy is a clock interrupt every 10 msec and then +selecting the next environment in a round-robin fashion. + +

    But this only works if processes are compute-bound. What if a +process gives up some of its 10 ms to wait for input? Do we have to +keep track of that and give it back? + +

    How long should the quantum be? is 10 msec the right answer? +Shorter quantum will lead to better interactive performance, but +lowers overall system throughput because we will reschedule more, +which has overhead. + +

    What if the environment computes for 1 msec and sends an IPC to +the file server environment? Shouldn't the file server get more CPU +time because it operates on behalf of all other functions? + +

    Potential improvements for the 6.828 kernel: track "recent" CPU use +(e.g., over the last second) and always run environment with least +recent CPU use. (Still, if you sleep long enough you lose.) Other +solution: directed yield; specify on the yield to which environment +you are donating the remainder of the quantuam (e.g., to the file +server so that it can compute on the environment's behalf). + +

  • Pitfall: Priority Inversion +
    +  Assume policy is strict priority.
    +  Thread T1: low priority.
    +  Thread T2: medium priority.
    +  Thread T3: high priority.
    +  T1: acquire(l)
    +  context switch to T3
    +  T3: acquire(l)... must wait for T1 to release(l)...
    +  context switch to T2
    +  T2 computes for a while
    +  T3 is indefinitely delayed despite high priority.
    +  Can solve if T3 lends its priority to holder of lock it is waiting for.
    +    So T1 runs, not T2.
    +  [this is really a multiple scheduler problem.]
    +  [since locks schedule access to locked resource.]
    +
    + +
  • Pitfall: Efficiency. Efficiency often conflicts with fairness (or +any other policy). Long time quantum for efficiency in CPU scheduling +versus low delay. Shortest seek versus FIFO disk scheduling. +Contiguous read-ahead vs data needed now. For example, scheduler +swaps out my idle emacs to let gcc run faster with more phys mem. +What happens when I type a key? These don't fit well into a "who gets +to go next" scheduler framework. Inefficient scheduling may make +everybody slower, including high priority users. + +
  • Pitfall: Multiple Interacting Schedulers. Suppose you want your +emacs to have priority over everything else. Give it high CPU +priority. Does that mean nothing else will run if emacs wants to run? +Disk scheduler might not know to favor emacs's disk I/Os. Typical +UNIX disk scheduler favors disk efficiency, not process prio. Suppose +emacs needs more memory. Other processes have dirty pages; emacs must +wait. Does disk scheduler know these other processes' writes are high +prio? + +
  • Pitfall: Server Processes. Suppose emacs uses X windows to +display. The X server must serve requests from many clients. Does it +know that emacs' requests should be given priority? Does the OS know +to raise X's priority when it is serving emacs? Similarly for DNS, +and NFS. Does the network know to give emacs' NFS requests priority? + +
+ +

In short, scheduling is a system problem. There are many +schedulers; they interact. The CPU scheduler is usually the easy +part. The hardest part is system structure. For example, the +existence of interrupts is bad for scheduling. Conflicting +goals may limit effectiveness. + +

Case study: modern UNIX

+ +

Goals: +

    +
  • Simplicity (e.g. avoid complex locking regimes). +
  • Quick response to device interrupts. +
  • Favor interactive response. +
+ +

UNIX has a number of execution environments. We care about +scheduling transitions among them. Some transitions aren't possible, +some can't be be controlled. The execution environments are: + +

    +
  • Process, user half +
  • Process, kernel half +
  • Soft interrupts: timer, network +
  • Device interrupts +
+ +

The rules are: +

    +
  • User is pre-emptible. +
  • Kernel half and software interrupts are not pre-emptible. +
  • Device handlers may not make blocking calls (e.g., sleep) +
  • Effective priorities: intr > soft intr > kernel half > user +
+ + + +

Rules are implemented as follows: + +

    + +
  • UNIX: Process User Half. Runs in process address space, on +per-process stack. Interruptible. Pre-emptible: interrupt may cause +context switch. We don't trust user processes to yield CPU. +Voluntarily enters kernel half via system calls and faults. + +
  • UNIX: Process Kernel Half. Runs in kernel address space, on +per-process kernel stack. Executes system calls and faults for its +process. Interruptible (but can defer interrupts in critical +sections). Not pre-emptible. Only yields voluntarily, when waiting +for an event. E.g. disk I/O done. This simplifies concurrency +control; locks often not required. No user process runs if any kernel +half wants to run. Many process' kernel halfs may be sleeping in the +kernel. + +
  • UNIX: Device Interrupts. Hardware asks CPU for an interrupt to ask +for attention. Disk read/write completed, or network packet received. +Runs in kernel space, on special interrupt stack. Interrupt routine +cannot block; must return. Interrupts are interruptible. They nest +on the one interrupt stack. Interrupts are not pre-emptible, and +cannot really yield. The real-time clock is a device and interrupts +every 10ms (or whatever). Process scheduling decisions can be made +when interrupt returns (e.g. wake up the process waiting for this +event). You want interrupt processing to be fast, since it has +priority. Don't do any more work than you have to. You're blocking +processes and other interrupts. Typically, an interrupt does the +minimal work necessary to keep the device happy, and then call wakeup +on a thread. + +
  • UNIX: Soft Interrupts. (Didn't exist in xv6) Used when device +handling is expensive. But no obvious process context in which to +run. Examples include IP forwarding, TCP input processing. Runs in +kernel space, on interrupt stack. Interruptable. Not pre-emptable, +can't really yield. Triggered by hardware interrupt. Called when +outermost hardware interrupt returns. Periodic scheduling decisions +are made in timer s/w interrupt. Scheduled by hardware timer +interrupt (i.e., if current process has run long enough, switch). +
+ +

Is this good software structure? Let's talk about receive +livelock. + +

Paper discussion

+ +
    + +
  • What is application that the paper is addressing: IP forwarding. +What functionality does a network interface offer to driver? +
      +
    • Read packets +
    • Poke hardware to send packets +
    • Interrupts when packet received/transmit complete +
    • Buffer many input packets +
    + +
  • What devices in the 6.828 kernel are interrupt driven? Which one +are polling? Is this ideal? + +
  • Explain Figure 6-1. Why does it go up? What determines how high +the peak is? Why does it go down? What determines how fast it goes +does? Answer: +
    +(fraction of packets discarded)(work invested in discarded packets)
    +           -------------------------------------------
    +              (total work CPU is capable of)
    +
    + +
  • Suppose I wanted to test an NFS server for livelock. +
    +  Run client with this loop:
    +    while(1){
    +      send NFS READ RPC;
    +      wait for response;
    +    }
    +
    +What would I see? Is the NFS server probably subject to livelock? +(No--offered load subject to feedback). + +
  • What other problems are we trying to address? +
      +
    • Increased latency for packet delivery and forwarding (e.g., start +disk head moving when first NFS read request comes) +
    • Transmit starvation +
    • User-level CPU starvation +
    + +
  • Why not tell the O/S scheduler to give interrupts lower priority? +Non-preemptible. +Could you fix this by making interrupts faster? (Maybe, if coupled +with some limit on input rate.) + +
  • Why not completely process each packet in the interrupt handler? +(I.e. forward it?) Other parts of kernel don't expect to run at high +interrupt-level (e.g., some packet processing code might invoke a function +that sleeps). Still might want an output queue + +
  • What about using polling instead of interrupts? Solves overload +problem, but killer for latency. + +
  • What's the paper's solution? +
      +
    • No IP input queue. +
    • Input processing and device input polling in kernel thread. +
    • Device receive interrupt just wakes up thread. And leaves +interrupts *disabled* for that device. +
    • Thread does all input processing, then re-enables interrupts. +
    +

    Why does this work? What happens when packets arrive too fast? +What happens when packets arrive slowly? + +

  • Explain Figure 6-3. +
      +
    • Why does "Polling (no quota)" work badly? (Input still starves +xmit complete processing.) +
    • Why does it immediately fall to zero, rather than gradually decreasing? +(xmit complete processing must be very cheap compared to input.) +
    + +
  • Explain Figure 6-4. +
      + +
    • Why does "Polling, no feedback" behave badly? There's a queue in +front of screend. We can still give 100% to input thread, 0% to +screend. + +
    • Why does "Polling w/ feedback" behave well? Input thread yields +when queue to screend fills. + +
    • What if screend hangs, what about other consumers of packets? +(e.g., can you ssh to machine to fix screend?) Fortunately screend +typically is only application. Also, re-enable input after timeout. + +
    + +
  • Why are the two solutions different? +
      +
    1. Polling thread with quotas. +
    2. Feedback from full queue. +
    +(I believe they should have used #2 for both.) + +
  • If we apply the proposed fixes, does the phenomemon totally go + away? (e.g. for web server, waits for disk, &c.) +
      +
    • Can the net device throw away packets without slowing down host? +
    • Problem: We want to drop packets for applications with big queues. +But requires work to determine which application a packet belongs to +Solution: NI-LRP (have network interface sort packets) +
    + +
  • What about latency question? (Look at figure 14 p. 243.) +
      +
    • 1st packet looks like an improvement over non-polling. But 2nd +packet transmitted later with poling. Why? (No new packets added to +xmit buffer until xmit interrupt) +
    • Why? In traditional BSD, to +amortize cost of poking device. Maybe better to poke a second time +anyway. +
    + +
  • What if processing has more complex structure? +
      +
    • Chain of processing stages with queues? Does feedback work? + What happens when a late stage is slow? +
    • Split at some point, multiple parallel paths? No so great; one + slow path blocks all paths. +
    + +
  • Can we formulate any general principles from paper? +
      +
    • Don't spend time on new work before completing existing work. +
    • Or give new work lower priority than partially-completed work. +
    + +
diff --git a/web/l-threads.html b/web/l-threads.html new file mode 100644 index 0000000..8587abb --- /dev/null +++ b/web/l-threads.html @@ -0,0 +1,316 @@ +L8 + + + + + +

Threads, processes, and context switching

+ +

Required reading: proc.c (focus on scheduler() and sched()), +setjmp.S, and sys_fork (in sysproc.c) + +

Overview

+ + +

Big picture: more programs than processors. How to share the +limited number of processors among the programs? + +

Observation: most programs don't need the processor continuously, +because they frequently have to wait for input (from user, disk, +network, etc.) + +

Idea: when one program must wait, it releases the processor, and +gives it to another program. + +

Mechanism: thread of computation, an active active computation. A +thread is an abstraction that contains the minimal state that is +necessary to stop an active and an resume it at some point later. +What that state is depends on the processor. On x86, it is the +processor registers (see setjmp.S). + +

Address spaces and threads: address spaces and threads are in +principle independent concepts. One can switch from one thread to +another thread in the same address space, or one can switch from one +thread to another thread in another address space. Example: in xv6, +one switches address spaces by switching segmentation registers (see +setupsegs). Does xv6 ever switch from one thread to another in the +same address space? (Answer: yes, v6 switches, for example, from the +scheduler, proc[0], to the kernel part of init, proc[1].) In the JOS +kernel we switch from the kernel thread to a user thread, but we don't +switch kernel space necessarily. + +

Process: one address space plus one or more threads of computation. +In xv6 all user programs contain one thread of computation and +one address space, and the concepts of address space and threads of +computation are not separated but bundled together in the concept of a +process. When switching from the kernel program (which has multiple +threads) to a user program, xv6 switches threads (switching from a +kernel stack to a user stack) and address spaces (the hardware uses +the kernel segment registers and the user segment registers). + +

xv6 supports the following operations on processes: +

    +
  • fork; create a new process, which is a copy of the parent. +
  • exec; execute a program +
  • exit: terminte process +
  • wait: wait for a process to terminate +
  • kill: kill process +
  • sbrk: grow the address space of a process. +
+This interfaces doesn't separate threads and address spaces. For +example, with this interface one cannot create additional threads in +the same threads. Modern Unixes provides additional primitives +(called pthreads, POSIX threads) to create additional threads in a +process and coordinate their activities. + +

Scheduling. The thread manager needs a method for deciding which +thread to run if multiple threads are runnable. The xv6 policy is to +run the processes round robin. Why round robin? What other methods +can you imagine? + +

Preemptive scheduling. To force a thread to release the processor +periodically (in case the thread never calls sleep), a thread manager +can use preemptive scheduling. The thread manager uses the clock chip +to generate periodically a hardware interrupt, which will cause +control to transfer to the thread manager, which then can decide to +run another thread (e.g., see trap.c). + +

xv6 code examples

+ +

Thread switching is implemented in xv6 using setjmp and longjmp, +which take a jumpbuf as an argument. setjmp saves its context in a +jumpbuf for later use by longjmp. longjmp restores the context saved +by the last setjmp. It then causes execution to continue as if the +call of setjmp has just returned 1. +

    +
  • setjmp saves: ebx, exc, edx, esi, edi, esp, ebp, and eip. +
  • longjmp restores them, and puts 1 in eax! +
+ +

Example of thread switching: proc[0] switches to scheduler: +

    +
  • 1359: proc[0] calls iget, which calls sleep, which calls sched. +
  • 2261: The stack before the call to setjmp in sched is: +
    +CPU 0:
    +eax: 0x10a144   1089860
    +ecx: 0x6c65746e 1818588270
    +edx: 0x0        0
    +ebx: 0x10a0e0   1089760
    +esp: 0x210ea8   2166440
    +ebp: 0x210ebc   2166460
    +esi: 0x107f20   1081120
    +edi: 0x107740   1079104
    +eip: 0x1023c9  
    +eflags 0x12      
    +cs:  0x8       
    +ss:  0x10      
    +ds:  0x10      
    +es:  0x10      
    +fs:  0x10      
    +gs:  0x10      
    +   00210ea8 [00210ea8]  10111e
    +   00210eac [00210eac]  210ebc
    +   00210eb0 [00210eb0]  10239e
    +   00210eb4 [00210eb4]  0001
    +   00210eb8 [00210eb8]  10a0e0
    +   00210ebc [00210ebc]  210edc
    +   00210ec0 [00210ec0]  1024ce
    +   00210ec4 [00210ec4]  1010101
    +   00210ec8 [00210ec8]  1010101
    +   00210ecc [00210ecc]  1010101
    +   00210ed0 [00210ed0]  107740
    +   00210ed4 [00210ed4]  0001
    +   00210ed8 [00210ed8]  10cd74
    +   00210edc [00210edc]  210f1c
    +   00210ee0 [00210ee0]  100bbc
    +   00210ee4 [00210ee4]  107740
    +
    +
  • 2517: stack at beginning of setjmp: +
    +CPU 0:
    +eax: 0x10a144   1089860
    +ecx: 0x6c65746e 1818588270
    +edx: 0x0        0
    +ebx: 0x10a0e0   1089760
    +esp: 0x210ea0   2166432
    +ebp: 0x210ebc   2166460
    +esi: 0x107f20   1081120
    +edi: 0x107740   1079104
    +eip: 0x102848  
    +eflags 0x12      
    +cs:  0x8       
    +ss:  0x10      
    +ds:  0x10      
    +es:  0x10      
    +fs:  0x10      
    +gs:  0x10      
    +   00210ea0 [00210ea0]  1023cf   <--- return address (sched)
    +   00210ea4 [00210ea4]  10a144
    +   00210ea8 [00210ea8]  10111e
    +   00210eac [00210eac]  210ebc
    +   00210eb0 [00210eb0]  10239e
    +   00210eb4 [00210eb4]  0001
    +   00210eb8 [00210eb8]  10a0e0
    +   00210ebc [00210ebc]  210edc
    +   00210ec0 [00210ec0]  1024ce
    +   00210ec4 [00210ec4]  1010101
    +   00210ec8 [00210ec8]  1010101
    +   00210ecc [00210ecc]  1010101
    +   00210ed0 [00210ed0]  107740
    +   00210ed4 [00210ed4]  0001
    +   00210ed8 [00210ed8]  10cd74
    +   00210edc [00210edc]  210f1c
    +
    +
  • 2519: What is saved in jmpbuf of proc[0]? +
  • 2529: return 0! +
  • 2534: What is in jmpbuf of cpu 0? The stack is as follows: +
    +CPU 0:
    +eax: 0x0        0
    +ecx: 0x6c65746e 1818588270
    +edx: 0x108aa4   1084068
    +ebx: 0x10a0e0   1089760
    +esp: 0x210ea0   2166432
    +ebp: 0x210ebc   2166460
    +esi: 0x107f20   1081120
    +edi: 0x107740   1079104
    +eip: 0x10286e  
    +eflags 0x46      
    +cs:  0x8       
    +ss:  0x10      
    +ds:  0x10      
    +es:  0x10      
    +fs:  0x10      
    +gs:  0x10      
    +   00210ea0 [00210ea0]  1023fe
    +   00210ea4 [00210ea4]  108aa4
    +   00210ea8 [00210ea8]  10111e
    +   00210eac [00210eac]  210ebc
    +   00210eb0 [00210eb0]  10239e
    +   00210eb4 [00210eb4]  0001
    +   00210eb8 [00210eb8]  10a0e0
    +   00210ebc [00210ebc]  210edc
    +   00210ec0 [00210ec0]  1024ce
    +   00210ec4 [00210ec4]  1010101
    +   00210ec8 [00210ec8]  1010101
    +   00210ecc [00210ecc]  1010101
    +   00210ed0 [00210ed0]  107740
    +   00210ed4 [00210ed4]  0001
    +   00210ed8 [00210ed8]  10cd74
    +   00210edc [00210edc]  210f1c
    +
    +
  • 2547: return 1! stack looks as follows: +
    +CPU 0:
    +eax: 0x1        1
    +ecx: 0x108aa0   1084064
    +edx: 0x108aa4   1084068
    +ebx: 0x10074    65652
    +esp: 0x108d40   1084736
    +ebp: 0x108d5c   1084764
    +esi: 0x10074    65652
    +edi: 0xffde     65502
    +eip: 0x102892  
    +eflags 0x6       
    +cs:  0x8       
    +ss:  0x10      
    +ds:  0x10      
    +es:  0x10      
    +fs:  0x10      
    +gs:  0x10      
    +   00108d40 [00108d40]  10231c
    +   00108d44 [00108d44]  10a144
    +   00108d48 [00108d48]  0010
    +   00108d4c [00108d4c]  0021
    +   00108d50 [00108d50]  0000
    +   00108d54 [00108d54]  0000
    +   00108d58 [00108d58]  10a0e0
    +   00108d5c [00108d5c]  0000
    +   00108d60 [00108d60]  0001
    +   00108d64 [00108d64]  0000
    +   00108d68 [00108d68]  0000
    +   00108d6c [00108d6c]  0000
    +   00108d70 [00108d70]  0000
    +   00108d74 [00108d74]  0000
    +   00108d78 [00108d78]  0000
    +   00108d7c [00108d7c]  0000
    +
    +
  • 2548: where will longjmp return? (answer: 10231c, in scheduler) +
  • 2233:Scheduler on each processor selects in a round-robin fashion the + first runnable process. Which process will that be? (If we are + running with one processor.) (Ans: proc[0].) +
  • 2229: what will be saved in cpu's jmpbuf? +
  • What is in proc[0]'s jmpbuf? +
  • 2548: return 1. Stack looks as follows: +
    +CPU 0:
    +eax: 0x1        1
    +ecx: 0x6c65746e 1818588270
    +edx: 0x0        0
    +ebx: 0x10a0e0   1089760
    +esp: 0x210ea0   2166432
    +ebp: 0x210ebc   2166460
    +esi: 0x107f20   1081120
    +edi: 0x107740   1079104
    +eip: 0x102892  
    +eflags 0x2       
    +cs:  0x8       
    +ss:  0x10      
    +ds:  0x10      
    +es:  0x10      
    +fs:  0x10      
    +gs:  0x10      
    +   00210ea0 [00210ea0]  1023cf   <--- return to sleep
    +   00210ea4 [00210ea4]  108aa4
    +   00210ea8 [00210ea8]  10111e
    +   00210eac [00210eac]  210ebc
    +   00210eb0 [00210eb0]  10239e
    +   00210eb4 [00210eb4]  0001
    +   00210eb8 [00210eb8]  10a0e0
    +   00210ebc [00210ebc]  210edc
    +   00210ec0 [00210ec0]  1024ce
    +   00210ec4 [00210ec4]  1010101
    +   00210ec8 [00210ec8]  1010101
    +   00210ecc [00210ecc]  1010101
    +   00210ed0 [00210ed0]  107740
    +   00210ed4 [00210ed4]  0001
    +   00210ed8 [00210ed8]  10cd74
    +   00210edc [00210edc]  210f1c
    +
    +
+ +

Why switch from proc[0] to the processor stack, and then to + proc[0]'s stack? Why not instead run the scheduler on the kernel + stack of the last process that run on that cpu? + +

    + +
  • If the scheduler wanted to use the process stack, then it couldn't + have any stack variables live across process scheduling, since + they'd be different depending on which process just stopped running. + +
  • Suppose process p goes to sleep on CPU1, so CPU1 is idling in + scheduler() on p's stack. Someone wakes up p. CPU2 decides to run + p. Now p is running on its stack, and CPU1 is also running on the + same stack. They will likely scribble on each others' local + variables, return pointers, etc. + +
  • The same thing happens if CPU1 tries to reuse the process's page +tables to avoid a TLB flush. If the process gets killed and cleaned +up by the other CPU, now the page tables are wrong. I think some OSes +actually do this (with appropriate ref counting). + +
+ +

How is preemptive scheduling implemented in xv6? Answer see trap.c + line 2905 through 2917, and the implementation of yield() on sheet + 22. + +

How long is a timeslice for a user process? (possibly very short; + very important lock is held across context switch!) + + + + + diff --git a/web/l-vm.html b/web/l-vm.html new file mode 100644 index 0000000..ffce13e --- /dev/null +++ b/web/l-vm.html @@ -0,0 +1,462 @@ + + +Virtual Machines + + + + +

Virtual Machines

+ +

Required reading: Disco

+ +

Overview

+ +

What is a virtual machine? IBM definition: a fully protected and +isolated copy of the underlying machine's hardware.

+ +

Another view is that it provides another example of a kernel API. +In contrast to other kernel APIs (unix, microkernel, and exokernel), +the virtual machine operating system exports as the kernel API the +processor API (e.g., the x86 interface). Thus, each program running +in user space sees the services offered by a processor, and each +program sees its own processor. Of course, we don't want to make a +system call for each instruction, and in fact one of the main +challenges in virtual machine operation systems is to design the +system in such a way that the physical processor executes the virtual +processor API directly, at processor speed. + +

+Virtual machines can be useful for a number of reasons: +

    + +
  1. Run multiple operating systems on single piece of hardware. For +example, in one process, you run Linux, and in another you run +Windows/XP. If the kernel API is identical to the x86 (and faithly +emulates x86 instructions, state, protection levels, page tables), +then Linux and Windows/XP, the virual machine operationg system can +run these guest operating systems without modifications. + +
      +
    • Run "older" programs on the same hardware (e.g., run one x86 +virtual machine in real mode to execute old DOS apps). + +
    • Or run applications that require different operating system. +
    + +
  2. Fault isolation: like processes on UNIX but more complete, because +the guest operating systems runs on the virtual machine in user space. +Thus, faults in the guest OS cannot effect any other software. + +
  3. Customizing the apparent hardware: virtual machine may have +different view of hardware than is physically present. + +
  4. Simplify deployment/development of software for scalable +processors (e.g., Disco). + +
+

+ +

If your operating system isn't a virtual machine operating system, +what are the alternatives? Processor simulation (e.g., bochs) or +binary emulation (WINE). Simulation runs instructions purely in +software and is slow (e.g., 100x slow down for bochs); virtualization +gets out of the way whenever possible and can be efficient. + +

Simulation gives portability whereas virtualization focuses on +performance. However, this means that you need to model your hardware +very carefully in software. Binary emulation focuses on just getting +system call for a particular operating system's interface. Binary +emulation can be hard because it is targetted towards a particular +operating system (and even that can change between revisions). +

+ +

To provide each process with its own virtual processor that exports +the same API as the physical processor, what features must +the virtual machine operating system virtualize? +

    +
  1. CPU: instructions -- trap all privileged instructions
  2. +
  3. Memory: address spaces -- map "physical" pages managed +by the guest OS to machinepages, handle translation, etc.
  4. +
  5. Devices: any I/O communication needs to be trapped and passed + through/handled appropriately.
  6. +
+

+The software that implements the virtualization is typically called +the monitor, instead of the virtual machine operating system. + +

Virtual machine monitors (VMM) can be implemented in two ways: +

    +
  1. Run VMM directly on hardware: like Disco.
  2. +
  3. Run VMM as an application (though still running as root, with + integration into OS) on top of a host OS: like VMware. Provides + additional hardware support at low development cost in + VMM. Intercept CPU-level I/O requests and translate them into + system calls (e.g. read()).
  4. +
+

+ +

The three primary functions of a virtual machine monitor are: +

    +
  • virtualize processor (CPU, memory, and devices) +
  • dispatch events (e.g., forward page fault trap to guest OS). +
  • allocate resources (e.g., divide real memory in some way between +the physical memory of each guest OS). +
+ +

Virtualization in detail

+ +

Memory virtualization

+ +

+Understanding memory virtualization. Let's consider the MIPS example +from the paper. Ideally, we'd be able to intercept and rewrite all +memory address references. (e.g., by intercepting virtual memory +calls). Why can't we do this on the MIPS? (There are addresses that +don't go through address translation --- but we don't want the virtual +machine to directly access memory!) What does Disco do to get around +this problem? (Relink the kernel outside this address space.) +

+ +

+Having gotten around that problem, how do we handle things in general? +

+
+// Disco's tlb miss handler.
+// Called when a memory reference for virtual adddress
+// 'VA' is made, but there is not VA->MA (virtual -> machine)
+// mapping in the cpu's TLB.
+void tlb_miss_handler (VA)
+{
+  // see if we have a mapping in our "shadow" tlb (which includes
+  // "main" tlb)
+  tlb_entry *t = tlb_lookup (thiscpu->l2tlb, va);
+  if (t && defined (thiscpu->pmap[t->pa]))   // is there a MA for this PA?
+    tlbwrite (va, thiscpu->pmap[t->pa], t->otherdata);
+  else if (t)
+    // get a machine page, copy physical page into, and tlbwrite
+  else
+    // trap to the virtual CPU/OS's handler
+}
+
+// Disco's procedure which emulates the MIPS
+// instruction which writes to the tlb.
+//
+// VA -- virtual addresss
+// PA -- physical address (NOT MA machine address!)
+// otherdata -- perms and stuff
+void emulate_tlbwrite_instruction (VA, PA, otherdata)
+{
+  tlb_insert (thiscpu->l2tlb, VA, PA, otherdata); // cache
+  if (!defined (thiscpu->pmap[PA])) { // fill in pmap dynamically
+    MA = allocate_machine_page ();
+    thiscpu->pmap[PA] = MA; // See 4.2.2
+    thiscpu->pmapbackmap[MA] = PA;
+    thiscpu->memmap[MA] = VA; // See 4.2.3 (for TLB shootdowns)
+  }
+  tlbwrite (va, thiscpu->pmap[PA], otherdata);
+}
+
+// Disco's procedure which emulates the MIPS
+// instruction which read the tlb.
+tlb_entry *emulate_tlbread_instruction (VA)
+{
+  // Must return a TLB entry that has a "Physical" address;
+  // This is recorded in our secondary TLB cache.
+  // (We don't have to read from the hardware TLB since
+  // all writes to the hardware TLB are mediated by Disco.
+  // Thus we can always keep the l2tlb up to date.)
+  return tlb_lookup (thiscpu->l2tlb, va);
+}
+
+ +

CPU virtualization

+ +

Requirements: +

    +
  1. Results of executing non-privileged instructions in privileged and + user mode must be equivalent. (Why? B/c the virtual "privileged" + system will not be running in true "privileged" mode.) +
  2. There must be a way to protect the VM from the real machine. (Some + sort of memory protection/address translation. For fault isolation.)
  3. +
  4. There must be a way to detect and transfer control to the VMM when + the VM tries to execute a sensitive instruction (e.g. a privileged + instruction, or one that could expose the "virtualness" of the + VM.) It must be possible to emulate these instructions in + software. Can be classified into completely virtualizable + (i.e. there are protection mechanisms that cause traps for all + instructions), partly (insufficient or incomplete trap + mechanisms), or not at all (e.g. no MMU). +
+

+ +

The MIPS didn't quite meet the second criteria, as discussed +above. But, it does have a supervisor mode that is between user mode and +kernel mode where any privileged instruction will trap.

+ +

What might a the VMM trap handler look like?

+
+void privilege_trap_handler (addr) {
+  instruction, args = decode_instruction (addr)
+  switch (instruction) {
+  case foo:
+    emulate_foo (thiscpu, args, ...);
+    break;
+  case bar:
+    emulate_bar (thiscpu, args, ...);
+    break;
+  case ...:
+    ...
+  }
+}
+
+

The emulator_foo bits will have to evaluate the +state of the virtual CPU and compute the appropriate "fake" answer. +

+ +

What sort of state is needed in order to appropriately emulate all +of these things? +

+- all user registers
+- CPU specific regs (e.g. on x86, %crN, debugging, FP...)
+- page tables (or tlb)
+- interrupt tables
+
+This is needed for each virtual processor. +

+ +

Device I/O virtualization

+ +

We intercept all communication to the I/O devices: read/writes to +reserved memory addresses cause page faults into special handlers +which will emulate or pass through I/O as appropriate. +

+ +

+In a system like Disco, the sequence would look something like: +

    +
  1. VM executes instruction to access I/O
  2. +
  3. Trap generated by CPU (based on memory or privilege protection) + transfers control to VMM.
  4. +
  5. VMM emulates I/O instruction, saving information about where this + came from (for demultiplexing async reply from hardware later) .
  6. +
  7. VMM reschedules a VM.
  8. +
+

+ +

+Interrupts will require some additional work: +

    +
  1. Interrupt occurs on real machine, transfering control to VMM + handler.
  2. +
  3. VMM determines the VM that ought to receive this interrupt.
  4. +
  5. VMM causes a simulated interrupt to occur in the VM, and reschedules a + VM.
  6. +
  7. VM runs its interrupt handler, which may involve other I/O + instructions that need to be trapped.
  8. +
+

+ +

+The above can be slow! So sometimes you want the guest operating +system to be aware that it is a guest and allow it to avoid the slow +path. Special device drivers or changing instructions that would cause +traps into memory read/write instructions. +

+ +

Intel x86/vmware

+ +

VMware, unlike Disco, runs as an application on a guest OS and +cannot modify the guest OS. Furthermore, it must virtualize the x86 +instead of MIPS processor. Both of these differences make good design +challenges. + +

The first challenge is that the monitor runs in user space, yet it +must dispatch traps and it must execute privilege instructions, which +both require kernel privileges. To address this challenge, the +monitor downloads a piece of code, a kernel module, into the guest +OS. Most modern operating systems are constructed as a core kernel, +extended with downloadable kernel modules. +Privileged users can insert kernel modules at run-time. + +

The monitor downloads a kernel module that reads the IDT, copies +it, and overwrites the hard-wired entries with addresses for stubs in +the just downloaded kernel module. When a trap happens, the kernel +module inspects the PC, and either forwards the trap to the monitor +running in user space or to the guest OS. If the trap is caused +because a guest OS execute a privileged instructions, the monitor can +emulate that privilege instruction by asking the kernel module to +perform that instructions (perhaps after modifying the arguments to +the instruction). + +

The second challenge is virtualizing the x86 + instructions. Unfortunately, x86 doesn't meet the 3 requirements for + CPU virtualization. the first two requirements above. If you run + the CPU in ring 3, most x86 instructions will be fine, + because most privileged instructions will result in a trap, which + can then be forwarded to vmware for emulation. For example, + consider a guest OS loading the root of a page table in CR3. This + results in trap (the guest OS runs in user space), which is + forwarded to the monitor, which can emulate the load to CR3 as + follows: + +

+// addr is a physical address
+void emulate_lcr3 (thiscpu, addr)
+{
+  thiscpu->cr3 = addr;
+  Pte *fakepdir = lookup (addr, oldcr3cache);
+  if (!fakepdir) {
+    fakedir = ppage_alloc ();
+    store (oldcr3cache, addr, fakedir);
+    // May wish to scan through supplied page directory to see if
+    // we have to fix up anything in particular.
+    // Exact settings will depend on how we want to handle
+    // problem cases below and our own MM.
+  }
+  asm ("movl fakepdir,%cr3");
+  // Must make sure our page fault handler is in sync with what we do here.
+}
+
+ +

To virtualize the x86, the monitor must intercept any modifications +to the page table and substitute appropriate responses. And update +things like the accessed/dirty bits. The monitor can arrange for this +to happen by making all page table pages inaccessible so that it can +emulate loads and stores to page table pages. This setup allow the +monitor to virtualize the memory interface of the x86.

+ +

Unfortunately, not all instructions that must be virtualized result +in traps: +

    +
  • pushf/popf: FL_IF is handled different, + for example. In user-mode setting FL_IF is just ignored.
  • +
  • Anything (push, pop, mov) + that reads or writes from %cs, which contains the + privilege level. +
  • Setting the interrupt enable bit in EFLAGS has different +semantics in user space and kernel space. In user space, it +is ignored; in kernel space, the bit is set. +
  • And some others... (total, 17 instructions). +
+These instructions are unpriviliged instructions (i.e., don't cause a +trap when executed by a guest OS) but expose physical processor state. +These could reveal details of virtualization that should not be +revealed. For example, if guest OS sets the interrupt enable bit for +its virtual x86, the virtualized EFLAGS should reflect that the bit is +set, even though the guest OS is running in user space. + +

How can we virtualize these instructions? An approach is to decode +the instruction stream that is provided by the user and look for bad +instructions. When we find them, replace them with an interrupt +(INT 3) that will allow the VMM to handle it +correctly. This might look something like: +

+ +
+void initcode () {
+  scan_for_nonvirtual (0x7c00);
+}
+
+void scan_for_nonvirtualizable (thiscpu, startaddr) {
+  addr  = startaddr;
+  instr = disassemble (addr);
+  while (instr is not branch or bad) {
+    addr += len (instr);
+    instr = disassemble (addr);
+  }
+  // remember that we wanted to execute this instruction.
+  replace (addr, "int 3");
+  record (thiscpu->rewrites, addr, instr);
+}
+
+void breakpoint_handler (tf) {
+  oldinstr = lookup (thiscpu->rewrites, tf->eip);
+  if (oldinstr is branch) {
+    newcs:neweip = evaluate branch
+    scan_for_nonvirtualizable (thiscpu, newcs:neweip)
+    return;
+  } else { // something non virtualizable
+    // dispatch to appropriate emulation
+  }
+}
+
+

All pages must be scanned in this way. Fortunately, most pages +probably are okay and don't really need any special handling so after +scanning them once, we can just remember that the page is okay and let +it run natively. +

+ +

What if a guest OS generates instructions, writes them to memory, +and then wants to execute them? We must detect self-modifying code +(e.g. must simulate buffer overflow attacks correctly.) When a write +to a physical page that happens to be in code segment happens, must +trap the write and then rescan the affected portions of the page.

+ +

What about self-examining code? Need to protect it some +how---possibly by playing tricks with instruction/data TLB caches, or +introducing a private segment for code (%cs) that is different than +the segment used for reads/writes (%ds). +

+ +

Some Disco paper notes

+ +

+Disco has some I/O specific optimizations. +

+
    +
  • Disk reads only need to happen once and can be shared between + virtual machines via copy-on-write virtual memory tricks.
  • +
  • Network cards do not need to be fully virtualized --- intra + VM communication doesn't need a real network card backing it.
  • +
  • Special handling for NFS so that all VMs "share" a buffer cache.
  • +
+ +

+Disco developers clearly had access to IRIX source code. +

+
    +
  • Need to deal with KSEG0 segment of MIPS memory by relinking kernel + at different address space.
  • +
  • Ensuring page-alignment of network writes (for the purposes of + doing memory map tricks.)
  • +
+ +

Performance?

+
    +
  • Evaluated in simulation.
  • +
  • Where are the overheads? Where do they come from?
  • +
  • Does it run better than NUMA IRIX?
  • +
+ +

Premise. Are virtual machine the preferred approach to extending +operating systems? Have scalable multiprocessors materialized?

+ +

Related papers

+ +

John Scott Robin, Cynthia E. Irvine. Analysis of the +Intel Pentium's Ability to Support a Secure Virtual Machine +Monitor.

+ +

Jeremy Sugerman, Ganesh Venkitachalam, Beng-Hong Lim. Virtualizing +I/O Devices on VMware Workstation's Hosted Virtual Machine +Monitor. In Proceedings of the 2001 Usenix Technical Conference.

+ +

Kevin Lawton, Drew Northup. Plex86 Virtual +Machine.

+ +

Xen +and the Art of Virtualization, Paul Barham, Boris +Dragovic, Keir Fraser, Steven Hand, Tim Harris, Alex Ho, Rolf +Neugebauer, Ian Pratt, Andrew Warfield, SOSP 2003

+ +

A comparison of +software and hardware techniques for x86 virtualizatonKeith Adams +and Ole Agesen, ASPLOS 2006

+ + + + + diff --git a/web/l-xfi.html b/web/l-xfi.html new file mode 100644 index 0000000..41ba434 --- /dev/null +++ b/web/l-xfi.html @@ -0,0 +1,246 @@ + + +XFI + + + +

XFI

+ +

Required reading: XFI: software guards for system address spaces. + +

Introduction

+ +

Problem: how to use untrusted code (an "extension") in a trusted +program? +

    +
  • Use untrusted jpeg codec in Web browser +
  • Use an untrusted driver in the kernel +
+ +

What are the dangers? +

    +
  • No fault isolations: extension modifies trusted code unintentionally +
  • No protection: extension causes a security hole +
      +
    • Extension has a buffer overrun problem +
    • Extension calls trusted program's functions +
    • Extensions calls a trusted program's functions that is allowed to + call, but supplies "bad" arguments +
    • Extensions calls privileged hardware instructions (when extending + kernel) +
    • Extensions reads data out of trusted program it shouldn't. +
    +
+ +

Possible solutions approaches: +

    + +
  • Run extension in its own address space with minimal + privileges. Rely on hardware and operating system protection + mechanism. + +
  • Restrict the language in which the extension is written: +
      + +
    • Packet filter language. Language is limited in its capabilities, + and it easy to guarantee "safe" execution. + +
    • Type-safe language. Language runtime and compiler guarantee "safe" +execution. +
    + +
  • Software-based sandboxing. + +
+ +

Software-based sandboxing

+ +

Sandboxer. A compiler or binary-rewriter sandboxes all unsafe + instructions in an extension by inserting additional instructions. + For example, every indirect store is preceded by a few instructions + that compute and check the target of the store at runtime. + +

Verifier. When the extension is loaded in the trusted program, the + verifier checks if the extension is appropriately sandboxed (e.g., + are all indirect stores sandboxed? does it call any privileged + instructions?). If not, the extension is rejected. If yes, the + extension is loaded, and can run. If the extension runs, the + instruction that sandbox unsafe instructions check if the unsafe + instruction is used in a safe way. + +

The verifier must be trusted, but the sandboxer doesn't. We can do + without the verifier, if the trusted program can establish that the + extension has been sandboxed by a trusted sandboxer. + +

The paper refers to this setup as instance of proof-carrying code. + +

Software fault isolation

+ +

SFI +by Wahbe et al. explored out to use sandboxing for fault isolation +extensions; that is, use sandboxing to control that stores and jump +stay within a specified memory range (i.e., they don't overwrite and +jump into addresses in the trusted program unchecked). They +implemented SFI for a RISC processor, which simplify things since +memory can be written only by store instructions (other instructions +modify registers). In addition, they assumed that there were plenty +of registers, so that they can dedicate a few for sandboxing code. + +

The extension is loaded into a specific range (called a segment) + within the trusted application's address space. The segment is + identified by the upper bits of the addresses in the + segment. Separate code and data segments are necessary to prevent an + extension overwriting its code. + +

An unsafe instruction on the MIPS is an instruction that jumps or + stores to an address that cannot be statically verified to be within + the correct segment. Most control transfer operations, such + program-counter relative can be statically verified. Stores to + static variables often use an immediate addressing mode and can be + statically verified. Indirect jumps and indirect stores are unsafe. + +

To sandbox those instructions the sandboxer could generate the + following code for each unsafe instruction: +

+  DR0 <- target address
+  R0 <- DR0 >> shift-register;  // load in R0 segment id of target
+  CMP R0, segment-register;     // compare to segment id to segment's ID
+  BNE fault-isolation-error     // if not equal, branch to trusted error code
+  STORE using DR0
+
+In this code, DR0, shift-register, and segment register +are dedicated: they cannot be used by the extension code. The +verifier must check if the extension doesn't use they registers. R0 +is a scratch register, but doesn't have to be dedicated. The +dedicated registers are necessary, because otherwise extension could +load DR0 and jump to the STORE instruction directly, skipping the +check. + +

This implementation costs 4 registers, and 4 additional instructions + for each unsafe instruction. One could do better, however: +

+  DR0 <- target address & and-mask-register // mask segment ID from target
+  DR0 <- DR0 | segment register // insert this segment's ID
+  STORE using DR0
+
+This code just sets the write segment ID bits. It doesn't catch +illegal addresses; it just ensures that illegal addresses are within +the segment, harming the extension but no other code. Even if the +extension jumps to the second instruction of this sandbox sequence, +nothing bad will happen (because DR0 will already contain the correct +segment ID). + +

Optimizations include: +

    +
  • use guard zones for store value, offset(reg) +
  • treat SP as dedicated register (sandbox code that initializes it) +
  • etc. +
+ +

XFI

+ +

XFI extends SFI in several ways: +

    +
  • Handles fault isolation and protection +
  • Uses control-folow integrity (CFI) to get good performance +
  • Doesn't use dedicated registers +
  • Use two stacks (a scoped stack and an allocation stack) and only + allocation stack can be corrupted by buffer-overrun attacks. The + scoped stack cannot via computed memory references. +
  • Uses a binary rewriter. +
  • Works for the x86 +
+ +

x86 is challenging, because limited registers and variable length + of instructions. SFI technique won't work with x86 instruction + set. For example if the binary contains: +

+  25 CD 80 00 00   # AND eax, 0x80CD
+
+and an adversary can arrange to jump to the second byte, then the +adversary calls system call on Linux, which has binary the binary +representation CD 80. Thus, XFI must control execution flow. + +

XFI policy goals: +

    +
  • Memory-access constraints (like SFI) +
  • Interface restrictions (extension has fixed entry and exit points) +
  • Scoped-stack integrity (calling stack is well formed) +
  • Simplified instructions semantics (remove dangerous instructions) +
  • System-environment integrity (ensure certain machine model + invariants, such as x86 flags register cannot be modified) +
  • Control-flow integrity: execution must follow a static, expected + control-flow graph. (enter at beginning of basic blocks) +
  • Program-data integrity (certain global variables in extension + cannot be accessed via computed memory addresses) +
+ +

The binary rewriter inserts guards to ensure these properties. The + verifier check if the appropriate guards in place. The primary + mechanisms used are: +

    +
  • CFI guards on computed control-flow transfers (see figure 2) +
  • Two stacks +
  • Guards on computer memory accesses (see figure 3) +
  • Module header has a section that contain access permissions for + region +
  • Binary rewriter, which performs intra-procedure analysis, and + generates guards, code for stack use, and verification hints +
  • Verifier checks specific conditions per basic block. hints specify + the verification state for the entry to each basic block, and at + exit of basic block the verifier checks that the final state implies + the verification state at entry to all possible successor basic + blocks. (see figure 4) +
+ +

Can XFI protect against the attack discussed in last lecture? +

+  unsigned int j;
+  p=(unsigned char *)s->init_buf->data;
+  j= *(p++);
+  s->session->session_id_length=j;
+  memcpy(s->session->session_id,p,j);
+
+Where will j be located? + +

How about the following one from the paper Beyond stack smashing: + recent advances in exploiting buffer overruns? +

+void f2b(void * arg, size_t len) {
+  char buf[100];
+  long val = ..;
+  long *ptr = ..;
+  extern void (*f)();
+  
+  memcopy(buff, arg, len);
+  *ptr = val;
+  f();
+  ...
+  return;
+}
+
+What code can (*f)() call? Code that the attacker inserted? +Code in libc? + +

How about an attack that use ptr in the above code to + overwrite a method's address in a class's dispatch table with an + address of support function? + +

How about data-only attacks? For example, attacker + overwrites pw_uid in the heap with 0 before the following + code executes (when downloading /etc/passwd and then uploading it with a + modified entry). +

+FILE *getdatasock( ... ) {
+  seteuid(0);
+  setsockeope ( ...);
+  ...
+  seteuid(pw->pw_uid);
+  ...
+}
+
+ +

How much does XFI slow down applications? How many more + instructions are executed? (see Tables 1-4) + + diff --git a/web/l1.html b/web/l1.html new file mode 100644 index 0000000..9865601 --- /dev/null +++ b/web/l1.html @@ -0,0 +1,288 @@ +L1 + + + + + +

OS overview

+ +

Overview

+ +
    +
  • Goal of course: + +
      +
    • Understand operating systems in detail by designing and +implementing miminal OS +
    • Hands-on experience with building systems ("Applying 6.033") +
    + +
  • What is an operating system? +
      +
    • a piece of software that turns the hardware into something useful +
    • layered picture: hardware, OS, applications +
    • Three main functions: fault isolate applications, abstract hardware, +manage hardware +
    + +
  • Examples: +
      +
    • OS-X, Windows, Linux, *BSD, ... (desktop, server) +
    • PalmOS Windows/CE (PDA) +
    • Symbian, JavaOS (Cell phones) +
    • VxWorks, pSOS (real-time) +
    • ... +
    + +
  • OS Abstractions +
      +
    • processes: fork, wait, exec, exit, kill, getpid, brk, nice, sleep, +trace +
    • files: open, close, read, write, lseek, stat, sync +
    • directories: mkdir, rmdir, link, unlink, mount, umount +
    • users + security: chown, chmod, getuid, setuid +
    • interprocess communication: signals, pipe +
    • networking: socket, accept, snd, recv, connect +
    • time: gettimeofday +
    • terminal: +
    + +
  • Sample Unix System calls (mostly POSIX) +
      +
    • int read(int fd, void*, int) +
    • int write(int fd, void*, int) +
    • off_t lseek(int fd, off_t, int [012]) +
    • int close(int fd) +
    • int fsync(int fd) +
    • int open(const char*, int flags [, int mode]) +
        +
      • O_RDONLY, O_WRONLY, O_RDWR, O_CREAT +
      +
    • mode_t umask(mode_t cmask) +
    • int mkdir(char *path, mode_t mode); +
    • DIR *opendir(char *dirname) +
    • struct dirent *readdir(DIR *dirp) +
    • int closedir(DIR *dirp) +
    • int chdir(char *path) +
    • int link(char *existing, char *new) +
    • int unlink(char *path) +
    • int rename(const char*, const char*) +
    • int rmdir(char *path) +
    • int stat(char *path, struct stat *buf) +
    • int mknod(char *path, mode_t mode, dev_t dev) +
    • int fork() +
        +
      • returns childPID in parent, 0 in child; only + difference +
      +
    • int getpid() +
    • int waitpid(int pid, int* stat, int opt) +
        +
      • pid==-1: any; opt==0||WNOHANG +
      • returns pid or error +
      +
    • void _exit(int status) +
    • int kill(int pid, int signal) +
    • int sigaction(int sig, struct sigaction *, struct sigaction *) +
    • int sleep (int sec) +
    • int execve(char* prog, char** argv, char** envp) +
    • void *sbrk(int incr) +
    • int dup2(int oldfd, int newfd) +
    • int fcntl(int fd, F_SETFD, int val) +
    • int pipe(int fds[2]) +
        +
      • writes on fds[1] will be read on fds[0] +
      • when last fds[1] closed, read fds[0] retursn EOF +
      • when last fds[0] closed, write fds[1] kills SIGPIPE/fails + EPIPE +
      +
    • int fchown(int fd, uind_t owner, gid_t group) +
    • int fchmod(int fd, mode_t mode) +
    • int socket(int domain, int type, int protocol) +
    • int accept(int socket_fd, struct sockaddr*, int* namelen) +
        +
      • returns new fd +
      +
    • int listen(int fd, int backlog) +
    • int connect(int fd, const struct sockaddr*, int namelen) +
    • void* mmap(void* addr, size_t len, int prot, int flags, int fd, + off_t offset) +
    • int munmap(void* addr, size_t len) +
    • int gettimeofday(struct timeval*) +
    +
+ +

See the reference page for links to +the early Unix papers. + +

Class structure

+ +
    +
  • Lab: minimal OS for x86 in an exokernel style (50%) +
      +
    • kernel interface: hardware + protection +
    • libOS implements fork, exec, pipe, ... +
    • applications: file system, shell, .. +
    • development environment: gcc, bochs +
    • lab 1 is out +
    + +
  • Lecture structure (20%) +
      +
    • homework +
    • 45min lecture +
    • 45min case study +
    + +
  • Two quizzes (30%) +
      +
    • mid-term +
    • final's exam week +
    + +
+ +

Case study: the shell (simplified)

+ +
    +
  • interactive command execution and a programming language +
  • Nice example that uses various OS abstractions. See Unix +paper if you are unfamiliar with the shell. +
  • Final lab is a simple shell. +
  • Basic structure: +
    +      
    +       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");
    +            }
    +        }
    +
    +

    The split of creating a process with a new program in fork and exec +is mostly a historical accident. See the assigned paper for today. +

  • Example: +
    +        $ ls
    +
    +
  • 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.) +
  • I/O: file descriptors. Child inherits open file descriptors +from parent. By convention: +
      +
    • file descriptor 0 for input (e.g., keyboard). read_command: +
      +     read (1, buf, bufsize)
      +
      +
    • file descriptor 1 for output (e.g., terminal) +
      +     write (1, "hello\n", strlen("hello\n")+1)
      +
      +
    • file descriptor 2 for error (e.g., terminal) +
    +
  • How does the shell implement: +
    +     $ls > tmp1
    +
    +just before exec insert: +
    +    	   close (1);
    +	   fd = open ("tmp1", O_CREAT|O_WRONLY);   // fd will be 1!
    +
    +

    The kernel will return the first free file descriptor, 1 in this case. +

  • How does the shell implement sharing an output file: +
    +     $ls 2> tmp1 > tmp1
    +
    +replace last code with: +
    +
    +	   close (1);
    +	   close (2);
    +	   fd1 = open ("tmp1", O_CREAT|O_WRONLY);   // fd will be 1!
    +	   fd2 = dup (fd1);
    +
    +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 -9
    +
    +
  • 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 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);
    +        }
    +
    +
  • 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. +
  • 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: +
    	    
    +	    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");
    +            }
    +
    +
  • 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 in the shell lecture later in the term. + + + + diff --git a/web/l13.html b/web/l13.html new file mode 100644 index 0000000..af0f405 --- /dev/null +++ b/web/l13.html @@ -0,0 +1,245 @@ +High-performance File Systems + + + + + +

    High-performance File Systems

    + +

    Required reading: soft updates. + +

    Overview

    + +

    A key problem in designing file systems is how to obtain +performance on file system operations while providing consistency. +With consistency, we mean, that file system invariants are maintained +is on disk. These invariants include that if a file is created, it +appears in its directory, etc. If the file system data structures are +consistent, then it is possible to rebuild the file system to a +correct state after a failure. + +

    To ensure consistency of on-disk file system data structures, + modifications to the file system must respect certain rules: +

      + +
    • Never point to a structure before it is initialized. An inode must +be initialized before a directory entry references it. An block must +be initialized before an inode references it. + +
    • Never reuse a structure before nullifying all pointers to it. An +inode pointer to a disk block must be reset before the file system can +reallocate the disk block. + +
    • Never reset the last point to a live structure before a new +pointer is set. When renaming a file, the file system should not +remove the old name for an inode until after the new name has been +written. +
    +The paper calls these dependencies update dependencies. + +

    xv6 ensures these rules by writing every block synchronously, and + by ordering the writes appropriately. With synchronous, we mean + that a process waits until the current disk write has been + completed before continuing with execution. + +

      + +
    • What happens if power fails after 4776 in mknod1? Did we lose the + inode for ever? No, we have a separate program (called fsck), which + can rebuild the disk structures correctly and can mark the inode on + the free list. + +
    • Does the order of writes in mknod1 matter? Say, what if we wrote + directory entry first and then wrote the allocated inode to disk? + This violates the update rules and it is not a good plan. If a + failure happens after the directory write, then on recovery we have + an directory pointing to an unallocated inode, which now may be + allocated by another process for another file! + +
    • Can we turn the writes (i.e., the ones invoked by iupdate and + wdir) into delayed writes without creating problems? No, because + the cause might write them back to the disk in an incorrect order. + It has no information to decide in what order to write them. + +
    + +

    xv6 is a nice example of the tension between consistency and + performance. To get consistency, xv6 uses synchronous writes, + but these writes are slow, because they perform at the rate of a + seek instead of the rate of the maximum data transfer rate. The + bandwidth to a disk is reasonable high for large transfer (around + 50Mbyte/s), but latency is low, because of the cost of moving the + disk arm(s) (the seek latency is about 10msec). + +

    This tension is an implementation-dependent one. The Unix API + doesn't require that writes are synchronous. Updates don't have to + appear on disk until a sync, fsync, or open with O_SYNC. Thus, in + principle, the UNIX API allows delayed writes, which are good for + performance: +

      +
    • Batch many writes together in a big one, written at the disk data + rate. +
    • Absorp writes to the same block. +
    • Schedule writes to avoid seeks. +
    + +

    Thus the question: how to delay writes and achieve consistency? + The paper provides an answer. + +

    This paper

    + +

    The paper surveys some of the existing techniques and introduces a +new to achieve the goal of performance and consistency. + +

    + +

    Techniques possible: +

      + +
    • Equip system with NVRAM, and put buffer cache in NVRAM. + +
    • Logging. Often used in UNIX file systems for metadata updates. +LFS is an extreme version of this strategy. + +
    • Flusher-enforced ordering. All writes are delayed. This flusher +is aware of dependencies between blocks, but doesn't work because +circular dependencies need to be broken by writing blocks out. + +
    + +

    Soft updates is the solution explored in this paper. It doesn't +require NVRAM, and performs as well as the naive strategy of keep all +dirty block in main memory. Compared to logging, it is unclear if +soft updates is better. The default BSD file systems uses soft + updates, but most Linux file systems use logging. + +

    Soft updates is a sophisticated variant of flusher-enforced +ordering. Instead of maintaining dependencies on the block-level, it +maintains dependencies on file structure level (per inode, per +directory, etc.), reducing circular dependencies. Furthermore, it +breaks any remaining circular dependencies by undo changes before +writing the block and then redoing them to the block after writing. + +

    Pseudocode for create: +

    +create (f) {
    +   allocate inode in block i  (assuming inode is available)
    +   add i to directory data block d  (assuming d has space)
    +   mark d has dependent on i, and create undo/redo record
    +   update directory inode in block di
    +   mark di has dependent on d
    +}
    +
    + +

    Pseudocode for the flusher: +

    +flushblock (b)
    +{
    +  lock b;
    +  for all dependencies that b is relying on
    +    "remove" that dependency by undoing the change to b
    +    mark the dependency as "unrolled"
    +  write b 
    +}
    +
    +write_completed (b) {
    +  remove dependencies that depend on b
    +  reapply "unrolled" dependencies that b depended on
    +  unlock b
    +}
    +
    + +

    Apply flush algorithm to example: +

      +
    • A list of two dependencies: directory->inode, inode->directory. +
    • Lets say syncer picks directory first +
    • Undo directory->inode changes (i.e., unroll ) +
    • Write directory block +
    • Remove met dependencies (i.e., remove inode->directory dependency) +
    • Perform redo operation (i.e., redo ) +
    • Select inode block and write it +
    • Remove met dependencies (i.e., remove directory->inode dependency) +
    • Select directory block (it is dirty again!) +
    • Write it. +
    + +

    An file operation that is important for file-system consistency +is rename. Rename conceptually works as follows: +

    +rename (from, to)
    +   unlink (to);
    +   link (from, to);
    +   unlink (from);
    +
    + +

    Rename it often used by programs to make a new version of a file +the current version. Committing to a new version must happen +atomically. Unfortunately, without a transaction-like support +atomicity is impossible to guarantee, so a typical file systems +provides weaker semantics for rename: if to already exists, an +instance of to will always exist, even if the system should crash in +the middle of the operation. Does the above implementation of rename +guarantee this semantics? (Answer: no). + +

    If rename is implemented as unlink, link, unlink, then it is +difficult to guarantee even the weak semantics. Modern UNIXes provide +rename as a file system call: +

    +   update dir block for to point to from's inode // write block
    +   update dir block for from to free entry // write block
    +
    +

    fsck may need to correct refcounts in the inode if the file +system fails during rename. for example, a crash after the first +write followed by fsck should set refcount to 2, since both from +and to are pointing at the inode. + +

    This semantics is sufficient, however, for an application to ensure +atomicity. Before the call, there is a from and perhaps a to. If the +call is successful, following the call there is only a to. If there +is a crash, there may be both a from and a to, in which case the +caller knows the previous attempt failed, and must retry. The +subtlety is that if you now follow the two links, the "to" name may +link to either the old file or the new file. If it links to the new +file, that means that there was a crash and you just detected that the +rename operation was composite. On the other hand, the retry +procedure can be the same for either case (do the rename again), so it +isn't necessary to discover how it failed. The function follows the +golden rule of recoverability, and it is idempotent, so it lays all +the needed groundwork for use as part of a true atomic action. + +

    With soft updates renames becomes: +

    +rename (from, to) {
    +   i = namei(from);
    +   add "to" directory data block td a reference to inode i
    +   mark td dependent on block i
    +   update directory inode "to" tdi
    +   mark tdi as dependent on td
    +   remove "from" directory data block fd a reference to inode i
    +   mark fd as dependent on tdi
    +   update directory inode in block fdi
    +   mark fdi as dependent on fd
    +}
    +
    +

    No synchronous writes! + +

    What needs to be done on recovery? (Inspect every statement in +rename and see what inconsistencies could exist on the disk; e.g., +refcnt inode could be too high.) None of these inconsitencies require +fixing before the file system can operate; they can be fixed by a +background file system repairer. + +

    Paper discussion

    + +

    Do soft updates perform any useless writes? (A useless write is a +write that will be immediately overwritten.) (Answer: yes.) Fix +syncer to becareful with what block to start. Fix cache replacement +to selecting LRU block with no pendending dependencies. + +

    Can a log-structured file system implement rename better? (Answer: +yes, since it can get the refcnts right). + +

    Discuss all graphs. + + + diff --git a/web/l14.txt b/web/l14.txt new file mode 100644 index 0000000..d121dff --- /dev/null +++ b/web/l14.txt @@ -0,0 +1,247 @@ +Why am I lecturing about Multics? + Origin of many ideas in today's OSes + Motivated UNIX design (often in opposition) + Motivated x86 VM design + This lecture is really "how Intel intended x86 segments to be used" + +Multics background + design started in 1965 + very few interactive time-shared systems then: CTSS + design first, then implementation + system stable by 1969 + so pre-dates UNIX, which started in 1969 + ambitious, many years, many programmers, MIT+GE+BTL + +Multics high-level goals + many users on same machine: "time sharing" + perhaps commercial services sharing the machine too + remote terminal access (but no recognizable data networks: wired or phone) + persistent reliable file system + encourage interaction between users + support joint projects that share data &c + control access to data that should not be shared + +Most interesting aspect of design: memory system + idea: eliminate memory / file distinction + file i/o uses LD / ST instructions + no difference between memory and disk files + just jump to start of file to run program + enhances sharing: no more copying files to private memory + this seems like a really neat simplification! + +GE 645 physical memory system + 24-bit phys addresses + 36-bit words + so up to 75 megabytes of physical memory!!! + but no-one could afford more than about a megabyte + +[per-process state] + DBR + DS, SDW (== address space) + KST + stack segment + per-segment linkage segments + +[global state] + segment content pages + per-segment page tables + per-segment branch in directory segment + AST + +645 segments (simplified for now, no paging or rings) + descriptor base register (DBR) holds phy addr of descriptor segment (DS) + DS is an array of segment descriptor words (SDW) + SDW: phys addr, length, r/w/x, present + CPU has pairs of registers: 18 bit offset, 18 bit segment # + five pairs (PC, arguments, base, linkage, stack) + early Multics limited each segment to 2^16 words + thus there are lots of them, intended to correspond to program modules + note: cannot directly address phys mem (18 vs 24) + 645 segments are a lot like the x86! + +645 paging + DBR and SDW actually contain phy addr of 64-entry page table + each page is 1024 words + PTE holds phys addr and present flag + no permission bits, so you really need to use the segments, not like JOS + no per-process page table, only per-segment + so all processes using a segment share its page table and phys storage + makes sense assuming segments tend to be shared + paging environment doesn't change on process switch + +Multics processes + each process has its own DS + Multics switches DBR on context switch + different processes typically have different number for same segment + +how to use segments to unify memory and file system? + don't want to have to use 18-bit seg numbers as file names + we want to write programs using symbolic names + names should be hierarchical (for users) + so users can have directories and sub-directories + and path names + +Multics file system + tree structure, directories and files + each file and directory is a segment + dir seg holds array of "branches" + name, length, ACL, array of block #s, "active" + unique ROOT directory + path names: ROOT > A > B + note there are no inodes, thus no i-numbers + so "real name" for a file is the complete path name + o/s tables have path name where unix would have i-number + presumably makes renaming and removing active files awkward + no hard links + +how does a program refer to a different segment? + inter-segment variables contain symbolic segment name + A$E refers to segment A, variable/function E + what happens when segment B calls function A$E(1, 2, 3)? + +when compiling B: + compiler actually generates *two* segments + one holds B's instructions + one holds B's linkage information + initial linkage entry: + name of segment e.g. "A" + name of symbol e.g. "E" + valid flag + CALL instruction is indirect through entry i of linkage segment + compiler marks entry i invalid + [storage for strings "A" and "E" really in segment B, not linkage seg] + +when a process is executing B: + two segments in DS: B and a *copy* of B's linkage segment + CPU linkage register always points to current segment's linkage segment + call A$E is really call indirect via linkage[i] + faults because linkage[i] is invalid + o/s fault handler + looks up segment name for i ("A") + search path in file system for segment "A" (cwd, library dirs) + if not already in use by some process (branch active flag and AST knows): + allocate page table and pages + read segment A into memory + if not already in use by *this* process (KST knows): + find free SDW j in process DS, make it refer to A's page table + set up r/w/x based on process's user and file ACL + also set up copy of A's linkage segment + search A's symbol table for "E" + linkage[i] := j / address(E) + restart B + now the CALL works via linkage[i] + and subsequent calls are fast + +how does A get the correct linkage register? + the right value cannot be embedded in A, since shared among processes + so CALL actually goes to instructions in A's linkage segment + load current seg# into linkage register, jump into A + one set of these per procedure in A + +all memory / file references work this way + as if pointers were really symbolic names + segment # is really a transparent optimization + linking is "dynamic" + programs contain symbolic references + resolved only as needed -- if/when executed + code is shared among processes + was program data shared? + probably most variables not shared (on stack, in private segments) + maybe a DB would share a data segment, w/ synchronization + file data: + probably one at a time (locks) for read/write + read-only is easy to share + +filesystem / segment implications + programs start slowly due to dynamic linking + creat(), unlink(), &c are outside of this model + store beyond end extends a segment (== appends to a file) + no need for buffer cache! no need to copy into user space! + but no buffer cache => ad-hoc caches e.g. active segment table + when are dirty segments written back to disk? + only in page eviction algorithm, when free pages are low + database careful ordered writes? e.g. log before data blocks? + I don't know, probably separate flush system calls + +how does shell work? + you type a program name + the shell just CALLs that program, as a segment! + dynamic linking finds program segment and any library segments it needs + the program eventually returns, e.g. with RET + all this happened inside the shell process's address space + no fork, no exec + buggy program can crash the shell! e.g. scribble on stack + process creation was too slow to give each program its own process + +how valuable is the sharing provided by segment machinery? + is it critical to users sharing information? + or is it just there to save memory and copying? + +how does the kernel fit into all this? + kernel is a bunch of code modules in segments (in file system) + a process dynamically loads in the kernel segments that it uses + so kernel segments have different numbers in different processes + a little different from separate kernel "program" in JOS or xv6 + kernel shares process's segment# address space + thus easy to interpret seg #s in system call arguments + kernel segment ACLs in file system restrict write + so mapped non-writeable into processes + +how to call the kernel? + very similar to the Intel x86 + 8 rings. users at 4. core kernel at 0. + CPU knows current execution level + SDW has max read/write/execute levels + call gate: lowers ring level, but only at designated entry + stack per ring, incoming call switches stacks + inner ring can always read arguments, write results + problem: checking validity of arguments to system calls + don't want user to trick kernel into reading/writing the wrong segment + you have this problem in JOS too + later Multics CPUs had hardware to check argument references + +are Multics rings a general-purpose protected subsystem facility? + example: protected game implementation + protected so that users cannot cheat + put game's code and data in ring 3 + BUT what if I don't trust the author? + or if i've already put some other subsystem in ring 3? + a ring has full power over itself and outer rings: you must trust + today: user/kernel, server processes and IPC + pro: protection among mutually suspicious subsystems + con: no convenient sharing of address spaces + +UNIX vs Multics + UNIX was less ambitious (e.g. no unified mem/FS) + UNIX hardware was small + just a few programmers, all in the same room + evolved rather than pre-planned + quickly self-hosted, so they got experience earlier + +What did UNIX inherit from MULTICS? + a shell at user level (not built into kernel) + a single hierarchical file system, with subdirectories + controlled sharing of files + written in high level language, self-hosted development + +What did UNIX reject from MULTICS? + files look like memory + instead, unifying idea is file descriptor and read()/write() + memory is a totally separate resource + dynamic linking + instead, static linking at compile time, every binary had copy of libraries + segments and sharing + instead, single linear address space per process, like xv6 + (but shared libraries brought these back, just for efficiency, in 1980s) + Hierarchical rings of protection + simpler user/kernel + for subsystems, setuid, then client/server and IPC + +The most useful sources I found for late-1960s Multics VM: + 1. Bensoussan, Clingen, Daley, "The Multics Virtual Memory: Concepts + and Design," CACM 1972 (segments, paging, naming segments, dynamic + linking). + 2. Daley and Dennis, "Virtual Memory, Processes, and Sharing in Multics," + SOSP 1967 (more details about dynamic linking and CPU). + 3. Graham, "Protection in an Information Processing Utility," + CACM 1968 (brief account of rings and gates). diff --git a/web/l19.txt b/web/l19.txt new file mode 100644 index 0000000..af9d0bb --- /dev/null +++ b/web/l19.txt @@ -0,0 +1,1412 @@ +-- front +6.828 Shells Lecture + +Hello. + +-- intro +Bourne shell + +Simplest shell: run cmd arg arg ... + fork + exec in child + wait in parent + +More functionality: + file redirection: cmd >file + open file as fd 1 in child before exec + +Still more functionality: + pipes: cmd | cmd | cmd ... + create pipe, + run first cmd with pipe on fd 1, + run second cmd with other end of pipe on fd 0 + +More Bourne arcana: + $* - command args + "$@" - unexpanded command args + environment variables + macro substitution + if, while, for + || + && + "foo $x" + 'foo $x' + `cat foo` + +-- rc +Rc Shell + + +No reparsing of input (except explicit eval). + +Variables as explicit lists. + +Explicit concatenation. + +Multiple input pipes <{cmd} - pass /dev/fd/4 as file name. + +Syntax more like C, less like Algol. + +diff <{echo hi} <{echo bye} + +-- es +Es shell + + +rc++ + +Goal is to override functionality cleanly. + +Rewrite input like cmd | cmd2 as %pipe {cmd} {cmd2}. + +Users can redefine %pipe, etc. + +Need lexical scoping and let to allow new %pipe refer to old %pipe. + +Need garbage collection to collect unreachable code. + +Design principle: + minimal functionality + good defaults + allow users to customize implementations + + emacs, exokernel + +-- apps +Applications + +Shell scripts are only as good as the programs they use. + (What good are pipes without cat, grep, sort, wc, etc.?) + +The more the scripts can access, the more powerful they become. + +-- acme +Acme, Plan 9 text editor + +Make window system control files available to +everything, including shell. + +Can write shell scripts to script interactions. + +/home/rsc/bin/Slide +/home/rsc/bin/Slide- +/home/rsc/bin/Slide+ + +/usr/local/plan9/bin/adict + +win + +-- javascript +JavaScript + +Very powerful + - not because it's a great language + - because it has a great data set + - Google Maps + - Gmail + - Ymail + - etc. + +-- greasemonkey +GreaseMonkey + +// ==UserScript== +// @name Google Ring +// @namespace http://swtch.com/greasemonkey/ +// @description Changes Google Logo +// @include http://*.google.*/* +// ==/UserScript== + +(function() { + for(var i=0; i[2=1] | sed 1d | winwrite body + case 2 + dict=$2 + case 3 + dict=$2 + dict -d $dict $3 >[2=1] | winwrite body + } + winctl clean + wineventloop +} + +dict=NONE +if(~ $1 -d){ + shift + dict=$2 + shift +} +if(~ $1 -d*){ + dict=`{echo $1 | sed 's/-d//'} + shift +} +if(~ $1 -*){ + echo 'usage: adict [-d dict] [word...]' >[1=2] + exit usage +} + +switch($#*){ +case 0 + if(~ $dict NONE) + dictwin /adict/ + if not + dictwin /adict/$dict/ $dict +case * + if(~ $dict NONE){ + dict=`{dict -d'?' | 9 sed -n 's/^ ([^\[ ]+).*/\1/p' | sed 1q} + if(~ $#dict 0){ + echo 'no dictionaries present on this system' >[1=2] + exit nodict + } + } + for(i) + dictwin /adict/$dict/$i $dict $i +} + +-- /usr/local/plan9/lib/acme.rc +fn newwindow { + winctl=`{9p read acme/new/ctl} + winid=$winctl(1) + winctl noscroll +} + +fn winctl { + echo $* | 9p write acme/acme/$winid/ctl +} + +fn winread { + 9p read acme/acme/$winid/$1 +} + +fn winwrite { + 9p write acme/acme/$winid/$1 +} + +fn windump { + if(! ~ $1 - '') + winctl dumpdir $1 + if(! ~ $2 - '') + winctl dump $2 +} + +fn winname { + winctl name $1 +} + +fn winwriteevent { + echo $1$2$3 $4 | winwrite event +} + +fn windel { + if(~ $1 sure) + winctl delete + if not + winctl del +} + +fn wineventloop { + . <{winread event >[2]/dev/null | acmeevent} +} +-- /home/rsc/plan9/rc/bin/fedex +#!/bin/rc + +if(! ~ $#* 1) { + echo usage: fedex 123456789012 >[1=2] + exit usage +} + +rfork e + +fn bgrep{ +pattern=`{echo $1 | sed 's;/;\\&;'} +shift + +@{ echo 'X { +$ +a + +. +} +X ,x/(.+\n)+\n/ g/'$pattern'/p' | +sam -d $* >[2]/dev/null +} +} + +fn awk2 { + awk 'NR%2==1 { a=$0; } + NR%2==0 { b=$0; printf("%-30s %s\n", a, b); } + ' $* +} + +fn awk3 { + awk '{line[NR] = $0} + END{ + i = 4; + while(i < NR){ + what=line[i++]; + when=line[i]; + comment=""; + if(!(when ~ /..\/..\/.... ..:../)){ + # out of sync + printf("%s\n", what); + continue; + } + i++; + if(!(line[i+1] ~ /..\/..\/.... ..:../) && + (i+2 > NR || line[i+2] ~ /..\/..\/.... ..:../)){ + what = what ", " line[i++]; + } + printf("%s %s\n", when, what); + } + }' $* +} + +# hget 'http://www.fedex.com/cgi-bin/track_it?airbill_list='$1'&kurrent_airbill='$1'&language=english&cntry_code=us&state=0' | +hget 'http://www.fedex.com/cgi-bin/tracking?action=track&language=english&cntry_code=us&initial=x&mps=y&tracknumbers='$1 | + htmlfmt >/tmp/fedex.$pid +sed -n '/Tracking number/,/^$/p' /tmp/fedex.$pid | awk2 +echo +sed -n '/Reference number/,/^$/p' /tmp/fedex.$pid | awk2 +echo +sed -n '/Date.time/,/^$/p' /tmp/fedex.$pid | sed 1,4d | fmt -l 4000 | sed 's/ [A-Z][A-Z] /&\n/g' +rm /tmp/fedex.$pid +-- /home/rsc/src/webscript/a3 +#!./o.webscript + +load "http://www.ups.com/WebTracking/track?loc=en_US" +find textbox "InquiryNumber1" +input "1z30557w0340175623" +find next checkbox +input "yes" +find prev form +submit +if(find "Delivery Information"){ + find outer table + print +}else if(find "One or more"){ + print +}else{ + print "Unexpected results." + find page + print +} +-- /home/rsc/src/webscript/a2 +#load "http://apc-reset/outlets.htm" +load "apc.html" +print +print "\n=============\n" +find "yoshimi" +find outer row +find next select +input "Immediate Reboot" +submit +print +-- /usr/local/plan9/acid/port +// portable acid for all architectures + +defn pfl(addr) +{ + print(pcfile(addr), ":", pcline(addr), "\n"); +} + +defn +notestk(addr) +{ + local pc, sp; + complex Ureg addr; + + pc = addr.pc\X; + sp = addr.sp\X; + + print("Note pc:", pc, " sp:", sp, " ", fmt(pc, 'a'), " "); + pfl(pc); + _stk({"PC", pc, "SP", sp, linkreg(addr)}, 1); +} + +defn +notelstk(addr) +{ + local pc, sp; + complex Ureg addr; + + pc = addr.pc\X; + sp = addr.sp\X; + + print("Note pc:", pc, " sp:", sp, " ", fmt(pc, 'a'), " "); + pfl(pc); + _stk({"PC", pc, "SP", sp, linkreg(addr)}, 1); +} + +defn params(param) +{ + while param do { + sym = head param; + print(sym[0], "=", itoa(sym[1], "%#ux")); + param = tail param; + if param then + print (","); + } +} + +stkprefix = ""; +stkignore = {}; +stkend = 0; + +defn locals(l) +{ + local sym; + + while l do { + sym = head l; + print(stkprefix, "\t", sym[0], "=", itoa(sym[1], "%#ux"), "\n"); + l = tail l; + } +} + +defn _stkign(frame) +{ + local file; + + file = pcfile(frame[0]); + s = stkignore; + while s do { + if regexp(head s, file) then + return 1; + s = tail s; + } + return 0; +} + +// print a stack trace +// +// in a run of leading frames in files matched by regexps in stkignore, +// only print the last one. +defn _stk(regs, dolocals) +{ + local stk, frame, pc, fn, done, callerpc, paramlist, locallist; + + stk = strace(regs); + if stkignore then { + while stk && tail stk && _stkign(head tail stk) do + stk = tail stk; + } + + callerpc = 0; + done = 0; + while stk && !done do { + frame = head stk; + stk = tail stk; + fn = frame[0]; + pc = frame[1]; + callerpc = frame[2]; + paramlist = frame[3]; + locallist = frame[4]; + + print(stkprefix, fmt(fn, 'a'), "("); + params(paramlist); + print(")"); + if pc != fn then + print("+", itoa(pc-fn, "%#ux")); + print(" "); + pfl(pc); + if dolocals then + locals(locallist); + if fn == var("threadmain") || fn == var("p9main") then + done=1; + if fn == var("threadstart") || fn == var("scheduler") then + done=1; + if callerpc == 0 then + done=1; + } + if callerpc && !done then { + print(stkprefix, fmt(callerpc, 'a'), " "); + pfl(callerpc); + } +} + +defn findsrc(file) +{ + local lst, src; + + if file[0] == '/' then { + src = file(file); + if src != {} then { + srcfiles = append srcfiles, file; + srctext = append srctext, src; + return src; + } + return {}; + } + + lst = srcpath; + while head lst do { + src = file(head lst+file); + if src != {} then { + srcfiles = append srcfiles, file; + srctext = append srctext, src; + return src; + } + lst = tail lst; + } +} + +defn line(addr) +{ + local src, file; + + file = pcfile(addr); + src = match(file, srcfiles); + + if src >= 0 then + src = srctext[src]; + else + src = findsrc(file); + + if src == {} then { + print("no source for ", file, "\n"); + return {}; + } + line = pcline(addr)-1; + print(file, ":", src[line], "\n"); +} + +defn addsrcdir(dir) +{ + dir = dir+"/"; + + if match(dir, srcpath) >= 0 then { + print("already in srcpath\n"); + return {}; + } + + srcpath = {dir}+srcpath; +} + +defn source() +{ + local l; + + l = srcpath; + while l do { + print(head l, "\n"); + l = tail l; + } + l = srcfiles; + + while l do { + print("\t", head l, "\n"); + l = tail l; + } +} + +defn Bsrc(addr) +{ + local lst; + + lst = srcpath; + file = pcfile(addr); + if file[0] == '/' && access(file) then { + rc("B "+file+":"+itoa(pcline(addr))); + return {}; + } + while head lst do { + name = head lst+file; + if access(name) then { + rc("B "+name+":"+itoa(pcline(addr))); + return {}; + } + lst = tail lst; + } + print("no source for ", file, "\n"); +} + +defn srcline(addr) +{ + local text, cline, line, file, src; + file = pcfile(addr); + src = match(file,srcfiles); + if (src>=0) then + src = srctext[src]; + else + src = findsrc(file); + if (src=={}) then + { + return "(no source)"; + } + return src[pcline(addr)-1]; +} + +defn src(addr) +{ + local src, file, line, cline, text; + + file = pcfile(addr); + src = match(file, srcfiles); + + if src >= 0 then + src = srctext[src]; + else + src = findsrc(file); + + if src == {} then { + print("no source for ", file, "\n"); + return {}; + } + + cline = pcline(addr)-1; + print(file, ":", cline+1, "\n"); + line = cline-5; + loop 0,10 do { + if line >= 0 then { + if line == cline then + print(">"); + else + print(" "); + text = src[line]; + if text == {} then + return {}; + print(line+1, "\t", text, "\n"); + } + line = line+1; + } +} + +defn step() // single step the process +{ + local lst, lpl, addr, bput; + + bput = 0; + if match(*PC, bplist) >= 0 then { // Sitting on a breakpoint + bput = fmt(*PC, bpfmt); + *bput = @bput; + } + + lst = follow(*PC); + + lpl = lst; + while lpl do { // place break points + *(head lpl) = bpinst; + lpl = tail lpl; + } + + startstop(pid); // do the step + + while lst do { // remove the breakpoints + addr = fmt(head lst, bpfmt); + *addr = @addr; + lst = tail lst; + } + if bput != 0 then + *bput = bpinst; +} + +defn bpset(addr) // set a breakpoint +{ + if status(pid) != "Stopped" then { + print("Waiting...\n"); + stop(pid); + } + if match(addr, bplist) >= 0 then + print("breakpoint already set at ", fmt(addr, 'a'), "\n"); + else { + *fmt(addr, bpfmt) = bpinst; + bplist = append bplist, addr; + } +} + +defn bptab() // print a table of breakpoints +{ + local lst, addr; + + lst = bplist; + while lst do { + addr = head lst; + print("\t", fmt(addr, 'X'), " ", fmt(addr, 'a'), " ", fmt(addr, 'i'), "\n"); + lst = tail lst; + } +} + +defn bpdel(addr) // delete a breakpoint +{ + local n, pc, nbplist; + + if addr == 0 then { + while bplist do { + pc = head bplist; + pc = fmt(pc, bpfmt); + *pc = @pc; + bplist = tail bplist; + } + return {}; + } + + n = match(addr, bplist); + if n < 0 then { + print("no breakpoint at ", fmt(addr, 'a'), "\n"); + return {}; + } + + addr = fmt(addr, bpfmt); + *addr = @addr; + + nbplist = {}; // delete from list + while bplist do { + pc = head bplist; + if pc != addr then + nbplist = append nbplist, pc; + bplist = tail bplist; + } + bplist = nbplist; // delete from memory +} + +defn cont() // continue execution +{ + local addr; + + addr = fmt(*PC, bpfmt); + if match(addr, bplist) >= 0 then { // Sitting on a breakpoint + *addr = @addr; + step(); // Step over + *addr = bpinst; + } + startstop(pid); // Run +} + +defn stopped(pid) // called from acid when a process changes state +{ + pfixstop(pid); + pstop(pid); // stub so this is easy to replace +} + +defn procs() // print status of processes +{ + local c, lst, cpid; + + cpid = pid; + lst = proclist; + while lst do { + np = head lst; + setproc(np); + if np == cpid then + c = '>'; + else + c = ' '; + print(fmt(c, 'c'), np, ": ", status(np), " at ", fmt(*PC, 'a'), " setproc(", np, ")\n"); + lst = tail lst; + } + pid = cpid; + if pid != 0 then + setproc(pid); +} + +_asmlines = 30; + +defn asm(addr) +{ + local bound; + + bound = fnbound(addr); + + addr = fmt(addr, 'i'); + loop 1,_asmlines do { + print(fmt(addr, 'a'), " ", fmt(addr, 'X')); + print("\t", @addr++, "\n"); + if bound != {} && addr > bound[1] then { + lasmaddr = addr; + return {}; + } + } + lasmaddr = addr; +} + +defn casm() +{ + asm(lasmaddr); +} + +defn xasm(addr) +{ + local bound; + + bound = fnbound(addr); + + addr = fmt(addr, 'i'); + loop 1,_asmlines do { + print(fmt(addr, 'a'), " ", fmt(addr, 'X')); + print("\t", *addr++, "\n"); + if bound != {} && addr > bound[1] then { + lasmaddr = addr; + return {}; + } + } + lasmaddr = addr; +} + +defn xcasm() +{ + xasm(lasmaddr); +} + +defn win() +{ + local npid, estr; + + bplist = {}; + notes = {}; + + estr = "/sys/lib/acid/window '0 0 600 400' "+textfile; + if progargs != "" then + estr = estr+" "+progargs; + + npid = rc(estr); + npid = atoi(npid); + if npid == 0 then + error("win failed to create process"); + + setproc(npid); + stopped(npid); +} + +defn win2() +{ + local npid, estr; + + bplist = {}; + notes = {}; + + estr = "/sys/lib/acid/transcript '0 0 600 400' '100 100 700 500' "+textfile; + if progargs != "" then + estr = estr+" "+progargs; + + npid = rc(estr); + npid = atoi(npid); + if npid == 0 then + error("win failed to create process"); + + setproc(npid); + stopped(npid); +} + +printstopped = 1; +defn new() +{ + local a; + + bplist = {}; + newproc(progargs); + a = var("p9main"); + if a == {} then + a = var("main"); + if a == {} then + return {}; + bpset(a); + while *PC != a do + cont(); + bpdel(a); +} + +defn stmnt() // step one statement +{ + local line; + + line = pcline(*PC); + while 1 do { + step(); + if line != pcline(*PC) then { + src(*PC); + return {}; + } + } +} + +defn func() // step until we leave the current function +{ + local bound, end, start, pc; + + bound = fnbound(*PC); + if bound == {} then { + print("cannot locate text symbol\n"); + return {}; + } + + pc = *PC; + start = bound[0]; + end = bound[1]; + while pc >= start && pc < end do { + step(); + pc = *PC; + } +} + +defn next() +{ + local sp, bound, pc; + + sp = *SP; + bound = fnbound(*PC); + if bound == {} then { + print("cannot locate text symbol\n"); + return {}; + } + stmnt(); + pc = *PC; + if pc >= bound[0] && pc < bound[1] then + return {}; + + while (pc < bound[0] || pc > bound[1]) && sp >= *SP do { + step(); + pc = *PC; + } + src(*PC); +} + +defn maps() +{ + local m, mm; + + m = map(); + while m != {} do { + mm = head m; + m = tail m; + print(mm[2]\X, " ", mm[3]\X, " ", mm[4]\X, " ", mm[0], " ", mm[1], "\n"); + } +} + +defn dump(addr, n, fmt) +{ + loop 0, n do { + print(fmt(addr, 'X'), ": "); + addr = mem(addr, fmt); + } +} + +defn mem(addr, fmt) +{ + + local i, c, n; + + i = 0; + while fmt[i] != 0 do { + c = fmt[i]; + n = 0; + while '0' <= fmt[i] && fmt[i] <= '9' do { + n = 10*n + fmt[i]-'0'; + i = i+1; + } + if n <= 0 then n = 1; + addr = fmt(addr, fmt[i]); + while n > 0 do { + print(*addr++, " "); + n = n-1; + } + i = i+1; + } + print("\n"); + return addr; +} + +defn symbols(pattern) +{ + local l, s; + + l = symbols; + while l do { + s = head l; + if regexp(pattern, s[0]) then + print(s[0], "\t", s[1], "\t", s[2], "\t", s[3], "\n"); + l = tail l; + } +} + +defn havesymbol(name) +{ + local l, s; + + l = symbols; + while l do { + s = head l; + l = tail l; + if s[0] == name then + return 1; + } + return 0; +} + +defn spsrch(len) +{ + local addr, a, s, e; + + addr = *SP; + s = origin & 0x7fffffff; + e = etext & 0x7fffffff; + loop 1, len do { + a = *addr++; + c = a & 0x7fffffff; + if c > s && c < e then { + print("src(", a, ")\n"); + pfl(a); + } + } +} + +defn acidtypes() +{ + local syms; + local l; + + l = textfile(); + if l != {} then { + syms = "acidtypes"; + while l != {} do { + syms = syms + " " + ((head l)[0]); + l = tail l; + } + includepipe(syms); + } +} + +defn getregs() +{ + local regs, l; + + regs = {}; + l = registers; + while l != {} do { + regs = append regs, var(l[0]); + l = tail l; + } + return regs; +} + +defn setregs(regs) +{ + local l; + + l = registers; + while l != {} do { + var(l[0]) = regs[0]; + l = tail l; + regs = tail regs; + } + return regs; +} + +defn resetregs() +{ + local l; + + l = registers; + while l != {} do { + var(l[0]) = register(l[0]); + l = tail l; + } +} + +defn clearregs() +{ + local l; + + l = registers; + while l != {} do { + var(l[0]) = refconst(~0); + l = tail l; + } +} + +progargs=""; +print(acidfile); + +-- /usr/local/plan9/acid/386 +// 386 support + +defn acidinit() // Called after all the init modules are loaded +{ + bplist = {}; + bpfmt = 'b'; + + srcpath = { + "./", + "/sys/src/libc/port/", + "/sys/src/libc/9sys/", + "/sys/src/libc/386/" + }; + + srcfiles = {}; // list of loaded files + srctext = {}; // the text of the files +} + +defn linkreg(addr) +{ + return {}; +} + +defn stk() // trace +{ + _stk({"PC", *PC, "SP", *SP}, 0); +} + +defn lstk() // trace with locals +{ + _stk({"PC", *PC, "SP", *SP}, 1); +} + +defn gpr() // print general(hah hah!) purpose registers +{ + print("AX\t", *AX, " BX\t", *BX, " CX\t", *CX, " DX\t", *DX, "\n"); + print("DI\t", *DI, " SI\t", *SI, " BP\t", *BP, "\n"); +} + +defn spr() // print special processor registers +{ + local pc; + local cause; + + pc = *PC; + print("PC\t", pc, " ", fmt(pc, 'a'), " "); + pfl(pc); + print("SP\t", *SP, " ECODE ", *ECODE, " EFLAG ", *EFLAGS, "\n"); + print("CS\t", *CS, " DS\t ", *DS, " SS\t", *SS, "\n"); + print("GS\t", *GS, " FS\t ", *FS, " ES\t", *ES, "\n"); + + cause = *TRAP; + print("TRAP\t", cause, " ", reason(cause), "\n"); +} + +defn regs() // print all registers +{ + spr(); + gpr(); +} + +defn mmregs() +{ + print("MM0\t", *MM0, " MM1\t", *MM1, "\n"); + print("MM2\t", *MM2, " MM3\t", *MM3, "\n"); + print("MM4\t", *MM4, " MM5\t", *MM5, "\n"); + print("MM6\t", *MM6, " MM7\t", *MM7, "\n"); +} + +defn pfixstop(pid) +{ + if *fmt(*PC-1, 'b') == 0xCC then { + // Linux stops us after the breakpoint, not at it + *PC = *PC-1; + } +} + + +defn pstop(pid) +{ + local l; + local pc; + local why; + + pc = *PC; + + // FIgure out why we stopped. + if *fmt(pc, 'b') == 0xCC then { + why = "breakpoint"; + + // fix up instruction for print; will put back later + *pc = @pc; + } else if *(pc-2\x) == 0x80CD then { + pc = pc-2; + why = "system call"; + } else + why = "stopped"; + + if printstopped then { + print(pid,": ", why, "\t"); + print(fmt(pc, 'a'), "\t", *fmt(pc, 'i'), "\n"); + } + + if why == "breakpoint" then + *fmt(pc, bpfmt) = bpinst; + + if printstopped && notes then { + if notes[0] != "sys: breakpoint" then { + print("Notes pending:\n"); + l = notes; + while l do { + print("\t", head l, "\n"); + l = tail l; + } + } + } +} + +aggr Ureg +{ + 'U' 0 di; + 'U' 4 si; + 'U' 8 bp; + 'U' 12 nsp; + 'U' 16 bx; + 'U' 20 dx; + 'U' 24 cx; + 'U' 28 ax; + 'U' 32 gs; + 'U' 36 fs; + 'U' 40 es; + 'U' 44 ds; + 'U' 48 trap; + 'U' 52 ecode; + 'U' 56 pc; + 'U' 60 cs; + 'U' 64 flags; + { + 'U' 68 usp; + 'U' 68 sp; + }; + 'U' 72 ss; +}; + +defn +Ureg(addr) { + complex Ureg addr; + print(" di ", addr.di, "\n"); + print(" si ", addr.si, "\n"); + print(" bp ", addr.bp, "\n"); + print(" nsp ", addr.nsp, "\n"); + print(" bx ", addr.bx, "\n"); + print(" dx ", addr.dx, "\n"); + print(" cx ", addr.cx, "\n"); + print(" ax ", addr.ax, "\n"); + print(" gs ", addr.gs, "\n"); + print(" fs ", addr.fs, "\n"); + print(" es ", addr.es, "\n"); + print(" ds ", addr.ds, "\n"); + print(" trap ", addr.trap, "\n"); + print(" ecode ", addr.ecode, "\n"); + print(" pc ", addr.pc, "\n"); + print(" cs ", addr.cs, "\n"); + print(" flags ", addr.flags, "\n"); + print(" sp ", addr.sp, "\n"); + print(" ss ", addr.ss, "\n"); +}; +sizeofUreg = 76; + +aggr Linkdebug +{ + 'X' 0 version; + 'X' 4 map; +}; + +aggr Linkmap +{ + 'X' 0 addr; + 'X' 4 name; + 'X' 8 dynsect; + 'X' 12 next; + 'X' 16 prev; +}; + +defn +linkdebug() +{ + local a; + + if !havesymbol("_DYNAMIC") then + return 0; + + a = _DYNAMIC; + while *a != 0 do { + if *a == 21 then // 21 == DT_DEBUG + return *(a+4); + a = a+8; + } + return 0; +} + +defn +dynamicmap() +{ + if systype == "linux" || systype == "freebsd" then { + local r, m, n; + + r = linkdebug(); + if r then { + complex Linkdebug r; + m = r.map; + n = 0; + while m != 0 && n < 100 do { + complex Linkmap m; + if m.name && *(m.name\b) && access(*(m.name\s)) then + print("textfile({\"", *(m.name\s), "\", ", m.addr\X, "});\n"); + m = m.next; + n = n+1; + } + } + } +} + +defn +acidmap() +{ +// dynamicmap(); + acidtypes(); +} + +print(acidfile); diff --git a/web/l2.html b/web/l2.html new file mode 100644 index 0000000..e183d5a --- /dev/null +++ b/web/l2.html @@ -0,0 +1,494 @@ + + +L2 + + + +

    6.828 Lecture Notes: x86 and PC architecture

    + +

    Outline

    +
      +
    • PC architecture +
    • x86 instruction set +
    • gcc calling conventions +
    • PC emulation +
    + +

    PC architecture

    + +
      +
    • A full PC has: +
        +
      • an x86 CPU with registers, execution unit, and memory management +
      • CPU chip pins include address and data signals +
      • memory +
      • disk +
      • keyboard +
      • display +
      • other resources: BIOS ROM, clock, ... +
      + +
    • We will start with the original 16-bit 8086 CPU (1978) +
    • CPU runs instructions: +
      +for(;;){
      +	run next instruction
      +}
      +
      + +
    • Needs work space: registers +
        +
      • four 16-bit data registers: AX, CX, DX, BX +
      • each in two 8-bit halves, e.g. AH and AL +
      • very fast, very few +
      +
    • More work space: memory +
        +
      • CPU sends out address on address lines (wires, one bit per wire) +
      • Data comes back on data lines +
      • or data is written to data lines +
      + +
    • Add address registers: pointers into memory +
        +
      • SP - stack pointer +
      • BP - frame base pointer +
      • SI - source index +
      • DI - destination index +
      + +
    • Instructions are in memory too! +
        +
      • IP - instruction pointer (PC on PDP-11, everything else) +
      • increment after running each instruction +
      • can be modified by CALL, RET, JMP, conditional jumps +
      + +
    • Want conditional jumps +
        +
      • FLAGS - various condition codes +
          +
        • whether last arithmetic operation overflowed +
        • ... was positive/negative +
        • ... was [not] zero +
        • ... carry/borrow on add/subtract +
        • ... overflow +
        • ... etc. +
        • whether interrupts are enabled +
        • direction of data copy instructions +
        +
      • JP, JN, J[N]Z, J[N]C, J[N]O ... +
      + +
    • Still not interesting - need I/O to interact with outside world +
        +
      • Original PC architecture: use dedicated I/O space +
          +
        • Works same as memory accesses but set I/O signal +
        • Only 1024 I/O addresses +
        • Example: write a byte to line printer: +
          +#define DATA_PORT    0x378
          +#define STATUS_PORT  0x379
          +#define   BUSY 0x80
          +#define CONTROL_PORT 0x37A
          +#define   STROBE 0x01
          +void
          +lpt_putc(int c)
          +{
          +  /* wait for printer to consume previous byte */
          +  while((inb(STATUS_PORT) & BUSY) == 0)
          +    ;
          +
          +  /* put the byte on the parallel lines */
          +  outb(DATA_PORT, c);
          +
          +  /* tell the printer to look at the data */
          +  outb(CONTROL_PORT, STROBE);
          +  outb(CONTROL_PORT, 0);
          +}
          +
          +		
        + +
      • Memory-Mapped I/O +
          +
        • Use normal physical memory addresses +
            +
          • Gets around limited size of I/O address space +
          • No need for special instructions +
          • System controller routes to appropriate device +
          +
        • Works like ``magic'' memory: +
            +
          • Addressed and accessed like memory, + but ... +
          • ... does not behave like memory! +
          • Reads and writes can have ``side effects'' +
          • Read results can change due to external events +
          +
        +
      + + +
    • What if we want to use more than 2^16 bytes of memory? +
        +
      • 8086 has 20-bit physical addresses, can have 1 Meg RAM +
      • each segment is a 2^16 byte window into physical memory +
      • virtual to physical translation: pa = va + seg*16 +
      • the segment is usually implicit, from a segment register +
      • CS - code segment (for fetches via IP) +
      • SS - stack segment (for load/store via SP and BP) +
      • DS - data segment (for load/store via other registers) +
      • ES - another data segment (destination for string operations) +
      • tricky: can't use the 16-bit address of a stack variable as a pointer +
      • but a far pointer includes full segment:offset (16 + 16 bits) +
      + +
    • But 8086's 16-bit addresses and data were still painfully small +
        +
      • 80386 added support for 32-bit data and addresses (1985) +
      • boots in 16-bit mode, boot.S switches to 32-bit mode +
      • registers are 32 bits wide, called EAX rather than AX +
      • operands and addresses are also 32 bits, e.g. ADD does 32-bit arithmetic +
      • prefix 0x66 gets you 16-bit mode: MOVW is really 0x66 MOVW +
      • the .code32 in boot.S tells assembler to generate 0x66 for e.g. MOVW +
      • 80386 also changed segments and added paged memory... +
      + +
    + +

    x86 Physical Memory Map

    + +
      +
    • The physical address space mostly looks like ordinary RAM +
    • Except some low-memory addresses actually refer to other things +
    • Writes to VGA memory appear on the screen +
    • Reset or power-on jumps to ROM at 0x000ffff0 +
    + +
    ++------------------+  <- 0xFFFFFFFF (4GB)
    +|      32-bit      |
    +|  memory mapped   |
    +|     devices      |
    +|                  |
    +/\/\/\/\/\/\/\/\/\/\
    +
    +/\/\/\/\/\/\/\/\/\/\
    +|                  |
    +|      Unused      |
    +|                  |
    ++------------------+  <- depends on amount of RAM
    +|                  |
    +|                  |
    +| Extended Memory  |
    +|                  |
    +|                  |
    ++------------------+  <- 0x00100000 (1MB)
    +|     BIOS ROM     |
    ++------------------+  <- 0x000F0000 (960KB)
    +|  16-bit devices, |
    +|  expansion ROMs  |
    ++------------------+  <- 0x000C0000 (768KB)
    +|   VGA Display    |
    ++------------------+  <- 0x000A0000 (640KB)
    +|                  |
    +|    Low Memory    |
    +|                  |
    ++------------------+  <- 0x00000000
    +
    + +

    x86 Instruction Set

    + +
      +
    • Two-operand instruction set +
        +
      • Intel syntax: op dst, src +
      • AT&T (gcc/gas) syntax: op src, dst +
          +
        • uses b, w, l suffix on instructions to specify size of operands +
        +
      • Operands are registers, constant, memory via register, memory via constant +
      • Examples: +
+
AT&T syntax "C"-ish equivalent +
movl %eax, %edx edx = eax; register mode +
movl $0x123, %edx edx = 0x123; immediate +
movl 0x123, %edx edx = *(int32_t*)0x123; direct +
movl (%ebx), %edx edx = *(int32_t*)ebx; indirect +
movl 4(%ebx), %edx edx = *(int32_t*)(ebx+4); displaced +
+ + +

  • Instruction classes + + +
  • Intel architecture manual Volume 2 is the reference + + + +

    gcc x86 calling conventions

    + + + + +

    PC emulation

    + + diff --git a/web/l3.html b/web/l3.html new file mode 100644 index 0000000..7d6ca0d --- /dev/null +++ b/web/l3.html @@ -0,0 +1,334 @@ +L3 + + + + + +

    Operating system organizaton

    + +

    Required reading: Exokernel paper. + +

    Intro: virtualizing

    + +

    One way to think about an operating system interface is that it +extends the hardware instructions with a set of "instructions" that +are implemented in software. These instructions are invoked using a +system call instruction (int on the x86). In this view, a task of the +operating system is to provide each application with a virtual +version of the interface; that is, it provides each application with a +virtual computer. + +

    One of the challenges in an operating system is multiplexing the +physical resources between the potentially many virtual computers. +What makes the multiplexing typically complicated is an additional +constraint: isolate the virtual computers well from each other. That +is, +

    + +

    In this lecture, we will explore at a high-level how to build +virtual computer that meet these goals. In the rest of the term we +work out the details. + +

    Virtual processors

    + +

    To give each application its own set of virtual processor, we need +to virtualize the physical processors. One way to do is to multiplex +the physical processor over time: the operating system runs one +application for a while, then runs another application for while, etc. +We can implement this solution as follows: when an application has run +for its share of the processor, unload the state of the phyical +processor, save that state to be able to resume the application later, +load in the state for the next application, and resume it. + +

    What needs to be saved and restored? That depends on the +processor, but for the x86: +

    + +

    To enforce that a virtual processor doesn't keep a processor, the +operating system can arrange for a periodic interrupt, and switch the +processor in the interrupt routine. + +

    To separate the memories of the applications, we may also need to save +and restore the registers that define the (virtual) memory of the +application (e.g., segment and MMU registers on the x86), which is +explained next. + + + +

    Separating memories

    + +

    Approach to separating memories: +

    +The approaches can be combined. + +

    Lets assume unlimited physical memory for a little while. We can +enforce separation then as follows: +

    +Why does this work? load/stores/jmps cannot touch/enter other +application's domains. + +

    To allow for controled sharing and separation with an application, +extend domain registers with protectioin bits: read (R), write (W), +execute-only (X). + +

    How to protect the domain registers? Extend the protection bits +with a kernel-only one. When in kernel-mode, processor can change +domain registers. As we will see in lecture 4, x86 stores the U/K +information in CPL (current privilege level) in CS segment +register. + +

    To change from user to kernel, extend the hardware with special +instructions for entering a "supervisor" or "system" call, and +returning from it. On x86, int and reti. The int instruction takes as +argument the system call number. We can then think of the kernel +interface as the set of "instructions" that augment the instructions +implemented in hardware. + +

    Memory management

    + +

    We assumed unlimited physical memory and big addresses. In +practice, operating system must support creating, shrinking, and +growing of domains, while still allowing the addresses of an +application to be contiguous (for programming convenience). What if +we want to grow the domain of application 1 but the memory right below +and above it is in use by application 2? + +

    How? Virtual addresses and spaces. Virtualize addresses and let +the kernel control the mapping from virtual to physical. + +

    Address spaces provide each application with the ideas that it has +a complete memory for itself. All the addresses it issues are its +addresses (e.g., each application has an address 0). + +

  • How do you give each application its own address space? + + +
  • What if two applications want to share real memory? Map the pages +into multiple address spaces and have protection bits per page. + +
  • How do you give an application access to a memory-mapped-IO +device? Map the physical address for the device into the applications +address space. + +
  • How do you get off the ground? + + +

    Operating system organizations

    + +

    A central theme in operating system design is how to organize the +operating system. It is helpful to define a couple of terms: +

    + +

    Example: trace a call to printf made by an application. + +

    There are roughly 4 operating system designs: +

    + +

    Although monolithic operating systems are the dominant operating +system architecture for desktop and server machines, it is worthwhile +to consider alternative architectures, even it is just to understand +operating systems better. This lecture looks at exokernels, because +that is what you will building in the lab. xv6 is organized as a +monolithic system, and we will study in the next lectures. Later in +the term we will read papers about microkernel and virtual machine +operating systems. + +

    Exokernels

    + +

    The exokernel architecture takes an end-to-end approach to +operating system design. In this design, the kernel just securely +multiplexes physical resources; any programmer can decide what the +operating system interface and its implementation are for his +application. One would expect a couple of popular APIs (e.g., UNIX) +that most applications will link against, but a programmer is always +free to replace that API, partially or completely. (Draw picture of +JOS.) + +

    Compare UNIX interface (v6 or OSX) with the JOS exokernel-like interface: +

    +enum
    +{
    +	SYS_cputs = 0,
    +	SYS_cgetc,
    +	SYS_getenvid,
    +	SYS_env_destroy,
    +	SYS_page_alloc,
    +	SYS_page_map,
    +	SYS_page_unmap,
    +	SYS_exofork,
    +	SYS_env_set_status,
    +	SYS_env_set_trapframe,
    +	SYS_env_set_pgfault_upcall,
    +	SYS_yield,
    +	SYS_ipc_try_send,
    +	SYS_ipc_recv,
    +};
    +
    + +

    To illustrate the differences between these interfaces in more +detail consider implementing the following: +

    + +

    How well can each kernel interface implement the above examples? +(Start with UNIX interface and see where you run into problems.) (The +JOS kernel interface is not flexible enough: for example, +ipc_receive is blocking.) + +

    Exokernel paper discussion

    + + +

    The central challenge in an exokernel design it to provide +extensibility, but provide fault isolation. This challenge breaks +down into three problems: + +

    + + + + diff --git a/web/l4.html b/web/l4.html new file mode 100644 index 0000000..342af32 --- /dev/null +++ b/web/l4.html @@ -0,0 +1,518 @@ +L4 + + + + + +

    Address translation and sharing using segments

    + +

    This lecture is about virtual memory, focusing on address +spaces. It is the first lecture out of series of lectures that uses +xv6 as a case study. + +

    Address spaces

    + + + +

    Two main approaches to implementing address spaces: using segments + and using page tables. Often when one uses segments, one also uses + page tables. But not the other way around; i.e., paging without + segmentation is common. + +

    Example support for address spaces: x86

    + +

    For an operating system to provide address spaces and address +translation typically requires support from hardware. The translation +and checking of permissions typically must happen on each address used +by a program, and it would be too slow to check that in software (if +even possible). The division of labor is operating system manages +address spaces, and hardware translates addresses and checks +permissions. + +

    PC block diagram without virtual memory support: +

    + +

    The x86 starts out in real mode and translation is as follows: +

    + +

    The operating system can switch the x86 to protected mode, which +allows the operating system to create address spaces. Translation in +protected mode is as follows: +

    + +

    Next lecture covers paging; now we focus on segmentation. + +

    Protected-mode segmentation works as follows: +

    + +

    Case study (xv6)

    + +

    xv6 is a reimplementation of Unix 6th edition. +

    + +

    Newer Unixs have inherited many of the conceptual ideas even though +they added paging, networking, graphics, improve performance, etc. + +

    You will need to read most of the source code multiple times. Your +goal is to explain every line to yourself. + +

    Overview of address spaces in xv6

    + +

    In today's lecture we see how xv6 creates the kernel address + spaces, first user address spaces, and switches to it. To understand + how this happens, we need to understand in detail the state on the + stack too---this may be surprising, but a thread of control and + address space are tightly bundled in xv6, in a concept + called process. The kernel address space is the only address + space with multiple threads of control. We will study context + switching and process management in detail next weeks; creation of + the first user process (init) will get you a first flavor. + +

    xv6 uses only the segmentation hardware on xv6, but in a limited + way. (In JOS you will use page-table hardware too, which we cover in + next lecture.) The adddress space layouts are as follows: +

    + +

    xv6 makes minimal use of the segmentation hardware available on the +x86. What other plans could you envision? + +

    In xv6, each each program has a user and a kernel stack; when the +user program switches to the kernel, it switches to its kernel stack. +Its kernel stack is stored in process's proc structure. (This is +arranged through the descriptors in the IDT, which is covered later.) + +

    xv6 assumes that there is a lot of physical memory. It assumes that + segments can be stored contiguously in physical memory and has + therefore no need for page tables. + +

    xv6 kernel address space

    + +

    Let's see how xv6 creates the kernel address space by tracing xv6 + from when it boots, focussing on address space management: +

    + +

    xv6 user address spaces

    + + + +

    Managing physical memory

    + +

    To create an address space we must allocate physical memory, which + will be freed when an address space is deleted (e.g., when a user + program terminates). xv6 implements a first-fit memory allocater + (see kalloc.c). + +

    It maintains a list of ranges of free memory. The allocator finds + the first range that is larger than the amount of requested memory. + It splits that range in two: one range of the size requested and one + of the remainder. It returns the first range. When memory is + freed, kfree will merge ranges that are adjacent in memory. + +

    Under what scenarios is a first-fit memory allocator undesirable? + +

    Growing an address space

    + +

    How can a user process grow its address space? growproc. +

    +

    We could do a lot better if segments didn't have to contiguous in + physical memory. How could we arrange that? Using page tables, which + is our next topic. This is one place where page tables would be + useful, but there are others too (e.g., in fork). + + + diff --git a/web/l5.html b/web/l5.html new file mode 100644 index 0000000..61b55e4 --- /dev/null +++ b/web/l5.html @@ -0,0 +1,210 @@ +Lecture 5/title> +<html> +<head> +</head> +<body> + +<h2>Address translation and sharing using page tables</h2> + +<p> Reading: <a href="../readings/i386/toc.htm">80386</a> chapters 5 and 6<br> + +<p> Handout: <b> x86 address translation diagram</b> - +<a href="x86_translation.ps">PS</a> - +<a href="x86_translation.eps">EPS</a> - +<a href="x86_translation.fig">xfig</a> +<br> + +<p>Why do we care about x86 address translation? +<ul> +<li>It can simplify s/w structure by placing data at fixed known addresses. +<li>It can implement tricks like demand paging and copy-on-write. +<li>It can isolate programs to contain bugs. +<li>It can isolate programs to increase security. +<li>JOS uses paging a lot, and segments more than you might think. +</ul> + +<p>Why aren't protected-mode segments enough? +<ul> +<li>Why did the 386 add translation using page tables as well? +<li>Isn't it enough to give each process its own segments? +</ul> + +<p>Translation using page tables on x86: +<ul> +<li>paging hardware maps linear address (la) to physical address (pa) +<li>(we will often interchange "linear" and "virtual") +<li>page size is 4096 bytes, so there are 1,048,576 pages in 2^32 +<li>why not just have a big array with each page #'s translation? +<ul> +<li>table[20-bit linear page #] => 20-bit phys page # +</ul> +<li>386 uses 2-level mapping structure +<li>one page directory page, with 1024 page directory entries (PDEs) +<li>up to 1024 page table pages, each with 1024 page table entries (PTEs) +<li>so la has 10 bits of directory index, 10 bits table index, 12 bits offset +<li>What's in a PDE or PTE? +<ul> +<li>20-bit phys page number, present, read/write, user/supervisor +</ul> +<li>cr3 register holds physical address of current page directory +<li>puzzle: what do PDE read/write and user/supervisor flags mean? +<li>puzzle: can supervisor read/write user pages? + +<li>Here's how the MMU translates an la to a pa: + + <pre> + uint + translate (uint la, bool user, bool write) + { + uint pde; + pde = read_mem (%CR3 + 4*(la >> 22)); + access (pde, user, read); + pte = read_mem ( (pde & 0xfffff000) + 4*((la >> 12) & 0x3ff)); + access (pte, user, read); + return (pte & 0xfffff000) + (la & 0xfff); + } + + // check protection. pxe is a pte or pde. + // user is true if CPL==3 + void + access (uint pxe, bool user, bool write) + { + if (!(pxe & PG_P) + => page fault -- page not present + if (!(pxe & PG_U) && user) + => page fault -- not access for user + + if (write && !(pxe & PG_W)) + if (user) + => page fault -- not writable + else if (!(pxe & PG_U)) + => page fault -- not writable + else if (%CR0 & CR0_WP) + => page fault -- not writable + } + </pre> + +<li>CPU's TLB caches vpn => ppn mappings +<li>if you change a PDE or PTE, you must flush the TLB! +<ul> + <li>by re-loading cr3 +</ul> +<li>turn on paging by setting CR0_PE bit of %cr0 +</ul> + +Can we use paging to limit what memory an app can read/write? +<ul> +<li>user can't modify cr3 (requires privilege) +<li>is that enough? +<li>could user modify page tables? after all, they are in memory. +</ul> + +<p>How we will use paging (and segments) in JOS: +<ul> +<li>use segments only to switch privilege level into/out of kernel +<li>use paging to structure process address space +<li>use paging to limit process memory access to its own address space +<li>below is the JOS virtual memory map +<li>why map both kernel and current process? why not 4GB for each? +<li>why is the kernel at the top? +<li>why map all of phys mem at the top? i.e. why multiple mappings? +<li>why map page table a second time at VPT? +<li>why map page table a third time at UVPT? +<li>how do we switch mappings for a different process? +</ul> + +<pre> + 4 Gig --------> +------------------------------+ + | | RW/-- + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + : . : + : . : + : . : + |~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~| RW/-- + | | RW/-- + | Remapped Physical Memory | RW/-- + | | RW/-- + KERNBASE -----> +------------------------------+ 0xf0000000 + | Cur. Page Table (Kern. RW) | RW/-- PTSIZE + VPT,KSTACKTOP--> +------------------------------+ 0xefc00000 --+ + | Kernel Stack | RW/-- KSTKSIZE | + | - - - - - - - - - - - - - - -| PTSIZE + | Invalid Memory | --/-- | + ULIM ------> +------------------------------+ 0xef800000 --+ + | Cur. Page Table (User R-) | R-/R- PTSIZE + UVPT ----> +------------------------------+ 0xef400000 + | RO PAGES | R-/R- PTSIZE + UPAGES ----> +------------------------------+ 0xef000000 + | RO ENVS | R-/R- PTSIZE + UTOP,UENVS ------> +------------------------------+ 0xeec00000 + UXSTACKTOP -/ | User Exception Stack | RW/RW PGSIZE + +------------------------------+ 0xeebff000 + | Empty Memory | --/-- PGSIZE + USTACKTOP ---> +------------------------------+ 0xeebfe000 + | Normal User Stack | RW/RW PGSIZE + +------------------------------+ 0xeebfd000 + | | + | | + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + . . + . . + . . + |~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~| + | Program Data & Heap | + UTEXT --------> +------------------------------+ 0x00800000 + PFTEMP -------> | Empty Memory | PTSIZE + | | + UTEMP --------> +------------------------------+ 0x00400000 + | Empty Memory | PTSIZE + 0 ------------> +------------------------------+ +</pre> + +<h3>The VPT </h3> + +<p>Remember how the X86 translates virtual addresses into physical ones: + +<p><img src=pagetables.png> + +<p>CR3 points at the page directory. The PDX part of the address +indexes into the page directory to give you a page table. The +PTX part indexes into the page table to give you a page, and then +you add the low bits in. + +<p>But the processor has no concept of page directories, page tables, +and pages being anything other than plain memory. So there's nothing +that says a particular page in memory can't serve as two or three of +these at once. The processor just follows pointers: + +pd = lcr3(); +pt = *(pd+4*PDX); +page = *(pt+4*PTX); + +<p>Diagramatically, it starts at CR3, follows three arrows, and then stops. + +<p>If we put a pointer into the page directory that points back to itself at +index Z, as in + +<p><img src=vpt.png> + +<p>then when we try to translate a virtual address with PDX and PTX +equal to V, following three arrows leaves us at the page directory. +So that virtual page translates to the page holding the page directory. +In Jos, V is 0x3BD, so the virtual address of the VPD is +(0x3BD<<22)|(0x3BD<<12). + + +<p>Now, if we try to translate a virtual address with PDX = V but an +arbitrary PTX != V, then following three arrows from CR3 ends +one level up from usual (instead of two as in the last case), +which is to say in the page tables. So the set of virtual pages +with PDX=V form a 4MB region whose page contents, as far +as the processor is concerned, are the page tables themselves. +In Jos, V is 0x3BD so the virtual address of the VPT is (0x3BD<<22). + +<p>So because of the "no-op" arrow we've cleverly inserted into +the page directory, we've mapped the pages being used as +the page directory and page table (which are normally virtually +invisible) into the virtual address space. + + +</body> diff --git a/web/mkhtml b/web/mkhtml new file mode 100755 index 0000000..74987e6 --- /dev/null +++ b/web/mkhtml @@ -0,0 +1,70 @@ +#!/usr/bin/perl + +my @lines = <>; +my $text = join('', @lines); +my $title; +if($text =~ /^\*\* (.*?)\n/m){ + $title = $1; + $text = $` . $'; +}else{ + $title = "Untitled"; +} + +$text =~ s/[ \t]+$//mg; +$text =~ s/^$/<br><br>/mg; +$text =~ s!\b([a-z0-9]+\.(c|s|pl|h))\b!<a href="src/$1.html">$1</a>!g; +$text =~ s!^(Lecture [0-9]+\. .*?)$!<b><i>$1</i></b>!mg; +$text =~ s!^\* (.*?)$!<h2>$1</h2>!mg; +$text =~ s!((<br>)+\n)+<h2>!\n<h2>!g; +$text =~ s!</h2>\n?((<br>)+\n)+!</h2>\n!g; +$text =~ s!((<br>)+\n)+<b>!\n<br><br><b>!g; +$text =~ s!\b\s*--\s*\b!\–!g; +$text =~ s!\[([^\[\]|]+) \| ([^\[\]]+)\]!<a href="$1">$2</a>!g; +$text =~ s!\[([^ \t]+)\]!<a href="$1">$1</a>!g; + +$text =~ s!``!\“!g; +$text =~ s!''!\”!g; + +print <<EOF; +<!-- AUTOMATICALLY GENERATED: EDIT the .txt version, not the .html version --> +<html> +<head> +<title>$title + + + +

    $title

    +

    +EOF +print $text; +print < + +EOF diff --git a/web/x86-intr.html b/web/x86-intr.html new file mode 100644 index 0000000..0369e25 --- /dev/null +++ b/web/x86-intr.html @@ -0,0 +1,53 @@ +Homework: xv6 and Interrupts and Exceptions + + + + + +

    Homework: xv6 and Interrupts and Exceptions

    + +

    +Read: xv6's trapasm.S, trap.c, syscall.c, vectors.S, and usys.S. Skim +lapic.c, ioapic.c, and picirq.c + +

    +Hand-In Procedure +

    +You are to turn in this homework during lecture. Please +write up your answers to the exercises below and hand them in to a +6.828 staff member at the beginning of the lecture. +

    + +Introduction + +

    Try to understand +xv6's trapasm.S, trap.c, syscall.c, vectors.S, and usys.S. Skim + You will need to consult: + +

    Chapter 5 of IA-32 Intel +Architecture Software Developer's Manual, Volume 3: System programming +guide; you can skip sections 5.7.1, 5.8.2, and 5.12.2. Be aware +that terms such as exceptions, traps, interrupts, faults and aborts +have no standard meaning. + +

    Chapter 9 of the 1987 i386 +Programmer's Reference Manual also covers exception and interrupt +handling in IA32 processors. + +

    Assignment: + +In xv6, set a breakpoint at the beginning of syscall() to +catch the very first system call. What values are on the stack at +this point? Turn in the output of print-stack 35 at that +breakpoint with each value labeled as to what it is (e.g., +saved %ebp for trap, +trapframe.eip, etc.). +

    +This completes the homework. + + + + + + + diff --git a/web/x86-intro.html b/web/x86-intro.html new file mode 100644 index 0000000..323d92e --- /dev/null +++ b/web/x86-intro.html @@ -0,0 +1,18 @@ +Homework: Intro to x86 and PC + + + + + +

    Homework: Intro to x86 and PC

    + +

    Today's lecture is an introduction to the x86 and the PC, the +platform for which you will write an operating system. The assigned +book is a reference for x86 assembly programming of which you will do +some. + +

    Assignment Make sure to do exercise 1 of lab 1 before +coming to lecture. + + + diff --git a/web/x86-mmu.html b/web/x86-mmu.html new file mode 100644 index 0000000..a83ff26 --- /dev/null +++ b/web/x86-mmu.html @@ -0,0 +1,33 @@ +Homework: x86 MMU + + + + + +

    Homework: x86 MMU

    + +

    Read chapters 5 and 6 of +Intel 80386 Reference Manual. +These chapters explain +the x86 Memory Management Unit (MMU), +which we will cover in lecture today and which you need +to understand in order to do lab 2. + +

    +Read: bootasm.S and setupsegs() in proc.c + +

    +Hand-In Procedure +

    +You are to turn in this homework during lecture. Please +write up your answers to the exercises below and hand them in to a +6.828 staff member by the beginning of lecture. +

    + +

    Assignment: Try to understand setupsegs() in proc.c. + What values are written into gdt[SEG_UCODE] + and gdt[SEG_UDATA] for init, the first user-space + process? + (You can use Bochs to answer this question.) + + diff --git a/web/x86-mmu1.pdf b/web/x86-mmu1.pdf new file mode 100644 index 0000000000000000000000000000000000000000..e7103e7f0241c9ba3fc9ed6a7512ae8d02da5a3b GIT binary patch literal 134308 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 literal 0 HcmV?d00001 diff --git a/web/x86-mmu2.pdf b/web/x86-mmu2.pdf new file mode 100644 index 0000000..e548148 --- /dev/null +++ b/web/x86-mmu2.pdf @@ -0,0 +1,55 @@ +%PDF-1.4 1 0 obj <> >> stream KFf<dp|n'G2tyJ\#菑‘Fa7Ll1њ"^+ aࠎG垼JY w&EBQ 2(09dQfԣjA.zX0r2Aw&9']Y*AɎ[ xr hEd'`_{Z D#:;IVb.D!U~PL 3L<\ๆ  @֝hqiմӭtOQ;wICړ|wA xzzpc/ [*">DQiu"4 {W T7ӹzAӚI_5lw3$XjZE[wluիwjdW_M5ShAB\ e`H"""""""#k%Ph#B#-"@He0AAaMS}'T§Y'D /(&ALɬ *H8TOr~};VK|W (Ku %JxPK %_ I/o($e.J~%jKL~AsfHk?_at04"""#Q|Dr/8-#Hk|h)(r `= Z&9>.ʕ s.!wxvL sK:Ȼ*o;@8z _-Zǵ~!{qr9fcf4'܆戎 48M?afPx_wM7q .({Z]/I_z .aIے{I + JD~>quZtx?__Y#_z_s:%s7_o}p׵`iXKm.kظ⽍0+jjWQV"{ojg4vA8@WM5U"""?21)Kz?w}c(E.Έ8""""#4r c!.ɹCr9F]Uy!͢%,n[o_^jzo __W%0@[;'3`1H*'SQd%JF~4?4vҼfh_Mzg _Kv~C %IL*-Hu P<7ZN$ +]$xY!ܸ({/Zt]RTگz T}M^ kzoҹg}ʙm?߫kJ"8׶ >v0G:8v=4-^*X hWqO3HGn`DE!WA=#w~솔VA7<\@ySYmpX^/W?~{/VlG.>97"8 c Aۣzok_~{]&3Gp3A|GM 5Y _?V_fq'3͑#5E#lˌ֎/DDDDDDDDD}m}:'zGe"G~Bio 2ЌSd]QoPv}^ cs1Z^a =pj]={p ͢78A_df<|NXg>EGNt ot}XCCh4 t?[?zAvkfǬHz&w=Hhc BUa<:WzPA7/<d_vqW A>D)vBw? 8)/isH.Ճ&Oȱ`ő)"^!Џ_:D6#!O??Rv}i U_o 7U91VҴl+,y ~g [68H p]>Q$q;iz+Wg~^AA; +a~[mm;>:_aA # ),0CkV +Q텆 H3KDGxzVLTS,4?w Pi;[4[ „""!(Mqkk^P֒^o~}TpL%|%:(W/[lXRl%Gz 㐎Bh% D%Ŏ]g"g f@a' 4yTEvs˛xdb \3L8JǹpC +! 0'TLz#H98_]KZ}/׿}_wG5DᠹuQ{^믻OUO^KuOG3:5",08atUnɃU"H0D:;C jB? dxqǔqx!07гp^`gxOՐ#_%] "#)YyEN3kW.N暈N_B9; ܇_@E{UՄ"",TsRׂ>&/N8K""#Boz+ A}ճϿ"էwk믯׾! ᄶ!qL}a$De1owk ;A= S ڪMi^Ԭʪ]%#FK@U0ԝw~#▓^k?8rzALY8epaU i}?\ kjm+ -bW_~W]s_1̏Fh0 +p """""""ykܫ?f-2 +Ng+Ymz(AeWRםB.l?,]hd)?"{tE/7(&pC*TeaN!`܆ +C Pzk"n3\5% dܖL/zXL N/Sa5Ysf-e%nZ=wD *' 'w}06uf"GD_OOՠ `|@'_Wtj /|[]uꩪH`}c}JK#1Kmk'nN2qDaik¯ Do.`8aai: (D$4 |CkI`B1P[OT?ִ?]k%B\k]$։|="x8Iux\!Q dZGJAK]5?=p}W ?K./oAi/Cw/|']PKׯ@kյmo^X'^v% +a) 1H8إ&DL*Ar7~{H.ӆ@%^ panT'VP*p<X!  +Þ"jb$""hDDDDD,NM~OA{ ~_%Kz]lR ܯt^a_h#/b#Ʒ2IaHa +h ` zA'wһZrk.ysr{a{~ [ T!إ~޿?_H"?1 n;ڦݵDwO7#m k/c\_t j?Uҿ~o4_%3+K__-~O[Kײ vjڶ]w9_+K҆ bwG> >llW_SVK~V޽M?"ui~׻28^ft ڦ&[_a;00/Xg|@4h!H~$^DDDDG"vu׆(%~5_EQ6-T- ׿c!x8̈́#r#pp   ya3Aqda^Z !`x0G\L[9s0]Ps{89w +@MY hpERX$$95 fC=DCY2dB Y˨4RsQ2S\h)J2rq) ռ"HgdޤE̋8 giY28 *#Ԝz&i2^IT^ו#_Ti}))ӑV { +l^Kx{T}4yq[#TDj3΄;1;8dsA4K|>'0S1 +ۇ>"F4gq=Oz502@ ̎VP.|] = `AA?_40  +&8}bL 4N-=:ӈi"c_TnWWqCx_'_Mi릩ߧ:Oz]LCd!iWw~Fܔr7N$g Q~)czFJ?#'Z"#X2;EB@DY"Ò%r,dw%=?O#Oy*@iG +?Z<'E/t ^kǥzzK?;EkQc#$kA?SwKUI/TD!ݿѠ?Ql#}G]^Bj=σ~ ]D路0넿_  7-_~.l7ᇯ"׿?ւikY:.uu AҐ࿾=4_:[N5dta-__rӽϙrz pA #:խGKAm! -ۆh[}W ,,?yd^H 0]tDl+-ֿa~Vcce  b-qߍگlcoӵw{WK}NY>궿ZnAD[ﰚ 'vDkdX®NPd 0Zb1HzdAި4^ 0@:2:j4!0Mavh՚Bh"hOQ"""""65f?V5հx/tJ@ KF?s7c`fiRUӍGT bl˃rt\ˊb9gto#qDmP w!9 ,rrxQ6"""""C$,r-0h9k DDDI9 1gNSHD*,!G(DDDDHcmnE4"""%`!tl GQ^).ق0M]TG!z(As#DDDX`;;3?pӳY#1vp9,KMM$e9}.]ʂMK\ǒ:_D 6#ă]( w i3š9F\G3SF /L! ac׈|E. 6. Ȏfc˖h߿ji(E_8;'ԂzH|?P=$(|0Ikv^4^~  H:OHh'.װȣOB\%~G K]uuUE̞x'ǒ- X*ڶa0i}/銏S[Q{DZ څL/a|DZu+ \!_0DDHDr+٦_ 91ɎCne aʂr +auG\(2 +ADDDDAs$ A 3TF W,a VȔF5&@́)ׯ_ y8 z!'|z}n}?__⾻ ׯ]O{4o߿X`/=VKֲ?\hqMtCvq +_ׯ]r};#_~sY]h뿵|fq)5HN +hDvxydr̠ڔ?3vPY ntxMM=m=? 0; a"g#>~T'kiX=[DYwDoylh5i;[>'oOt]^:U_ L?^?[>}D܄lݨ理Z(n>>yps"7_ ]BׯWq_x_蹯_ ҷ^iy~S_?;ܞب/_¶^kN\c}ҩֿ.XkkI}pm5 {z]E_'V[_ǻ dQ5i{c?C :'Aia8d~M?-NᓅdW]ᔝ#V]Sy78_]{t`_q__zy@gA&$NCj2âa﾿]}kZ3d(?o_^_>_y?Q޺߯S`֪u~_~ oxX/?___޿bɹݞd' H0c94eACМʖ5DDDDDDZ2:0@[DDDDDDAr +9>4P 's1—S0`B9i|DDDDDDDDV eـqGC0+<\4G,G B8d;+?jd"طj֔oZWޚvF+UӮU___^~_[U_ɰ@VE,50Lvԟ__߯}Ǒ3 Eԏ36}#S=,?/,w?4!58P@‚ p:|Cb3 0@~C#Óyᚣ_moOOOZN!ń AމwiJ=r''n˶HnEԛI 7P_[~wA<% $xASrxy;%nNRԼ?N'^uMzNqwt o}{V}i'[<߽d''Ԋc_~wq\WTG_xo0<_}B`@Tׯx7?]1W5`ofQ_;^oO?/9?A"&(R9}Ԝl~߿YW]^kK_;K2*j 5+]RMoD]{TC⡄ _ /L/Uô:_–: M /,4DDDGABPGTsq?b""Zn־IC+pVĂ]"7~-:32Ȝ`Y;63v;zAJZzQwMݭ=+VZSOk_i\:K߿ϑ c"z8fI$}  N_=k׻t0=rDa8)E ͦxf*}&}4~A[]>=m?K^[?녗O(ƉGN$; z$ $]5OxA*D?:I?L/K/"Ηu!ۆ)w { aqN=(uJrc}*(DDHd0Oa'B B_WY<"$Zߠ{u+"莈GFbQ|j=p_-C9C,DDDDz._8oڶZV޵S̏\W5 0ҭ_׈>*-6?n+ kjL +NdW0xi"8qaA T aW&Ȏ8|GPޙ}+ޞ O.eDy_oKvtdHg#f6!.)ppFr)n3_uUB"""""""""?1 qn11vlb.]3er8rGP'_˂%~-Ƒvy#iIp/=w]_׷iov1{h8#AgHψj"""rW帾vth2CL3oIDv?|e3Gٸ:|#/ F/q`Eat}4A?{[WyJ'N܎=K%{WL'n u_HҽYi{ǿo}P4#.F$?7ztypQ)s~ ߿p|~1eq|u4˂8R8`mo޺|on"""""#uuk zj6)8☯X+5]V44 U Aa2p 'ik"""?rGs25/n,V4a0;_'7;}ׯ֟P)9{o.˰<_B & ~5_Vȏ %TO2yJWQT_ jƷ_.pz~_iz 2A\d\Gn/DDDH\Ɏw"a?qgfGH. #Ad`/##qF #3`m zKb"""""""""""%NtHP#28#6g4ds>p KwQEm骾m2 ˵M4M Z_'qh_i4͚؆5a;֤#w# V xֿZQh_iU8dpi.dqHxB8s0eȜpsK|et莫} n9!܌s *G09m ԡ. +B/""""""#7=>rXr=܄vGGtj",GfG܎%^/"""""""#c_k_aP׿唴0vpS0wS"DfF /޺9R <.uzU-t].b%kIAChק~!g<4 >&j282Gh/0G gs”3fh BxNI} ~a wUNc}Or ektn7A)+8Gȣ%t =SUI7%} _IzN%x+8FG MZgzҽӤ@:.xk*%W@BCiJQDoR(;ԗ=k\׵(H{'/xA-#C^6> ,k`݂ٲl.x\q~$q}ݯ]GWnցaa uwz=;"dcK"4" Ј3& +CSh _@DDFǯ *ڃI/ݤt]`ݤ0Ď{V!;O,F#3ˑ40d")jŕfL qH)dn C'UM5 5TAPL&+^0PoRiތKփ˿/4/?wznr垓Yє?/7WD0XK_a/#W_:v]n/W(4N+K ZqmSZҽ _V_你 ſ]-kiZja|*M1 |0_1[H c^Mi * nDDDD4#⠶`2<]dpPG\3fl60fl/ gAdrr B+':~+@9a[(B B'DDD;)F7(j"Q{C @L>OgϿ}馱"#'~]~}]_ 5Uw]Z|/z?k_8櫪8+">vu,MsxACޝ:CԿzuQ>:@&y5Y &K=xzOO} +h3B On#hFeoIz_Kbo[uO_WZ^5E]x?}|{N/zkޞ'lt*B(o0%ĭĸ=oEw_O8AW8/Fb~u6 ~<-ĺ%p/_J_ '|z]rߜ@nW_>8[OW]|M~io=i[[ aov az._{ kK66858>`/O*ikmkLjZoڿQi4_~_\DI A hC/RӴ ^-b"""#>HKR%d;"w8AܝH}$]t_z& Kza=0'A?ǼV[~_u=oOL/kA_ӻH'Km& z__o_zך\]5?=#qk_oR,}u]oЈu}yjrsY^o}?bST(ˢ$ϟkikztmy6=} +wb߈qVDZ0# /vV +z+zc㊊"? 5k}ki5ah0M4a* SMߖ:AE~ jE^DDDDDDDDqDJx0hDEe0t Qk __ 7-Y*2)B?JSP.ЕsAA7׶Ǘ"|d7 00tiuH~x'̾:'O>=7_wIx>kth'@}c?ԈfYEB CT +F1O%ߤ"""#%b?վ3es@#B>x͑2>Gˇ#r>_#AumDDDDDDDDDDDDGCc߾U!^ 'pja #2w d鄾kL_[32e(.Y)Tm͑'~ yK򆟄IW33b4y!'dDk BADX M4 dqM?8|XCM*wڼ>'n^d%}a‘R7~HF_ N~6a~}c=?Z_WZN/ׅ'LQOAN娈O EQUn3 B8g#l}KՈcZE?^Zճ;A:ߙ߿¶?mv]Q1 &%^*;[aq_wqݭ{ 2VpkR(ki'"""""""""!fQ0MT""#WZk~pk:ȣ.Z0lZM_zߐlB8*28`PiDDDDD$l36289᜸ˌO]/1&Ў2]aDDxABD5{K_]rc;4~P?__:C$ ΌA C1-M4{w];ܜQ(wEN'A==>&OM_pc?/>o0˃Is.P28As/SB"""""""@1x*|\<7DDDDDD{3:Zyzzvn5"a4|ɀ#Hg^ڈxaXaaa_oM1LqWh8i i""" ?~_}nJD b5@B ͪdIO{oF8_VNxRr8nn>)8y0Zoa ![5WH}CoNO rWA̼D=t=? :^߽%`/'u#B"l/J DDDDH-9A4LX"?DGU_Y># \'eOa/Ct2VgB.ˑ̸Ⱦp4 pp#$3?֕T_εa+[_l.kkimo9tLC?᭯j)_L>zdG0@F4  `0""""""""?ܵ6td3gc*}]: Jds "n^<7]UT.!麬0;IR15~<  tK0/_REȐF 1a4>NІh^ kz?PuyǩyD XM¿ޞ إkAN?'M8)"Fi:ez݃{52—^6t_);#VC5ޡ=ZRW1.`q .\#\ +_zoҰw""""""?2$K[ +vi"*8b5]Bh4״M`0a3fX4DDDG_?n NFfΗwrޫI_a?}+߯dgI}hn/|@ٰAI=uT^9;?DХt%boK^?/0<)q{.fq1Ⱦtα(F5H:*?q!x ]r 9aɎR ʿ_daA,r!lX a=6i͈6vy/3r.2WTD6?a2 4'5Xpaזa? ߠ'6P2Af a{zM;kJ/\,$Z=O`6?ɧk#aS.2l5M \C˜3G b 9{"aL0I`r'aυHr2&‡+ +VynP'l""""""""""""""#"i=3Bc!G 9}ߙDDDDLr!̐)aˣ4`S#h p<4O~|>M}V~ ToU 9 \saܭ9CB+%O$ܪW'aI2:}r\CC.r=?|ZwzZ 쪟_( |+#(J8??(KQUo o{ӯKYةAIym?7kUwK" pZ$?}BEP zPUr ״W6?U,8׵8;}uA'ϙqٲ_qvq׽ ?c+{l{`_V + &߿vޫ{~7kv?{Hq;"A|0Bh05^; kkui{4p 0Av  {L-A{[Z_M+oA""""""""" q P2 2Q2p7ȯu ?p +Z"*""""#B""!4 A-T 4@ͤ4Pp8dq\C-& +%~""""""""""8#C + ֎Zڌ+8a5T-dXiZ7+L i#@DT(#GeCD ЈaDdAJ)τxנi޷-ߴ=+dJ~Zb:ⓞ=4 B5' }63< Ayf4Dvt gS5Ašҽ0zC3 d~Z[O'h4uHx5䞫%jv}WX^GG{D h2W_ zz +=x +]6BUP]5]-_7@׾ǹ%_}Xz'[~Xp<ԆXK ֈQl6/GHt6I{kB7|`|}:tIG[U j߸ l`~69+:{pޯQc CJ*?]׮k魯i#2O B}V~AQ> stream q 612.00 0 0 792.00 0.00 0.00 cm 0 g /Obj1 Do Q endstream endobj 3 0 obj << /Type /Pages /Kids [ 4 0 R ] /Count 1 >> endobj 4 0 obj << /Type /Page /MediaBox [ 0 0 612 792 ] /Parent 3 0 R /Rotate 0 /Resources << /ProcSet [/PDF /ImageC /ImageB /ImageI] /XObject << /Obj1 1 0 R >> >> /Contents [2 0 R ] >> endobj 5 0 obj << /Type /Catalog /Pages 3 0 R >> endobj 6 0 obj << /Creator (HP Digital Sending Device) /CreationDate () /Author () /Producer (HP Digital Sending Device) /Title () /Subject() >> endobj xref 0 7 0000000000 65535 f 0000000009 00000 n 0000025495 00000 n 0000025597 00000 n 0000025656 00000 n 0000025843 00000 n 0000025892 00000 n trailer << /Size 7 /Root 5 0 R /Info 6 0 R >> startxref 26037 %%EOF \ No newline at end of file diff --git a/web/xv6-disk.html b/web/xv6-disk.html new file mode 100644 index 0000000..65bcf8f --- /dev/null +++ b/web/xv6-disk.html @@ -0,0 +1,63 @@ + + +Homework: Files and Disk I/O + + + +

    Homework: Files and Disk I/O

    + +

    +Read: bio.c, fd.c, fs.c, and ide.c + +

    +This homework should be turned in at the beginning of lecture. + +

    +File and Disk I/O + +

    Insert a print statement in bwrite so that you get a +print every time a block is written to disk: + +

    +  cprintf("bwrite sector %d\n", sector);
    +
    + +

    Build and boot a new kernel and run these three commands at the shell: +

    +  echo >a
    +  echo >a
    +  rm a
    +  mkdir d
    +
    + +(You can try rm d if you are curious, but it should look +almost identical to rm a.) + +

    You should see a sequence of bwrite prints after running each command. +Record the list and annotate it with the calling function and +what block is being written. +For example, this is the second echo >a: + +

    +$ echo >a
    +bwrite sector 121  # writei  (data block)
    +bwrite sector 3    # iupdate (inode block)
    +$ 
    +
    + +

    Hint: the easiest way to get the name of the +calling function is to add a string argument to bwrite, +edit all the calls to bwrite to pass the name of the +calling function, and just print it. +You should be able to reason about what kind of +block is being written just from the calling function. + +

    You need not write the following up, but try to +understand why each write is happening. This will +help your understanding of the file system layout +and the code. + +

    +This completes the homework. + + diff --git a/web/xv6-intro.html b/web/xv6-intro.html new file mode 100644 index 0000000..3669866 --- /dev/null +++ b/web/xv6-intro.html @@ -0,0 +1,163 @@ +Homework: intro to xv6 + + + + + +

    Homework: intro to xv6

    + +

    This lecture is the introduction to xv6, our re-implementation of + Unix v6. Read the source code in the assigned files. You won't have + to understand the details yet; we will focus on how the first + user-level process comes into existence after the computer is turned + on. +

    + +Hand-In Procedure +

    +You are to turn in this homework during lecture. Please +write up your answers to the exercises below and hand them in to a +6.828 staff member at the beginning of lecture. +

    + +

    Assignment: +
    +Fetch and un-tar the xv6 source: + +

    +sh-3.00$ wget http://pdos.csail.mit.edu/6.828/2007/src/xv6-rev1.tar.gz 
    +sh-3.00$ tar xzvf xv6-rev1.tar.gz
    +xv6/
    +xv6/asm.h
    +xv6/bio.c
    +xv6/bootasm.S
    +xv6/bootmain.c
    +...
    +$
    +
    + +Build xv6: +
    +$ cd xv6
    +$ make
    +gcc -O -nostdinc -I. -c bootmain.c
    +gcc -nostdinc -I. -c bootasm.S
    +ld -N -e start -Ttext 0x7C00 -o bootblock.o bootasm.o bootmain.o
    +objdump -S bootblock.o > bootblock.asm
    +objcopy -S -O binary bootblock.o bootblock
    +...
    +$ 
    +
    + +Find the address of the main function by +looking in kernel.asm: +
    +% grep main kernel.asm
    +...
    +00102454 <mpmain>:
    +mpmain(void)
    +001024d0 <main>:
    +  10250d:       79 f1                   jns    102500 <main+0x30>
    +  1025f3:       76 6f                   jbe    102664 <main+0x194>
    +  102611:       74 2f                   je     102642 <main+0x172>
    +
    +In this case, the address is 001024d0. +

    + +Run the kernel inside Bochs, setting a breakpoint +at the beginning of main (i.e., the address +you just found). +

    +$ make bochs
    +if [ ! -e .bochsrc ]; then ln -s dot-bochsrc .bochsrc; fi
    +bochs -q
    +========================================================================
    +                       Bochs x86 Emulator 2.2.6
    +                    (6.828 distribution release 1)
    +========================================================================
    +00000000000i[     ] reading configuration from .bochsrc
    +00000000000i[     ] installing x module as the Bochs GUI
    +00000000000i[     ] Warning: no rc file specified.
    +00000000000i[     ] using log file bochsout.txt
    +Next at t=0
    +(0) [0xfffffff0] f000:fff0 (unk. ctxt): jmp far f000:e05b         ; ea5be000f0
    +(1) [0xfffffff0] f000:fff0 (unk. ctxt): jmp far f000:e05b         ; ea5be000f0
    +<bochs> 
    +
    + +Look at the registers and the stack contents: + +
    +<bochs> info reg
    +...
    +<bochs> print-stack
    +...
    +<bochs>
    +
    + +Which part of the stack printout is actually the stack? +(Hint: not all of it.) Identify all the non-zero values +on the stack.

    + +Turn in: the output of print-stack with +the valid part of the stack marked. Write a short (3-5 word) +comment next to each non-zero value explaining what it is. +

    + +Now look at kernel.asm for the instructions in main that read: +

    +  10251e:       8b 15 00 78 10 00       mov    0x107800,%edx
    +  102524:       8d 04 92                lea    (%edx,%edx,4),%eax
    +  102527:       8d 04 42                lea    (%edx,%eax,2),%eax
    +  10252a:       c1 e0 04                shl    $0x4,%eax
    +  10252d:       01 d0                   add    %edx,%eax
    +  10252f:       8d 04 85 1c ad 10 00    lea    0x10ad1c(,%eax,4),%eax
    +  102536:       89 c4                   mov    %eax,%esp
    +
    +(The addresses and constants might be different on your system, +and the compiler might use imul instead of the lea,lea,shl,add,lea sequence. +Look for the move into %esp). +

    + +Which lines in main.c do these instructions correspond to? +

    + +Set a breakpoint at the first of those instructions +and let the program run until the breakpoint: +

    +<bochs> vb 0x8:0x10251e
    +<bochs> s
    +...
    +<bochs> c
    +(0) Breakpoint 2, 0x0010251e (0x0008:0x0010251e)
    +Next at t=1157430
    +(0) [0x0010251e] 0008:0x0010251e (unk. ctxt): mov edx, dword ptr ds:0x107800 ; 8b1500781000
    +(1) [0xfffffff0] f000:fff0 (unk. ctxt): jmp far f000:e05b         ; ea5be000f0
    +<bochs> 
    +
    +(The first s command is necessary +to single-step past the breakpoint at main, otherwise c +will not make any progress.) +

    + +Inspect the registers and stack again +(info reg and print-stack). +Then step past those seven instructions +(s 7) +and inspect them again. +Convince yourself that the stack has changed correctly. +

    + +Turn in: answers to the following questions. +Look at the assembly for the call to +lapic_init that occurs after the +the stack switch. Where does the +bcpu argument come from? +What would have happened if main +stored bcpu +on the stack before those four assembly instructions? +Would the code still work? Why or why not? +

    + + + diff --git a/web/xv6-lock.html b/web/xv6-lock.html new file mode 100644 index 0000000..887022a --- /dev/null +++ b/web/xv6-lock.html @@ -0,0 +1,100 @@ +Homework: Locking + + + + + +

    Homework: Locking

    + + +

    +Read: spinlock.c + +

    +Hand-In Procedure +

    +You are to turn in this homework at the beginning of lecture. Please +write up your answers to the exercises below and hand them in to a +6.828 staff member at the beginning of lecture. +

    + +Assignment: +In this assignment we will explore some of the interaction +between interrupts and locking. +

    + +Make sure you understand what would happen if the kernel executed +the following code snippet: +

    +  struct spinlock lk;
    +  initlock(&lk, "test lock");
    +  acquire(&lk);
    +  acquire(&lk);
    +
    +(Feel free to use Bochs to find out. acquire is in spinlock.c.) +

    + +An acquire ensures interrupts are off +on the local processor using cli, +and interrupts remain off until the release +of the last lock held by that processor +(at which point they are enabled using sti). +

    + +Let's see what happens if we turn on interrupts while +holding the ide lock. +In ide_rw in ide.c, add a call +to sti() after the acquire(). +Rebuild the kernel and boot it in Bochs. +Chances are the kernel will panic soon after boot; try booting Bochs a few times +if it doesn't. +

    + +Turn in: explain in a few sentences why the kernel panicked. +You may find it useful to look up the stack trace +(the sequence of %eip values printed by panic) +in the kernel.asm listing. +

    + +Remove the sti() you added, +rebuild the kernel, and make sure it works again. +

    + +Now let's see what happens if we turn on interrupts +while holding the kalloc_lock. +In kalloc() in kalloc.c, add +a call to sti() after the call to acquire(). +You will also need to add +#include "x86.h" at the top of the file after +the other #include lines. +Rebuild the kernel and boot it in Bochs. +It will not panic. +

    + +Turn in: explain in a few sentences why the kernel didn't panic. +What is different about kalloc_lock +as compared to ide_lock? +

    +You do not need to understand anything about the details of the IDE hardware +to answer this question, but you may find it helpful to look +at which functions acquire each lock, and then at when those +functions get called. +

    + +(There is a very small but non-zero chance that the kernel will panic +with the extra sti() in kalloc. +If the kernel does panic, make doubly sure that +you removed the sti() call from +ide_rw. If it continues to panic and the +only extra sti() is in bio.c, +then mail 6.828-staff@pdos.csail.mit.edu +and think about buying a lottery ticket.) +

    + +Turn in: Why does release() clear +lock->pcs[0] and lock->cpu +before clearing lock->locked? +Why not wait until after? + + + diff --git a/web/xv6-names.html b/web/xv6-names.html new file mode 100644 index 0000000..926be3a --- /dev/null +++ b/web/xv6-names.html @@ -0,0 +1,78 @@ + + +Homework: Naming + + + +

    Homework: Naming

    + +

    +Read: namei in fs.c, fd.c, sysfile.c + +

    +This homework should be turned in at the beginning of lecture. + +

    +Symbolic Links + +

    +As you read namei and explore its varied uses throughout xv6, +think about what steps would be required to add symbolic links +to xv6. +A symbolic link is simply a file with a special type (e.g., T_SYMLINK +instead of T_FILE or T_DIR) whose contents contain the path being +linked to. + +

    +Turn in a short writeup of how you would change xv6 to support +symlinks. List the functions that would have to be added or changed, +with short descriptions of the new functionality or changes. + +

    +This completes the homework. + +

    +The following is not required. If you want to try implementing +symbolic links in xv6, here are the files that the course staff +had to change to implement them: + +

    +fs.c: 20 lines added, 4 modified
    +syscall.c: 2 lines added
    +syscall.h: 1 line added
    +sysfile.c: 15 lines added
    +user.h: 1 line added
    +usys.S: 1 line added
    +
    + +Also, here is an ln program: + +
    +#include "types.h"
    +#include "user.h"
    +
    +int
    +main(int argc, char *argv[])
    +{
    +  int (*ln)(char*, char*);
    +  
    +  ln = link;
    +  if(argc > 1 && strcmp(argv[1], "-s") == 0){
    +    ln = symlink;
    +    argc--;
    +    argv++;
    +  }
    +  
    +  if(argc != 3){
    +    printf(2, "usage: ln [-s] old new (%d)\n", argc);
    +    exit();
    +  }
    +  if(ln(argv[1], argv[2]) < 0){
    +    printf(2, "%s failed\n", ln == symlink ? "symlink" : "link");
    +    exit();
    +  }
    +  exit();
    +}
    +
    + + diff --git a/web/xv6-sched.html b/web/xv6-sched.html new file mode 100644 index 0000000..f8b8b31 --- /dev/null +++ b/web/xv6-sched.html @@ -0,0 +1,96 @@ +Homework: Threads and Context Switching + + + + + +

    Homework: Threads and Context Switching

    + +

    +Read: swtch.S and proc.c (focus on the code that switches +between processes, specifically scheduler and sched). + +

    +Hand-In Procedure +

    +You are to turn in this homework during lecture. Please +write up your answers to the exercises below and hand them in to a +6.828 staff member at the beginning of lecture. +

    +Introduction + +

    +In this homework you will investigate how the kernel switches between +two processes. + +

    +Assignment: +

    + +Suppose a process that is running in the kernel +calls sched(), which ends up jumping +into scheduler(). + +

    +Turn in: +Where is the stack that sched() executes on? + +

    +Turn in: +Where is the stack that scheduler() executes on? + +

    +Turn in: +When sched() calls swtch(), +does that call to swtch() ever return? If so, when? + +

    +Turn in: +Why does swtch() copy %eip from the stack into the +context structure, only to copy it from the context +structure to the same place on the stack +when the process is re-activated? +What would go wrong if swtch() just left the +%eip on the stack and didn't store it in the context structure? + +

    +Surround the call to swtch() in schedule() with calls +to cons_putc() like this: +

    +      cons_putc('a');
    +      swtch(&cpus[cpu()].context, &p->context);
    +      cons_putc('b');
    +
    +

    +Similarly, +surround the call to swtch() in sched() with calls +to cons_putc() like this: + +

    +  cons_putc('c');
    +  swtch(&cp->context, &cpus[cpu()].context);
    +  cons_putc('d');
    +
    +

    +Rebuild your kernel and boot it on bochs. +With a few exceptions +you should see a regular four-character pattern repeated over and over. +

    +Turn in: What is the four-character pattern? +

    +Turn in: The very first characters are ac. Why does +this happen? +

    +Turn in: Near the start of the last line you should see +bc. How could this happen? + +

    +This completes the homework. + + + + + + + + diff --git a/web/xv6-sleep.html b/web/xv6-sleep.html new file mode 100644 index 0000000..e712a40 --- /dev/null +++ b/web/xv6-sleep.html @@ -0,0 +1,100 @@ +Homework: sleep and wakeup + + + + + +

    Homework: sleep and wakeup

    + +

    +Read: pipe.c + +

    +Hand-In Procedure +

    +You are to turn in this homework at the beginning of lecture. Please +write up your answers to the questions below and hand them in to a +6.828 staff member at the beginning of lecture. +

    +Introduction +

    + +Remember in lecture 7 we discussed locking a linked list implementation. +The insert code was: + +

    +        struct list *l;
    +        l = list_alloc();
    +        l->next = list_head;
    +        list_head = l;
    +
    + +and if we run the insert on multiple processors simultaneously with no locking, +this ordering of instructions can cause one of the inserts to be lost: + +
    +        CPU1                           CPU2
    +       
    +        struct list *l;
    +        l = list_alloc();
    +        l->next = list_head;
    +                                       struct list *l;
    +                                       l = list_alloc();
    +                                       l->next = list_head;
    +                                       list_head = l;
    +        list_head = l;
    +
    + +(Even though the instructions can happen simultaneously, we +write out orderings where only one CPU is "executing" at a time, +to avoid complicating things more than necessary.) +

    + +In this case, the list element allocated by CPU2 is lost from +the list by CPU1's update of list_head. +Adding a lock that protects the final two instructions makes +the read and write of list_head atomic, so that this +ordering is impossible. +

    + +The reading for this lecture is the implementation of sleep and wakeup, +which are used for coordination between different processes executing +in the kernel, perhaps simultaneously. +

    + +If there were no locking at all in sleep and wakeup, it would be +possible for a sleep and its corresponding wakeup, if executing +simultaneously on different processors, to miss each other, +so that the wakeup didn't find any process to wake up, and yet the +process calling sleep does go to sleep, never to awake. Obviously this is something +we'd like to avoid. +

    + +Read the code with this in mind. + +

    +

    +Questions +

    +(Answer and hand in.) +

    + +1. How does the proc_table_lock help avoid this problem? Give an +ordering of instructions (like the above example for linked list +insertion) +that could result in a wakeup being missed if the proc_table_lock were not used. +You need only include the relevant lines of code. +

    + +2. sleep is also protected by a second lock, its second argument, +which need not be the proc_table_lock. Look at the example in ide.c, +which uses the ide_lock. Give an ordering of instructions that could +result in a wakeup being missed if the ide_lock were not being used. +(Hint: this should not be the same as your answer to question 2. The +two locks serve different purposes.)

    + +

    +This completes the homework. + + +