Support accurate short delays in the timer code, to speed up disk
[pintos-anon] / doc / tour.texi
index abbcb692b1f5f0e1b2890a7453d7beea762ecad6..e9ba008f89ef9a15843dac2875e423b3f1ceefb7 100644 (file)
@@ -1,4 +1,4 @@
-@node Pintos Tour
+@node Pintos Tour, Project 1--Threads, Introduction, Top
 @chapter A Tour Through Pintos
 
 This chapter is a brief tour through the Pintos code.  It covers the
 @chapter A Tour Through Pintos
 
 This chapter is a brief tour through the Pintos code.  It covers the
@@ -6,12 +6,25 @@ entire code base, but you'll only be using Pintos one part at a time,
 so you may find that you want to read each part as you work on the
 corresponding project.
 
 so you may find that you want to read each part as you work on the
 corresponding project.
 
+Hint: try using ``tags'' to follow along with references to function
+and variable names (@pxref{Tags}).
+
+@menu
+* Pintos Loading::              
+* Threads Tour::                
+@end menu
+
 @node Pintos Loading
 @section Loading
 
 This section covers the Pintos loader and basic kernel
 initialization.
 
 @node Pintos Loading
 @section Loading
 
 This section covers the Pintos loader and basic kernel
 initialization.
 
+@menu
+* Pintos Loader::               
+* Kernel Initialization::       
+@end menu
+
 @node Pintos Loader
 @subsection The Loader
 
 @node Pintos Loader
 @subsection The Loader
 
@@ -77,7 +90,7 @@ Then we jump to the start of the compiled kernel image.  Using the
 ``linker script'' in @file{threads/kernel.lds.S}, the kernel has
 arranged that it begins with the assembly module
 @file{threads/start.S}.  This assembly module just calls
 ``linker script'' in @file{threads/kernel.lds.S}, the kernel has
 arranged that it begins with the assembly module
 @file{threads/start.S}.  This assembly module just calls
-@code{main()}, which never returns.
+@func{main}, which never returns.
 
 There's one more trick to the loader: the Pintos kernel command line
 is stored in the boot loader.  The @command{pintos} program actually
 
 There's one more trick to the loader: the Pintos kernel command line
 is stored in the boot loader.  The @command{pintos} program actually
@@ -90,18 +103,20 @@ and effective.
 @node Kernel Initialization
 @subsection Kernel Initialization
 
 @node Kernel Initialization
 @subsection Kernel Initialization
 
-The kernel proper starts with the @code{main()} function.  The
-@code{main()} function is written in C, as will be most of the code we
+The kernel proper starts with the @func{main} function.  The
+@func{main} function is written in C, as will be most of the code we
 encounter in Pintos from here on out.
 
 encounter in Pintos from here on out.
 
-When @code{main()} starts, the system is in a pretty raw state.  We're
+When @func{main} starts, the system is in a pretty raw state.  We're
 in protected mode with paging enabled, but hardly anything else is
 in protected mode with paging enabled, but hardly anything else is
-ready.  Thus, the @code{main()} function consists primarily of calls
-into other Pintos modules' initialization functions, which are
-typically named @code{@var{module}_init()}, where @var{module} is the
-module's name.
+ready.  Thus, the @func{main} function consists primarily of calls
+into other Pintos modules' initialization functions.  You should
+notice that these are usually named @func{@var{module}_init}, where
+@var{module} is the module's name, @file{@var{module}.c} is the
+module's source code, and @file{@var{module}.h} is the module's
+header.
 
 
-First we initialize kernel RAM in @code{ram_init()}.  The first step
+First we initialize kernel RAM in @func{ram_init}.  The first step
 is to clear out the kernel's so-called ``BSS'' segment.  The BSS is a
 segment that should be initialized to all zeros.  In C, whenever you
 declare a variable outside a function without providing an
 is to clear out the kernel's so-called ``BSS'' segment.  The BSS is a
 segment that should be initialized to all zeros.  In C, whenever you
 declare a variable outside a function without providing an
@@ -109,12 +124,961 @@ initializer, that variable goes into the BSS.@footnote{This isn't
 actually required by the ANSI C standard, but it is the case with most
 implementations of C on most machines.}  Because it's all zeros, the
 BSS isn't stored in the image that the loader brought into memory.  We
 actually required by the ANSI C standard, but it is the case with most
 implementations of C on most machines.}  Because it's all zeros, the
 BSS isn't stored in the image that the loader brought into memory.  We
-just use @code{memset()} to zero it out.  The other task of
-@code{ram_init()} is to read out the machine's memory size from where
+just use @func{memset} to zero it out.  The other task of
+@func{ram_init} is to read out the machine's memory size from where
 the loader stored it and put it into the @code{ram_pages} variable for
 later use.
 
 the loader stored it and put it into the @code{ram_pages} variable for
 later use.
 
+Next, @func{thread_init} initializes the thread system.  We will defer
+full discussion to our discussion of Pintos threads below.  It is
+called so early in initialization because the console, initialized
+just below, tries to use locks and locks in turn require there to be a
+running thread.
+
+Then we initialize the console so that we can use @func{printf}.
+@func{main} calls @func{vga_init}, which initializes the VGA text
+display and clears the screen.  It also calls @func{serial_init_poll}
+to initialize the first serial port in ``polling mode,'' that is,
+where the kernel busy-waits for the port to be ready for each
+character to be output.  (We use polling mode until we're ready to set
+up interrupts later.)  Finally we initialize the console device and
+print a startup message to the console.
+
+@func{main} calls @func{argv_init} to parse the kernel command line.
+This entails reading the command line out of the loader and updating
+global variables appropriately.
+
+The next block of functions we call initialize the kernel's memory
+system.  @func{palloc_init} sets up the kernel page allocator, which
+doles out memory one or more pages at a time.  @func{malloc_init} sets
+up the allocator that handles odd-sized allocations.
+@func{paging_init} sets up a page table for the kernel.
+
+In projects 2 and later, @func{main} also calls @func{tss_init} and
+@func{gdt_init}, but we'll talk about those later.
+
+@func{main} calls @func{random_init} to initialize the kernel random
+number generator.  @func{argv_init} might already have done this (if
+the user specified @option{-rs} on the @command{pintos} command line)
+but calling it a second time is harmless and has no effect.
+
+We initialize the interrupt system in the next set of calls.
+@func{intr_init} sets up the CPU's @dfn{interrupt descriptor table}
+(IDT) to ready it for interrupt handling, then @func{timer_init} and
+@func{kbd_init} prepare us to handle timer interrupts and keyboard
+interrupts, respectively.  In projects 2 and later, we also prepare to
+handle interrupts caused by user programs using @func{exception_init}
+and @func{syscall_init}.
+
+Now that interrupts are set up, we can start preemptively scheduling
+threads with @func{thread_start}, which also enables interrupts.
+Interrupt-driven serial port I/O is also possible now, so we use
+@func{serial_init_queue} to switch to that mode.  Finally,
+@func{timer_calibrate} calibrates the timer for accurate short delays.
+
+If the filesystem is compiled in, as it will be in project 2 and
+later, we now initialize the disks with @func{disk_init}, then the
+filesystem with @func{filesys_init}, and run any operations that were
+requested on the kernel command line with @func{fsutil_run}.
 
 
+Boot is complete, so we print a message.
+
+For project 1, now we execute @func{test}, which runs whatever test is
+currently compiled into the kernel.  For later projects, we will
+instead run a user program and wait for it to complete.
+
+Finally, if @option{-q} was specified on the kernel command line, we
+call @func{power_off} to terminate the machine simulator.  Otherwise,
+@func{main} calls @func{thread_exit}, which allows any other running
+threads to continue running.
 
 @node Threads Tour
 @section Threads
 
 @node Threads Tour
 @section Threads
+
+@menu
+* struct thread::               
+* Thread Functions::            
+* Thread Switching::            
+* Synchronization::             
+* Interrupt Handling::          
+* Memory Allocation::           
+@end menu
+
+@node struct thread
+@subsection @code{struct thread}
+
+The main Pintos data structure for threads is @struct{thread},
+declared in @file{threads/thread.h}.  @struct{thread} has these
+members with the given type:
+
+@table @code
+@item tid_t tid;
+The thread's thread identifier or @dfn{tid}.  Every thread must have a
+tid that is unique over the entire lifetime of the kernel.  By
+default, @code{tid_t} is a @code{typedef} for @code{int} and each new
+thread receives the numerically next higher tid, starting from 1 for
+the initial process.  You can change the type and the numbering scheme
+if you like.
+
+@item enum thread_status status;
+The thread's state, one of these:
+
+@table @code
+@item THREAD_RUNNING
+The thread is running.  Exactly one thread is running at a given time.
+@func{thread_current} returns the running thread.
+
+@item THREAD_READY
+The thread is ready to run, but it's not running right now.  The
+thread could be selected to run the next time the scheduler is
+invoked.  Ready threads are kept in a doubly linked list called
+@code{ready_list}.
+
+@item THREAD_BLOCKED
+The thread is waiting for something, e.g.@: a lock to become
+available, an interrupt to be invoked.  The thread won't be scheduled
+again until it transitions to the @code{THREAD_READY} state with a
+call to @func{thread_unblock}.
+
+@item THREAD_DYING
+The thread will be destroyed by the scheduler after switching to the
+next thread.
+@end table
+
+@item char name[16];
+The thread's name as a string, or at least the first few characters of
+it.
+
+@item uint8_t *stack;
+Every thread has its own stack to keep track of its state.  When the
+thread is running, the CPU's stack pointer register tracks the top of
+the stack and this member is unused.  But when the CPU switches to
+another thread, this member saves the thread's stack pointer.  No
+other members are needed to save the thread's registers, because the
+other registers that must be saved are saved on the stack.
+
+@item int priority;
+A thread priority, ranging from the lowest possible priority
+@code{PRI_MIN} (0) to the highest possible priority @code{PRI_MAX}
+(59).  Pintos as provided ignores thread priorities, but you will
+implement priority scheduling in problem 1-3 (@pxref{Problem 1-3
+Priority Scheduling}).
+
+@item list_elem elem;
+A ``list element'' used to put the thread into doubly linked lists,
+either the list of threads ready to run or a list of threads waiting
+on a semaphore.  Take a look at @file{lib/kernel/list.h} for
+information on how to use the Pintos doubly linked list ADT.
+
+@item uint32_t *pagedir;
+Only present in project 2 and later.
+
+@item unsigned magic;
+Always set to @code{THREAD_MAGIC}, which is just a random number
+defined in @file{threads/thread.c}, and used to detect stack overflow.
+See below for more information.
+@end table
+
+Every @struct{thread} occupies the beginning of its own page of
+memory.  The rest of the page is used for the thread's stack, which
+grows downward from the end of the page.  It looks like this:
+
+@example
+@group
+        4 kB +---------------------------------+
+             |          kernel stack           |
+             |                |                |
+             |                |                |
+             |                V                |
+             |         grows downward          |
+             |                                 |
+             |                                 |
+             |                                 |
+             |                                 |
+             |                                 |
+             |                                 |
+             |                                 |
+             |                                 |
+             +---------------------------------+
+             |              magic              |
+             |                :                |
+             |                :                |
+             |               name              |
+             |              status             |
+        0 kB +---------------------------------+
+@end group
+@end example
+
+The upshot of this is twofold.  First, @struct{thread} must not be
+allowed to grow too big.  If it does, then there will not be enough
+room for the kernel stack.  Our base @struct{thread} is only a few
+bytes in size.  It probably should stay well under 1 kB.
+
+Second, kernel stacks must not be allowed to grow too large.  If a
+stack overflows, it will corrupt the thread state.  Thus, kernel
+functions should not allocate large structures or arrays as non-static
+local variables.  Use dynamic allocation with @func{malloc} or
+@func{palloc_get_page} instead.
+
+This brings us to the purpose of @struct{thread}'s @code{magic}
+member.  If a thread's stack does overflow, then @code{magic} will be
+the first member of @struct{thread} to be overwritten, because it is
+closest to the kernel stack.  Thus, some of the thread functions
+(notably @func{thread_current}) check that @code{magic} has the proper
+value.
+
+@node Thread Functions
+@subsection Thread Functions
+
+@file{threads/thread.c} implements several public functions for thread
+support.  Let's take a look at the most useful:
+
+@deftypefun void thread_init (void)
+Called by @func{main} to initialize the thread system.  Its main
+purpose is to create a @struct{thread} for Pintos's initial thread.
+This is possible because the Pintos loader sets up the initial
+thread's stack at the end of a page.  Before @func{thread_init} runs,
+@func{thread_current} will fail because the running thread's
+@code{magic} value is incorrect.  Lots of functions call
+@func{thread_current} directly or indirectly, including
+@func{lock_acquire} for locking a lock, so @func{thread_init} is
+called early in Pintos initialization.
+@end deftypefun
+
+@deftypefun void thread_start (void)
+Called by @func{main} to start the scheduler.  Creates the idle
+thread, that is, the thread that is scheduled when no other thread is
+ready.  Then enables interrupts, which enables the scheduler because
+processes are rescheduled on return from the timer interrupt, using
+@func{intr_yield_on_return} (@pxref{External Interrupt Handling}).
+@end deftypefun
+
+@deftypefun void thread_create (const char *@var{name}, int @var{priority}, thread_func *@var{func}, void *@var{aux})
+Creates and starts a new thread named @var{name} with the given
+@var{priority}, returning the new thread's tid.  The thread executes
+@var{func}, passing @var{aux} as the function's single argument.
+
+@func{thread_create} works by allocating a page for the thread's
+@struct{thread} and stack and initializing its members, then setting
+up a set of fake stack frames for it (we'll talk more about this
+later).  The thread was initialized in the blocked state, so the final
+action before returning is to unblock it, allowing the new thread to
+be scheduled.
+@end deftypefun
+
+@deftypefun void thread_block (void)
+Transitions the running thread from the running state to the blocked
+state.  This thread will not run again until @func{thread_unblock} is
+called on it, so you'd better have some way arranged for that to
+happen.
+@end deftypefun
+
+@deftypefun void thread_unblock (struct thread *@var{thread})
+Transitions @var{thread}, which must be in the blocked state, to the
+ready state, allowing it to continue to run.  This is called when the
+event that the thread is waiting for happens, such as the lock that
+the thread is waiting on becoming available.
+@end deftypefun
+
+@deftypefun struct thread *thread_current (void)
+Returns the running thread.
+@end deftypefun
+
+@deftypefun void thread_exit (void) NO_RETURN
+Causes the current thread to exit.  Never returns (hence
+@code{NO_RETURN}: @pxref{UNUSED NO_RETURN NO_INLINE PRINTF_FORMAT}).
+@end deftypefun
+
+@deftypefun void thread_yield (void)
+Yields the CPU to the scheduler, which picks a new thread to run.  The
+new thread might be the current thread, so you can't depend on this
+function to keep this thread from running for any particular length of
+time.
+@end deftypefun
+
+@node Thread Switching
+@subsection Thread Switching
+
+@func{schedule} is the function responsible for switching threads.  It
+is internal to @file{threads/thread.c} and called only by the three
+public thread functions that need to switch threads:
+@func{thread_block}, @func{thread_exit}, and @func{thread_yield}.
+Before these functions call @func{schedule}, they all disable
+interrupts (or ensure that they are already disabled) and then change
+the running thread's state to something other than running.
+
+The actual @func{schedule} implementation is simple.  It records the
+current thread in local variable @var{cur}, determines the next thread
+to run as local variable @var{next} (by calling
+@func{next_thread_to_run}, and then calls @func{switch_threads} to do
+the actual thread switch.  The thread we switched to was also running
+inside @func{switch_threads}, as are all the threads not currently
+running in Pintos, so the new thread now returns out of
+@func{switch_threads}, returning the previously running thread.
+
+@func{switch_threads} is an assembly language routine in
+@file{threads/switch.S}.  It saves registers on the stack, stores the
+CPU's current stack pointer into the current @struct{thread}'s @code{stack}
+member, restores the new thread's @code{stack} into the CPU's stack
+pointer, restores registers from the stack, and returns.
+
+The rest of the scheduler is implemented as @func{schedule_tail}.  It
+marks the new thread as running.  If the thread we just switched from
+is in the dying state, then it also frees the page that contained the
+dying thread's @struct{thread} and stack, which couldn't be freed
+before the thread switch because the switch needed to use it.
+
+Running a thread for the first time is a special case.  When
+@func{thread_create} creates a new thread, it goes through a fair
+amount of trouble to get it started properly.  In particular, a new
+thread hasn't started running yet, so there's no way for it to be
+running inside @func{switch_threads} as the scheduler expects.  To
+solve the problem, @func{thread_create} creates some fake stack frames
+in the new thread's stack:
+
+@itemize @bullet
+@item
+The topmost fake stack frame is for @func{switch_threads}, represented
+by @struct{switch_threads_frame}.  The important part of this frame is
+its @code{eip} member, the return address.  We point @code{eip} to
+@func{switch_entry}.
+
+@item
+The next fake stack frame is for @func{switch_entry}, an assembly
+language routine in @file{threads/switch.S} that adjusts the stack
+pointer,@footnote{This is because @func{switch_threads} takes
+arguments on the stack and the 80@var{x}86 SVR4 calling convention
+requires the caller, not the called function, to remove them when the
+call is complete.  See @bibref{SysV-i386} chapter 3 for details.}
+calls @func{schedule_tail} (this special case is why
+@func{schedule_tail} is separate from @func{schedule}), and returns.
+We fill in its stack frame so that it returns into
+@func{kernel_thread}, a function in @file{threads/thread.c}.
+
+@item
+The final stack frame is for @func{kernel_thread}, which enables
+interrupts and calls the thread's function (the function passed to
+@func{thread_create}).  If the thread's function returns, it calls
+@func{thread_exit} to terminate the thread.
+@end itemize
+
+@node Synchronization
+@subsection Synchronization
+
+Situations often arise in threaded programs in which threads want to
+share resources.  If resource sharing is not handled in a careful,
+controlled fashion, then threads can end up making a big mess.
+This is especially the case in operating system kernels, where
+faulty sharing can crash the entire machine.  Pintos provides several
+synchronization primitives to help out.
+
+@menu
+* Disabling Interrupts::        
+* Semaphores::                  
+* Locks::                       
+* Condition Variables::         
+@end menu
+
+@node Disabling Interrupts
+@subsubsection Disabling Interrupts
+
+The crudest way to do synchronization is to disable interrupts, that
+is, to temporarily prevent the CPU from responding to interrupts.  If
+interrupts are off, no other thread will preempt the running thread,
+because thread preemption is driven by the timer interrupt.  If
+interrupts are on, as they normally are, then the running thread may
+be preempted by another at any time, whether between two C statements
+or even within the execution of one.
+
+Incidentally, this means that Pintos is a ``preemptible kernel,'' that
+is, kernel threads can be preempted at any time.  Traditional Unix
+systems are ``nonpreemptible,'' that is, kernel threads can only be
+preempted at points where they explicitly call into the scheduler.
+User programs can be preempted at any time in both models.  As you
+might imagine, preemptible kernels require more use of
+synchronization.
+
+You should have little need to set the interrupt state directly.  Most
+of the time you should use the other synchronization primitives
+described in the following sections.
+
+Types and functions for disabling and enabling interrupts are in
+@file{threads/interrupt.h}.
+
+@deftp Type {enum intr_level}
+One of @code{INTR_OFF} or @code{INTR_ON}, denoting that interrupts are
+disabled or enabled, respectively.
+@end deftp
+
+@deftypefun {enum intr_level} intr_get_level (void)
+Returns the current interrupt state.
+@end deftypefun
+
+@deftypefun {enum intr_level} intr_set_level (enum intr_level @var{level})
+Turns interrupts on or off according to @var{level} and returns the
+previous interrupt state.
+@end deftypefun
+
+@deftypefun {enum intr_level} intr_enable (void)
+Turns interrupts on and returns the previous interrupt state.
+@end deftypefun
+
+@deftypefun {enum intr_level} intr_disable (void)
+Turns interrupts off and returns the previous interrupt state.
+@end deftypefun
+
+@node Semaphores
+@subsubsection Semaphores
+
+A semaphore is a nonnegative integer along with two atomic operators
+for manipulating it, which are:
+
+@itemize @bullet
+@item
+``Down'' or ``P'': wait for the value to become positive, then
+decrement it.
+
+@item
+``Up'' or ``V'': increment the value (and wake up one waiting thread,
+if any).
+@end itemize
+
+A semaphore initialized to 1 is typically used for controlling access
+to a resource.  Before a block of code starts using the resource, it
+``downs'' the semaphore, then after it is done with the resource it
+``ups'' the resource.  In such a case a lock, described below, may be
+more appropriate.
+
+A semaphore initialized to 0 can be useful for waiting for an event
+that will happen exactly once.  For example, suppose thread @var{A}
+starts another thread @var{B} and wants to wait for @var{B} to signal
+that some activity is complete.  @var{A} can create a semaphore
+initialized to 0, pass it to @var{B}, and then ``down'' the semaphore.
+When @var{B} finishes its activity, it ``ups'' the semaphore.
+
+Pintos declared its semaphore type and operations on them in
+@file{threads/synch.h}.
+
+@deftp {Type} {struct semaphore}
+Represents a semaphore.
+@end deftp
+
+@deftypefun void sema_init (struct semaphore *sema, unsigned value, const char *name)
+Initializes @var{sema} as a new semaphore with the given initial
+@var{value}.  Gives @var{sema} the specified @var{name} for use in
+debugging.
+@end deftypefun
+
+@deftypefun void sema_down (struct semaphore *@var{sema})
+Executes the ``down'' or ``P'' operation on the semaphore, waiting for
+its value to become positive and then decrementing it by one.
+@end deftypefun
+
+@deftypefun void sema_up (struct semaphore *@var{sema})
+Increments @var{sema}'s value.  If any threads are waiting on
+@var{sema}, wakes one of them up.
+@end deftypefun
+
+Semaphores are internally built out of interrupt disabling
+(@pxref{Disabling Interrupts}), thread blocking and unblocking
+(@func{thread_block} and @func{thread_unblock}).  Semaphores maintain
+a doubly linked list of waiting threads using the linked list
+implementation in @file{lib/kernel/list.c}.
+
+@node Locks
+@subsubsection Locks
+
+A lock is a specialization of a semaphore (@pxref{Semaphores}) with an
+initial value of 1.  The difference between a lock and such a
+semaphore is twofold.  First, a semaphore can have a value greater
+than 1, but a lock can only be owned by a single thread at a time.
+Second, a semaphore does not have an owner, meaning that one thread
+can ``down'' the semaphore and then another one ``up'' it, but with a
+lock the same thread must both acquire and release it.  When these
+restrictions prove onerous, it's a good sign that a semaphore should
+be used, instead of a lock.
+
+Locks in Pintos are not ``recursive,'' that is, it is an error for the
+thread currently holding a lock to try to acquire that lock.
+
+Lock types and functions are declared in @file{threads/synch.h}.
+
+@deftp {Type} {struct lock}
+Represents a lock.
+@end deftp
+
+@deftypefun void lock_init (struct lock *@var{lock}, const char *@var{name})
+Initializes @var{lock} as a new lock and gives it the specified
+@var{name} for use in debugging.
+@end deftypefun
+
+@deftypefun void lock_acquire (struct lock *@var{lock})
+Acquires @var{lock} for use by the current thread, first waiting for
+any current owner to release it if necessary.
+@end deftypefun
+
+@deftypefun void lock_release (struct lock *@var{lock})
+Releases @var{lock}, which the current thread must own.
+@end deftypefun
+
+@deftypefun bool lock_held_by_current_thread (const struct lock *@var{lock})
+Returns true if the running thread owns @var{lock},
+false otherwise.
+@end deftypefun
+
+@node Condition Variables
+@subsubsection Condition Variables
+
+A condition variable allows one piece of code to signal a condition
+and cooperating code to receive the signal and act upon it.  Each
+condition variable is associated with a lock.  A given condition
+variable is associated with only a single lock, but one lock may be
+associated with any number of condition variables.  A lock along with
+all of its condition variables is known as a ``monitor.''
+
+A thread that owns the monitor lock is said to be ``in the monitor.''
+The thread in the monitor has control over all the data protected with
+the lock.  It may freely examine or modify this data.  If it discovers
+that it needs to wait for some condition to become true, then it
+``waits'' on the associated condition, which releases the lock and
+waits for the condition to be signaled.  If it, on the other hand, has
+caused one of these conditions to become true, it ``signals'' the
+condition to wake up one waiter, or ``broadcasts'' the condition to
+wake all of them.
+
+The classical example of a monitor is handling a buffer into which one
+``producer'' thread writes characters and out of which a second
+``consumer'' thread reads characters.  To implement this case we need,
+besides the monitor lock, two condition variables which we will call
+@var{not_full} and @var{not_empty}:
+
+@example
+char buf[BUF_SIZE];     /* @r{Buffer.} */
+size_t n = 0;           /* @r{0 <= n <= @var{BUF_SIZE}: # of characters in buffer.} */
+size_t head = 0;        /* @r{@var{buf} index of next char to write (mod @var{BUF_SIZE}).} */
+size_t tail = 0;        /* @r{@var{buf} index of next char to read (mod @var{BUF_SIZE}).} */
+struct lock lock;       /* @r{Monitor lock.} */
+struct condition not_empty; /* @r{Signaled when the buffer is not empty.} */
+struct condition not_full; /* @r{Signaled when the buffer is not full.} */
+
+@dots{}@r{initialize the locks and condition variables}@dots{}
+
+void put (char ch) @{
+  lock_acquire (&lock);
+  while (n == BUF_SIZE)         /* @r{Can't add to @var{buf} as long as it's full.} */
+    cond_wait (&not_full);
+  buf[head++ % BUF_SIZE] = ch;  /* @r{Add @var{ch} to @var{buf}.} */
+  n++;
+  cond_signal (&not_empty);     /* @r{@var{buf} can't be empty anymore.} */
+  lock_release (&lock);
+@}
+
+char get (void) @{
+  char ch;
+  lock_acquire (&lock);
+  while (n == 0)                /* @r{Can't read from @var{buf} as long as it's empty.} */
+    cond_wait (&not_empty);
+  ch = buf[tail++ % BUF_SIZE];  /* @r{Get @var{ch} from @var{buf}.} */
+  n--;
+  cond_signal (&not_full);      /* @r{@var{buf} can't be full anymore.} */
+  lock_release (&lock);
+@}
+@end example
+
+As the code above illustrates, Pintos monitors are ``Mesa'' style, not
+``Hoare'' style, that is, sending and receiving a signal are not an
+atomic operation.  Thus, typically the caller must recheck the
+condition after the wait completes and, if necessary, wait again.
+
+Condition variable types and functions are declared in
+@file{threads/synch.h}.
+
+@deftp {Type} {struct condition}
+Represents a condition variable.
+@end deftp
+
+@deftypefun void cond_init (struct condition *@var{cond}, const char *@var{name})
+Initializes @var{cond} as a new condition variable and gives it the
+specified @var{name} for use in debugging.
+@end deftypefun
+
+@deftypefun void cond_wait (struct condition *@var{cond}, struct lock *@var{name})
+Atomically releases @var{lock} (the monitor lock) and waits for
+@var{cond} to be signaled by some other piece of code.  After
+@var{cond} is signaled, @var{lock} is reacquired before returning.
+@var{lock} must be held before calling this function.
+@end deftypefun
+
+@deftypefun void cond_signal (struct condition *@var{cond}, struct lock *@var{lock})
+If any threads are waiting on @var{cond} (protected by monitor lock
+@var{lock}), then this function signals one of them to wake up from
+its wait.  @var{lock} must be held before calling this function.
+@end deftypefun
+
+@deftypefun void cond_broadcast (struct condition *@var{cond}, struct lock *@var{lock})
+Wakes up all threads, if any, waiting on @var{cond} (protected by
+monitor lock @var{lock}).  @var{lock} must be held before calling this
+function.
+@end deftypefun
+
+@node Interrupt Handling
+@subsection Interrupt Handling
+
+An @dfn{interrupt} notifies the CPU of some event.  Much of the work
+of an operating system relates to interrupts in one way or another.
+For our purposes, we classify interrupts into two broad categories:
+
+@itemize @bullet
+@item
+@dfn{External interrupts}, that is, interrupts originating outside the
+CPU.  These interrupts come from hardware devices such as the system
+timer, keyboard, serial ports, and disks.  External interrupts are
+@dfn{asynchronous}, meaning that they don't occur in a fashion
+synchronized with anything going on in the CPU.  External interrupts
+are what @func{intr_disable} and related functions can arrange to
+temporarily ignore (@pxref{Disabling Interrupts}).
+
+@item
+@dfn{Internal interrupts}, that is, interrupts caused by something
+executing on the CPU.  These interrupts are caused by something
+unusual happening during instruction execution: accessing invalid
+memory (a @dfn{page fault}), executing invalid instructions, and
+various other disallowed activities.  Because they are caused by CPU
+instructions, internal interrupts are @dfn{synchronous} or
+synchronized with CPU instructions.  @func{intr_disable} does not
+disable internal interrupts.
+@end itemize
+
+Because the CPU treats all interrupts largely the same way, regardless
+of source, Pintos uses the same infrastructure for both internal and
+external interrupts, up to a point.  The next section describes this
+common infrastructure.  But external and internal interrupts are
+actually quite different, so we also follow up that section with one
+specific to each category.
+
+If you haven't already read chapter 3 in @bibref{IA32-v1}, it is
+recommended that you do so now.  You might also want to skim chapter 5
+in @bibref{IA32-v3}.
+
+@menu
+* Interrupt Infrastructure::    
+* Internal Interrupt Handling::  
+* External Interrupt Handling::  
+@end menu
+
+@node Interrupt Infrastructure
+@subsubsection Interrupt Infrastructure
+
+When an interrupt occurs while the kernel is running, the CPU saves
+its most essential state on the stack and jumps to an interrupt
+handler routine.  The 80@var{x}86 architecture allows for 256 possible
+interrupts, each of which can have its own handler. The handler for
+each interrupt is defined in an array called the @dfn{interrupt
+descriptor table} or IDT.
+
+In Pintos, @func{intr_init} in @file{threads/interrupt.c} sets up the
+IDT so that each entry points to a unique entry point in
+@file{threads/intr-stubs.S} named @func{intr@var{NN}_stub}, where
+@var{NN} is the interrupt number in
+hexadecimal.@footnote{@file{threads/intr-stubs.S} is so repetitive
+that it is actually generated by a Perl script,
+@file{threads/intr-stubs.pl}.  Thus, you will actually find
+@file{threads/intr-stubs.S} in your @file{threads/build/threads}
+directory, not in plain @file{threads}.}  Because the CPU doesn't give
+us any other way to find out the interrupt number, this entry point
+pushes the interrupt number on the stack.  Then it jumps to
+@func{intr_entry}, which pushes all the registers that the processor
+didn't save for us, and then calls @func{intr_handler}, which
+brings us back into C in @file{threads/interrupt.c}.
+
+The main job of @func{intr_handler} is to call any function that has
+been registered for handling the particular interrupt.  (If there's no
+function registered, it dumps some information to the console and
+panics the kernel.)  It does some extra processing for external
+interrupts that we'll discuss later.
+
+When @func{intr_handler} returns, the assembly code in
+@file{threads/intr-stubs.S} restores all the CPU registers saved
+earlier and directs the CPU to return from the interrupt.
+
+A few types and functions apply to both internal and external
+interrupts.
+
+@deftypefun void intr_register (uint8_t @var{vec}, int @var{dpl}, enum intr_level @var{level}, intr_handler_func *@var{func}, const char *@var{name})
+Registers @var{func} to be called when the interrupt numbered
+@var{vec} is triggered.  Names the interrupt @var{name} for debugging
+purposes.
+
+If @var{level} is @code{INTR_OFF} then handling of further interrupts
+will be disabled while the interrupt is being processed.  If @var{vec}
+denotes an external interrupt, then @var{level} must be
+@code{INTR_OFF}.  Otherwise, interrupts should normally be turned on
+during the handling of an internal interrupt.
+
+For internal interrupts, @var{dpl} determines how the interrupt can be
+invoked.  If @var{dpl} is 0, then the interrupt can be invoked only by
+kernel threads.  Otherwise @var{dpl} should be 3, which allows user
+processes to invoke the interrupt as well (this is useful only
+starting with project 2).  @var{dpl} has no effect on external
+interrupts
+@end deftypefun
+
+@deftp {Type} {void intr_handler_func (struct intr_frame *@var{frame})}
+This is the type of an interrupt handler function.  Its @var{frame}
+argument (see below) allows it to determine the cause of the interrupt
+and the state of the thread that was interrupted.
+@end deftp
+
+@deftp {Type} {struct intr_frame}
+The stack frame of an interrupt handler.  Its most interesting members
+are as follows:
+@table @code
+@item uint32_t edi;
+@item uint32_t esi;
+@item uint32_t ebp;
+@item uint32_t esp_dummy;
+@item uint32_t ebx;
+@item uint32_t edx;
+@item uint32_t ecx;
+@item uint32_t eax;
+@item uint16_t es;
+@item uint16_t ds;
+Register values in the interrupted thread saved by @func{intr_entry}.
+The @code{esp_dummy} value isn't actually used (refer to the
+description of @code{PUSHA} in @bibref{IA32-v2b} for details).
+
+@item uint32_t vec_no;
+The interrupt vector number, ranging from 0 to 255.
+
+@item uint32_t error_code;
+The ``error code'' pushed on the stack by the CPU for some internal
+interrupts.
+
+@item void (*eip) (void);
+The address of the next instruction to be executed by the interrupted
+thread. 
+
+@item void *esp;
+The interrupted thread's stack pointer.
+@end table
+@end deftp
+
+@deftypefun {const char *} intr_name (uint8_t @var{vec})
+Returns the registered name of the interrupt numbered @var{vec}, or
+@code{"unknown"} if the interrupt's name is not known.
+@end deftypefun
+
+@node Internal Interrupt Handling
+@subsubsection Internal Interrupt Handling
+
+When an internal interrupt occurs, it is because the running kernel
+thread (or, starting from project 2, the running user process) has
+caused it.  Thus, because it is related to a thread (or process), an
+internal interrupt is said to happen in a ``process context.''
+
+In an internal interrupt, it can make sense to examine the
+@struct{intr_frame} passed to the interrupt handler.  In cases where
+the interrupted thread intentionally caused the interrupt, it can even
+make sense to modify it.  When the interrupt returns, modified members
+in @struct{intr_frame} become changes to the thread's registers.
+We'll use this in project 2 to return values from system call
+handlers.
+
+There are no special restrictions on what an internal interrupt
+handler can or can't do.  Generally they should run with interrupts
+enabled, just like other code, and so they can be preempted by other
+kernel threads.  Thus, they do need to synchronize with other threads
+on shared data and other resources (@pxref{Synchronization}).
+
+@node External Interrupt Handling
+@subsubsection External Interrupt Handling
+
+Whereas an internal interrupt runs in the context of the thread that
+caused it, external interrupts do not have any predictable context.
+They are asynchronous, so it can be invoked at any time that
+interrupts have not been enabled.  We say that an external interrupt
+runs in an ``interrupt context.''
+
+In an external interrupt, the @struct{intr_frame} passed to the
+handler is not very meaningful.  It describes the state of the thread
+or process that was interrupted, but there is no way to predict which
+one that is.  It is possible, though rarely useful, to examine it, but
+modifying it is a recipe for disaster.
+
+The activities of an external interrupt handler are severely
+restricted.  First, only one external interrupt may be processed at a
+time, that is, nested external interrupt handling is not supported.
+This means that external interrupts must be processed with interrupts
+disabled (@pxref{Disabling Interrupts}) and that interrupts may not be
+enabled at any point during their execution.
+
+Second, an interrupt handler must not call any function that can
+sleep, which includes @func{thread_yield}, @func{lock_acquire}, and
+many others.  This is because external interrupts use space on the
+stack of the kernel thread that was running at the time the interrupt
+occurred.  If the interrupt handler tried to sleep and that thread
+resumed, then the two uses of the single stack would interfere, which
+cannot be allowed.
+
+Because an external interrupt runs with interrupts disabled, it
+effectively monopolizes the machine and delays all other activities.
+Therefore, external interrupt handlers should complete as quickly as
+they can.  Any activities that require much CPU time should instead
+run in a kernel thread, possibly a thread whose activity is triggered
+by the interrupt using some synchronization primitive.
+
+External interrupts are also special because they are controlled by a
+device external to the CPU called a @dfn{programmable interrupt
+controller} or @dfn{PIC} for short.  When @func{intr_init} sets up the
+CPU's IDT, it also initializes the PIC for interrupt handling.  The
+PIC also has to be ``acknowledged'' at the end of processing for each
+external interrupt.  @func{intr_handler} takes care of that by calling
+@func{pic_end_of_interrupt}, which sends the proper signals to the
+PIC.
+
+The following additional interrupts functions are related to external
+interrupts.
+
+@deftypefun bool intr_context (void)
+Returns true if we are running in an interrupt context and false
+otherwise.  Mainly used at the beginning of functions that might sleep
+or that otherwise should not be, in this form:
+@example
+ASSERT (!intr_context ());
+@end example
+@end deftypefun
+
+@deftypefun intr_yield_on_return (void)
+When called in an interrupt context, causes @func{thread_yield} to be
+called just before the interrupt returns.  This is used, for example,
+in the timer interrupt handler to cause a new thread to be scheduled
+on every timer tick.
+@end deftypefun
+
+@node Memory Allocation
+@subsection Memory Allocation
+
+Pintos contains two memory allocators, one that allocates memory in
+units of a page, and one that can allocate blocks of any size.
+
+@menu
+* Page Allocation::             
+* Block Allocator::             
+@end menu
+
+@node Page Allocation
+@subsubsection Page Allocation
+
+The page allocator declared in @file{threads/palloc.h} allocates
+memory in units of a page.  It is most often used to allocate memory
+one page at a time, but it can also allocate multiple contiguous pages
+at once.
+
+The page allocator divides the memory it allocates into two pools,
+called the kernel and user pools.  By default, each pool gets half of
+system memory, but this can be changed with a kernel command line
+option (@pxref{Why PAL_USER?}).  An allocation request draws from one
+pool or the other.  If one pool becomes empty, the other may still
+have free pages.  The user pool should be used for allocating memory
+for user processes and the kernel pool for all other allocations.
+This will only become important starting with project 3.  Until then,
+all allocations should be made from the kernel pool.
+
+Each pools is managed with a bitmap of used pages, one bit per page in
+the pool.  An allocation request for @var{n} pages scans the bitmap,
+starting from the beginning, for @var{n} consecutive bits set to
+false, indicating that those pages are free, and then sets those bits
+to true to mark them as now used.  This is a ``first fit'' allocation
+strategy.
+
+The page allocator is subject to fragmentation.  That is, it may not
+be possible to allocate @var{n} contiguous pages even though @var{n}
+or more pages are free, because the free pages are separated by used
+pages.  In fact, in pathological cases it may be impossible to
+allocate 2 contiguous pages even though @var{n} / 2 pages are free!
+However, single-page requests can't fail due to fragmentation.  Thus,
+it is best to limit the need to allocate more than one contiguous page
+as much as possible.
+
+The interesting page allocator types and functions are described
+below.
+
+@deftp {Type} {enum palloc_flags}
+A set of flags that describe how to allocate pages.  These flags may
+be combined in any combination:
+
+@table @code
+@item PAL_ASSERT
+If the pages cannot be allocated, panic the kernel.  This is only
+appropriate during kernel initialization, because user processes
+should never be permitted to panic the kernel.
+
+@item PAL_ZERO
+Clear the pages to all zeros before returning them.  If not set,
+newly allocated pages' contents are unpredictable.
+
+@item PAL_USER
+Obtain the pages from the user pool.  If not set, pages are allocated
+from the kernel pool.
+@end table
+@end deftp
+
+@deftypefun {void *} palloc_get_page (enum palloc_flags @var{flags})
+Obtains a single free page, allocating it in the manner specified by
+@var{flags}, and returns it.  Returns a null pointer if no pages are
+free.
+@end deftypefun
+
+@deftypefun {void *} palloc_get_multiple (enum palloc_flags @var{flags}, size_t @var{page_cnt})
+Obtains @var{page_cnt} contiguous free pages, allocating them in the
+manner specified by @var{flags}, and returns them.  Returns a null
+pointer if no pages are free.
+@end deftypefun
+
+@deftypefun void palloc_free_page (void *@var{page})
+Frees @var{page}, which must have been obtained using
+@func{palloc_get_page} or @func{palloc_get_multiple}.
+@end deftypefun
+
+@deftypefun void palloc_free_multiple (void *@var{pages}, size_t @var{page_cnt})
+Frees the @var{page_cnt} contiguous pages starting at @var{pages}.
+All of the pages must have been obtained using @func{palloc_get_page}
+or @func{palloc_get_multiple}.
+@end deftypefun
+
+@node Block Allocator
+@subsubsection Block Allocator
+
+The block allocator, declared in @file{threads/malloc.h}, can allocate
+blocks of any size.  It is layered on top of the page allocator
+described in the previous section.  Blocks returned by the block
+allocator are obtained from the kernel pool.
+
+The block allocator uses two different strategies for allocating
+memory.  The first of these applies to blocks no larger than 1 kB (one
+fourth of the the page size).  These allocations are rounded up to the
+nearest power of 2, or 16 bytes, whichever is larger.  Then they are
+grouped into a page used only for allocations of that power of 2
+size.
+
+A different strategy applies to allocating blocks larger than 1 kB.
+These allocations (plus a small amount of overhead) are rounded up to
+the nearest page in size, and then the block allocator requests that
+number of contiguous pages from the page allocator.  
+
+In either case, the difference between the allocation requested size
+and the actual block size is wasted.  A real operating system would
+carefully tune its allocator to minimize this waste, but this is
+unimportant in an instructional system like Pintos.
+
+As long as a page can be obtained from the page allocator, small
+allocations always succeed.  Most small allocations will not require a
+new page from the page allocator at all.  However, large allocations
+always require calling into the page allocator, and any allocation
+that needs more than one contiguous page can fail due to fragmentation,
+as already discussed in the previous section.  Thus, you should
+minimize the number of large allocations in your code, especially
+those over approximately 4 kB each.
+
+The interface to the block allocator is through the standard C library
+functions @func{malloc}, @func{calloc}, and @func{free}.