From b5f0874cd96ee2a62aabc645b9626c2749cb6a01 Mon Sep 17 00:00:00 2001
From: manuel
+
+In this assignment, you will learn about the existing functionality
+in Pintos, and add two small features to the system: a more efficient
+implementation of
+
+You will be working in the
+
+The tests for Project 0 are executed by changing the working directory
+to
+
+Before you read the description of this project, you should read all of
+the following sections: 1. Introduction, B. Coding Standards,
+D. Debugging Tools, and E. Development Tools. You should at least
+skim the material from A.1 Loading through A.5 Memory Allocation, especially A.3 Synchronization.
+
+
+The first step is to read and understand the code for the initial thread
+system.
+Pintos already implements thread creation and thread completion,
+a simple scheduler to switch between threads, and synchronization
+primitives (semaphores, locks, condition variables, and optimization
+barriers).
+
+
+Some of this code might seem slightly mysterious. If
+you haven't already compiled and run the base system, as described in
+the introduction (see section 1. Introduction), you should do so now. You
+can read through parts of the source code to see what's going
+on. If you like, you can add calls to
+
+When a thread is created, you are creating a new context to be
+scheduled. You provide a function to be run in this context as an
+argument to
+
+At any given time, exactly one thread runs and the rest, if any,
+become inactive. The scheduler decides which thread to
+run next. (If no thread is ready to run
+at any given time, then the special "idle" thread, implemented in
+
+
+The mechanics of a context switch are
+in
+
+Using the GDB debugger, slowly trace through a context
+switch to see what happens (see section D.5 GDB). You can set a
+breakpoint on
+
+Warning: In Pintos, each thread is assigned a small,
+fixed-size execution stack just under 4 kB in size. The kernel
+tries to detect stack overflow, but it cannot do so perfectly. You
+may cause bizarre problems, such as mysterious kernel panics, if you
+declare large data structures as non-static local variables,
+e.g.
+
+Here is a brief overview of the files in the
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+The basic threaded kernel also includes these files in the
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Finally,
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Proper synchronization is an important part of the solutions to these
+problems. Any synchronization problem can be easily solved by turning
+interrupts off: while interrupts are off, there is no concurrency, so
+there's no possibility for race conditions. Therefore, it's tempting to
+solve all synchronization problems this way, but don't.
+Instead, use semaphores, locks, and condition variables to solve the
+bulk of your synchronization problems. Read the tour section on
+synchronization (see section A.3 Synchronization) or the comments in
+
+
+In the Pintos projects, the only class of problem best solved by
+disabling interrupts is coordinating data shared between a kernel thread
+and an interrupt handler. Because interrupt handlers can't sleep, they
+can't acquire locks. This means that data shared between kernel threads
+and an interrupt handler must be protected within a kernel thread by
+turning off interrupts.
+
+
+This project only requires accessing a little bit of thread state from
+interrupt handlers. For the alarm clock, the timer interrupt needs to
+wake up sleeping threads. In the advanced scheduler, the timer
+interrupt needs to access a few global and per-thread variables. When
+you access these variables from kernel threads, you will need to disable
+interrupts to prevent the timer interrupt from interfering.
+
+
+When you do turn off interrupts, take care to do so for the least amount
+of code possible, or you can end up losing important things such as
+timer ticks or input events. Turning off interrupts also increases the
+interrupt handling latency, which can make a machine feel sluggish if
+taken too far.
+
+
+The synchronization primitives themselves in
+
+Disabling interrupts can be useful for debugging, if you want to make
+sure that a section of code is not interrupted. You should remove
+debugging code before turning in your project. (Don't just comment it
+out, because that can make the code difficult to read.)
+
+
+There should be no busy waiting in your submission. A tight loop that
+calls
+
+In the past, many groups divided the assignment into pieces, then each
+group member worked on his or her piece until just before the
+deadline, at which time the group reconvened to combine their code and
+submit. This is a bad idea. We do not recommend this
+approach. Groups that do this often find that two changes conflict
+with each other, requiring lots of last-minute debugging. Some groups
+who have done this have turned in code that did not even compile or
+boot, much less pass any tests.
+
+
+Instead, we recommend integrating your team's changes early and often,
+using the source code control system git.
+This is less likely to produce surprises, because everyone can see
+everyone else's code as it is written, instead of just when it is
+finished. These systems also make it possible to review changes and,
+when a change introduces a bug, drop back to working versions of code.
+
+
+You should expect to run into bugs that you simply don't understand
+while working on this and subsequent projects. When you do,
+reread the appendix on debugging tools, which is filled with
+useful debugging tips that should help you to get back up to speed
+(see section D. Debugging Tools). Be sure to read the section on backtraces
+(see section D.4 Backtraces), which will help you to get the most out of every
+kernel panic or assertion failure.
+
+
+The tests for both the alarm clock assignment in Project 0, and the
+priority scheduler in Project 1, run as part of the operating system
+kernel, with full access to privileged parts of the system.
+Once we start running user programs on top of the operating system, this
+is no longer true.
+
+
+We allow more than one process to run at a time. Each process has one
+thread (multithreaded processes are not supported). User programs are
+written under the illusion that they have the entire machine. This
+means that when you load and run multiple processes at a time, you must
+manage memory, scheduling, and other state correctly to maintain this
+illusion.
+
+
+In Project 2, we will test your operating system by running
+user programs. This gives you much greater freedom. You must make sure
+that the user program interface meets the specifications described here,
+but given that constraint you are free to restructure or rewrite kernel
+code however you wish.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+You will need to interface to the file system code, because
+user programs are loaded from the file system and most of the
+system calls you must implement deal with the file system.
+You will want to look over the
+
+There is no need to modify the file system code in this course, and so
+we recommend that you do not. Working on the file system is likely to
+distract you from the project's foci.
+
+
+You will have to tolerate the following limitations, however:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+One important feature is included:
+
+
+
+
+You need to be able to create a simulated disk with a file system
+partition. The
+
+You'll need a way to copy files in and out of the simulated file system.
+The
+
+Incidentally, these commands work by passing special commands
+
+
+Here's a summary of how to create a disk with a file system partition,
+format the file system, copy the
+
+
+
+The three final steps can actually be combined into a single command:
+
+
+
+
+If you don't want to keep the file system disk around for later use or
+inspection, you can even combine all four steps into a single command.
+The
+
+
+
+You can delete a file from the Pintos file system using the
+
+Pintos can run normal C programs, as long as they fit into memory and use
+only the system calls you implement. Notably,
+
+The
+
+Pintos can load ELF executables with the loader provided for you
+in
+
+You should realize immediately that, until you copy a
+test program to the simulated file system, Pintos will be unable to do
+useful work. You won't be able to do
+interesting things until you copy a variety of programs to the file system.
+You might want to create a clean reference file system disk and copy that
+over whenever you trash your
+
+Virtual memory in Pintos is divided into two regions: user virtual
+memory and kernel virtual memory. User virtual memory ranges from
+virtual address 0 up to
+
+User virtual memory is per-process.
+When the kernel switches from one process to another, it
+also switches user virtual address spaces by changing the processor's
+page directory base register (see
+
+Kernel virtual memory is global. It is always mapped the same way,
+regardless of what user process or kernel thread is running. In
+Pintos, kernel virtual memory is mapped one-to-one to physical
+memory, starting at
+
+A user program can only access its own user virtual memory. An attempt to
+access kernel virtual memory causes a page fault, handled by
+
+
+Conceptually, each process is
+free to lay out its own user virtual memory however it
+chooses. In practice, user virtual memory is laid out like this:
+
+
+
+
+In this project, the user stack is fixed in size, but in project 2 it
+will be allowed to grow. Traditionally, the size of the uninitialized
+data segment can be adjusted with a system call, but you will not have
+to implement this.
+
+
+The code segment in Pintos starts at user virtual address
+0x08084000, approximately 128 MB from the bottom of the address
+space. This value is specified in [ SysV-i386] and has no deep
+significance.
+
+
+The linker sets the layout of a user program in memory, as directed by a
+"linker script" that tells it the names and locations of the various
+program segments. You can learn more about linker scripts by reading
+the "Scripts" chapter in the linker manual, accessible via
+
+To view the layout of a particular executable, run
+
+As part of a system
+call, the kernel must often access memory through pointers provided by a user
+program. The kernel must be very careful about doing so, because
+the user can pass a null pointer, a pointer to
+unmapped virtual memory, or a pointer to kernel virtual address space
+(above
+
+There are at least two reasonable ways to do this correctly. The
+first method is to verify
+the validity of a user-provided pointer, then dereference it.
+The second method is to check only that a user
+pointer points below
+
+In either case, one needs to make sure not to "leak" resources. For
+example, suppose that your system call has acquired a lock or
+allocated memory with
+
+Before you turn in your project, you must copy the
+project 0 design document template into your source tree under the name
+
+
+Reimplement
+
+
+
+The argument to
+
+Separate functions
+
+If your delays seem too short or too long, reread the explanation of the
+
+
+The tests for the 2.3.2 Alarm Clock assignment are executed by changing the
+working directory
+
+The alarm clock implementation is not needed for later projects.
+
+
+Currently,
+
+Within a command line, multiple spaces are equivalent to a single
+space, so that
+
+You can parse argument strings any way you like. If you're lost,
+look at
+
+See section 2.5.1 Program Startup Details, for information on exactly how you
+need to set up the stack.
+
+
+
+
+Here's a summary of our reference solution, produced by the
+
+
+The reference solution represents just one possible solution. Many
+other solutions are also possible and many of those differ greatly from
+the reference solution. Some excellent solutions may not modify all the
+files modified by the reference solution, and some may modify files not
+modified by the reference solution.
+
+
+
+
+
+
+
+To add a
+
+When you modify the top-level
+
+A new
+
+
+
+It means that you defined a non-
+
+
+
+Timer interrupts occur
+
+We don't recommend changing this value, because any changes are likely
+to cause many of the tests to fail.
+
+
+
+
+There are
+
+We don't recommend changing this value, because any changes are likely
+to cause many of the tests to fail.
+
+
+
+
+See section 1.2.1 Testing.
+
+
+See section D.4 Backtraces, for more information.
+
+
+
+
+Don't worry about the possibility of timer values overflowing. Timer
+values are expressed as signed 64-bit numbers, which at 100 ticks per
+second should be good for almost 2,924,712,087 years. By then, we
+expect Pintos to have been phased out of the curriculum.
+
+
+
+
+Did you format the file system (with
+
+Is your file name too long? The file system limits file names to 14
+characters. A command like
+
+Is the file system full?
+
+
+Does the file system already contain 16 files? The base Pintos file
+system has a 16-file limit.
+
+
+The file system may be so fragmented that there's not enough contiguous
+space for your file.
+
+
+
+
+Files are written under the name you refer to them, by default, so in
+this case the file copied in would be named
+
+You can list the files in your file system with
+
+
+
+This will happen if you haven't implemented argument passing
+(or haven't done so correctly). The basic C library for user programs tries
+to read argc and argv off the stack. If the stack
+isn't properly set up, this causes a page fault.
+
+
+
+
+The
+
+
+
+The C library we provide is very limited. It does not include many of
+the features that are expected of a real operating system's C library.
+The C library must be built specifically for the operating system (and
+architecture), since it must make system calls for I/O and memory
+allocation. (Not all functions do, of course, but usually the library
+is compiled as a unit.)
+
+
+The chances are good that the library you want uses parts of the C library
+that Pintos doesn't implement. It will probably take at least some
+porting effort to make it work under Pintos. Notably, the Pintos
+user program C library does not have a
+
+
+
+Modify
+
+
+
+Yes, with some limitations. See section D.5 GDB.
+
+
+
+
+You may modify the stack setup code to allocate more than one page of
+stack space for each process. In project 2, you will implement a better
+solution.
+
+
+
+
+You should implement the standard Unix semantics for files. That is, when
+a file is removed any process which has a file descriptor for that file
+may continue to use that descriptor. This means that
+they can read and write from the file. The file will not have a name,
+and no other processes will be able to open it, but it will continue
+to exist until all file descriptors referring to the file are closed
+or the machine shuts down.
+
+
+
+
+
+
+The top of stack is at
+
+
+
+No. You should be able to support
+
+This section summarizes important points of the convention used for
+normal function calls on 32-bit 80x86 implementations of Unix.
+Some details are omitted for brevity. If you do want all the details,
+refer to [ SysV-i386].
+
+
+The calling convention works like this:
+
+
+
+
+The stack grows downward: each push decrements the stack pointer, then
+stores into the location it now points to, like the C expression
+
+
+
+
+
+
+
+
+
+
+
+
+Consider a function
+
+
+
+The Pintos C library for user programs designates
+
+
+
+The kernel must put the arguments for the initial function on the stack
+before it allows the user program to begin executing. The arguments are
+passed in the same way as the normal calling convention (see section 2.5 80x86 Calling Convention).
+
+
+Consider how to handle arguments for the following example command:
+
+
+Then, push the address of each string plus a null pointer sentinel, on
+the stack, in right-to-left order. These are the elements of
+
+
+Then, push
+
+The table below shows the state of the stack and the relevant registers
+right before the beginning of the user program, assuming
+
+
+
+
+
+[ << ]
+[ >> ]
+ [Top]
+[Contents]
+[Index]
+[ ? ]
+
+ 2. Project 0: Introducing Pintos
+
+sleep
, and the ability to pass command line
+arguments to user programs.
+threads
directory for the first part
+of this assignment (with some work in the devices
directory on
+the side), and modify the file userprog/process.c
in the second part.
+intro
and running make
followed by make check
.
+
+
+ 2.1 Understanding Threads
+
+printf() almost
+anywhere, then recompile and run to see what happens and in what
+order. You can also run the kernel in a debugger and set breakpoints
+at interesting spots, single-step through code and examine data, and
+so on.
+thread_create(). The first time the thread is
+scheduled and runs, it starts from the beginning of that function
+and executes in that context. When the function returns, the thread
+terminates. Each thread, therefore, acts like a mini-program running
+inside Pintos, with the function passed to thread_create()
+acting like main().
+idle(), runs.)
+Synchronization primitives can force context switches when one
+thread needs to wait for another thread to do something.
+threads/switch.S
, which is 80x86
+assembly code. (You don't have to understand it.) It saves the
+state of the currently running thread and restores the state of the
+thread we're switching to.
+schedule() to start out, and then
+single-step from there.(1) Be sure
+to keep track of each thread's address
+and state, and what procedures are on the call stack for each thread.
+You will notice that when one thread calls switch_threads(),
+another thread starts running, and the first thing the new thread does
+is to return from switch_threads(). You will understand the thread
+system once you understand why and how the switch_threads() that
+gets called is different from the switch_threads() that returns.
+See section A.2.3 Thread Switching, for more information.
+int buf[1000];
. Alternatives to stack allocation include
+the page allocator and the block allocator (see section A.5 Memory Allocation).
+
+
+ 2.1.1 Source Files
+
+threads
+directory. You will not need to modify most of this code, but the
+hope is that presenting this overview will give you a start on what
+code to look at.
+
+
+loader.S
+loader.h
+start() in start.S
.
+See section A.1.1 The Loader, for details. You should not need to look at
+this code or modify it.
+start.S
+kernel.lds.S
+start.S
to be near the beginning
+of the kernel image. See section A.1.1 The Loader, for details. Again, you
+should not need to look at this code
+or modify it, but it's here in case you're curious.
+init.c
+init.h
+main(), the kernel's "main
+program." You should look over main() at least to see what
+gets initialized. You might want to add your own initialization code
+here. See section A.1.3 High-Level Kernel Initialization, for details.
+thread.c
+thread.h
+thread.h
defines struct thread, which you are likely to modify
+in all three projects. See A.2.1 struct thread and A.2 Threads for
+more information.
+switch.S
+switch.h
+palloc.c
+palloc.h
+malloc.c
+malloc.h
+malloc() and free() for
+the kernel. See section A.5.2 Block Allocator, for more information.
+interrupt.c
+interrupt.h
+intr-stubs.S
+intr-stubs.h
+synch.c
+synch.h
+io.h
+devices
directory that you won't have to touch.
+vaddr.h
+pte.h
+flags.h
+
+
+ 2.1.1.1
+
+devices
code devices
directory:
+
+
+timer.c
+timer.h
+vga.c
+vga.h
+printf()
+calls into the VGA display driver for you, so there's little reason to
+call this code yourself.
+serial.c
+serial.h
+printf() calls this code for you,
+so you don't need to do so yourself.
+It handles serial input by passing it to the input layer (see below).
+block.c
+block.h
+ide.c
+ide.h
+partition.c
+partition.h
+kbd.c
+kbd.h
+input.c
+input.h
+intq.c
+intq.h
+rtc.c
+rtc.h
+thread/init.c
+to choose an initial seed for the random number generator.
+speaker.c
+speaker.h
+pit.c
+pit.h
+devices/timer.c
and devices/speaker.c
+because each device uses one of the PIT's output channel.
+
+
+ 2.1.1.2
+
+lib
files lib
and lib/kernel
contain useful library
+routines. (lib/user
will be used by user programs, starting in
+project 2, but it is not part of the kernel.) Here's a few more
+details:
+
+
+ctype.h
+inttypes.h
+limits.h
+stdarg.h
+stdbool.h
+stddef.h
+stdint.h
+stdio.c
+stdio.h
+stdlib.c
+stdlib.h
+string.c
+string.h
+debug.c
+debug.h
+random.c
+random.h
+-rs
+kernel command-line option on each run, or use a simulator other than
+Bochs, or specify the -r
option to pintos.
+round.h
+syscall-nr.h
+kernel/list.c
+kernel/list.h
+kernel/bitmap.c
+kernel/bitmap.h
+kernel/hash.c
+kernel/hash.h
+kernel/console.c
+kernel/console.h
+kernel/stdio.h
+printf() and a few other functions.
+
+
+ 2.1.2 Synchronization
+
+threads/synch.c
if you're unsure what synchronization primitives
+may be used in what situations.
+synch.c
are
+implemented by disabling interrupts. You may need to increase the
+amount of code that runs with interrupts disabled here, but you should
+still try to keep it to a minimum.
+thread_yield() is one form of busy waiting.
+
+
+ 2.1.3 Development Suggestions
+
+
+
+ 2.2 Understanding User Programs
+
+
+
+ 2.2.1 Source Files
+
+
+
+process.c
+process.h
+pagedir.c
+pagedir.h
+syscall.c
+syscall.h
+exception.c
+exception.h
+gdt.c
+gdt.h
+tss.c
+tss.h
+
+
+ 2.2.2 Using the File System
+
+filesys.h
and file.h
+interfaces to understand how to use the file system, and especially
+its many limitations.
+
+
+
+
+filesys_remove() are implemented.
+That is, if a file is open when it is removed, its blocks
+are not deallocated and it may still be accessed by any
+threads that have it open, until the last one closes it. See Removing an Open File, for more information.
+pintos-mkdisk program provides this
+functionality. From the userprog/build
directory, execute
+pintos-mkdisk filesys.dsk --filesys-size=2. This command
+creates a simulated disk named filesys.dsk
that contains a 2
+MB Pintos file system partition. Then format the file system
+partition by passing -f -q
on the kernel's command line:
+pintos -f -q. The -f
option causes the file system to
+be formatted, and -q
causes Pintos to exit as soon as the
+format is done.
+pintos -p
("put") and -g
("get")
+options do this. To copy file
into the
+Pintos file system, use the command pintos -p file -- -q
.
+(The --
is needed because -p
is for the pintos
+script, not for the simulated kernel.) To copy it to the Pintos file
+system under the name newname
, add -a
+newname
: pintos -p file -a newname -- -q
. The
+commands for copying files out of a VM are similar, but substitute
+-g
for -p
.
+extract and append on the kernel's command line and copying
+to and from a special simulated "scratch" partition. If you're very
+curious, you can look at the pintos script as well as
+filesys/fsutil.c
to learn the implementation details.
+echo program into the new
+disk, and then run echo, passing argument x.
+(Argument passing won't work until you implemented it.) It assumes
+that you've already built the examples in examples
and that the
+current directory is userprog/build
:
+ pintos-mkdisk filesys.dsk --filesys-size=2
+pintos -f -q
+pintos -p ../../examples/echo -a echo -- -q
+pintos -q run 'echo x'
+
pintos-mkdisk filesys.dsk --filesys-size=2
+pintos -p ../../examples/echo -a echo -- -f -q run 'echo x'
+
--filesys-size=n option creates a temporary file
+system partition
+approximately n megabytes in size just for the duration of the
+pintos run. The Pintos automatic test suite makes extensive
+use of this syntax:
+ pintos --filesys-size=2 -p ../../examples/echo -a echo -- -f -q run 'echo x'
+
rm
+file kernel action, e.g. pintos -q rm file. Also,
+ls lists the files in the file system and cat
+file prints a file's contents to the display.
+
+
+ 2.2.3 How User Programs Work
+
+malloc() cannot be
+implemented because none of the system calls required for this project
+allow for memory allocation. Pintos also can't run programs that use
+floating point operations, since the kernel doesn't save and restore the
+processor's floating-point unit when switching threads.
+src/examples
directory contains a few sample user
+programs. The Makefile
in this directory
+compiles the provided examples, and you can edit it
+compile your own programs as well. Some of the example programs will
+not work with the current implementation of Pintos.
+userprog/process.c
. ELF is a file format used by Linux,
+Solaris, and many other operating systems for object files,
+shared libraries, and executables. You can actually use any compiler
+and linker that output 80x86 ELF executables to produce programs
+for Pintos. (We've provided compilers and linkers that should do just
+fine.)
+filesys.dsk
beyond a useful state,
+which may happen occasionally while debugging.
+
+
+ 2.2.4 Virtual Memory Layout
+
+PHYS_BASE, which is defined in
+threads/vaddr.h
and defaults to 0xc0000000 (3 GB). Kernel
+virtual memory occupies the rest of the virtual address space, from
+PHYS_BASE up to 4 GB.
+pagedir_activate() in
+userprog/pagedir.c
). struct thread contains a pointer to a
+process's page table.
+PHYS_BASE. That is, virtual address
+PHYS_BASE accesses physical
+address 0, virtual address PHYS_BASE + 0x1234 accesses
+physical address 0x1234, and so on up to the size of the machine's
+physical memory.
+page_fault() in userprog/exception.c
, and the process
+will be terminated. Kernel threads can access both kernel virtual
+memory and, if a user process is running, the user virtual memory of
+the running process. However, even in the kernel, an attempt to
+access memory at an unmapped user virtual address
+will cause a page fault.
+
+
+ 2.2.4.1 Typical Memory Layout
+
+ PHYS_BASE +----------------------------------+
+ | user stack |
+ | | |
+ | | |
+ | V |
+ | grows downward |
+ | |
+ | |
+ | |
+ | |
+ | grows upward |
+ | ^ |
+ | | |
+ | | |
+ +----------------------------------+
+ | uninitialized data segment (BSS) |
+ +----------------------------------+
+ | initialized data segment |
+ +----------------------------------+
+ | code segment |
+ 0x08048000 +----------------------------------+
+ | |
+ | |
+ | |
+ | |
+ | |
+ 0 +----------------------------------+
+
info
+ld
.
+objdump
+(80x86) or i386-elf-objdump (SPARC) with the -p
+option.
+
+
+ 2.2.5 Accessing User Memory
+
+PHYS_BASE). All of these types of invalid pointers must
+be rejected without harm to the kernel or other running processes, by
+terminating the offending process and freeing its resources.
+PHYS_BASE, then dereference it.
+An invalid user pointer will cause a "page fault" that you can
+handle by modifying the code for page_fault() in
+userprog/exception.c
. This technique is normally faster
+because it takes advantage of the processor's MMU, so it tends to be
+used in real kernels (including Linux). It is also the way
+access to user pointers is implemented in the Pintos version provided.
+malloc(). If you encounter an invalid user pointer
+afterward, you must still be sure to release the lock or free the page
+of memory. If you choose to verify user pointers before dereferencing
+them, this should be straightforward. It's more difficult to handle
+if an invalid pointer causes a page fault,
+because there's no way to return an error code from a memory access.
+
+
+ 2.3 Requirements
+
+
+
+ 2.3.1 Design Document
+
+pintos/src/intro/DESIGNDOC
and fill it in. We recommend that
+you read the design document template before you start working on the
+project. See section C. Project Documentation, for a sample design document
+that goes along with a fictitious project.
+
+
+ 2.3.2 Alarm Clock
+
+timer_sleep(), defined in devices/timer.c
.
+Although a working implementation is provided, it "busy waits," that
+is, it spins in a loop checking the current time and calling
+thread_yield() until enough time has gone by. Reimplement it to
+avoid busy waiting.
+
+
+timer_sleep() is useful for threads that operate in real-time,
+e.g. for blinking the cursor once per second.
+timer_sleep() is expressed in timer ticks, not in
+milliseconds or any another unit. There are TIMER_FREQ timer
+ticks per second, where TIMER_FREQ is a macro defined in
+devices/timer.h. The default value is 100. We don't recommend
+changing this value, because any change is likely to cause many of
+the tests to fail.
+timer_msleep(), timer_usleep(), and
+timer_nsleep() do exist for sleeping a specific number of
+milliseconds, microseconds, or nanoseconds, respectively, but these will
+call timer_sleep() automatically when necessary. You do not need
+to modify them.
+-r
option to pintos (see section 1.1.4 Debugging versus Testing).
+intro
. The run make
to build the
+Pintos kernel. Finally run make check
to run the tests,
+followed by make grade
to obtain your score.
+
+
+ 2.3.3 Argument Passing
+
+process_execute() does not support passing arguments to
+new processes. Implement this functionality, by extending
+process_execute() so that instead of simply taking a program file
+name as its argument, it divides it into words at spaces. The first
+word is the program name, the second word is the first argument, and so
+on. That is, process_execute("grep foo bar") should run
+grep passing two arguments foo and bar.
+process_execute("grep foo bar")
+is equivalent to our original example. You can impose a reasonable
+limit on the length of the command line arguments. For example, you
+could limit the arguments to those that will fit in a single page (4
+kB). (There is an unrelated limit of 128 bytes on command-line
+arguments that the pintos utility can pass to the kernel.)
+strtok_r(), prototyped in lib/string.h
and
+implemented with thorough comments in lib/string.c
. You can
+find more about it by looking at the man page (run man strtok_r
+at the prompt).
+
+
+ 2.4 FAQ
+
+
+
+diffstat program. The final row gives total lines inserted
+and deleted; a changed line counts as both an insertion and a deletion.
+ devices/timer.c | 40 +++++++++++-
+ threads/thread.h | 3 +
+ userprog/process.c | 148 ++++++++++++++++++++++++++++++-----------
+ 3 files changed, 150 insertions(+), 41 deletions(-)
+
+
+ 2.4.1 Threads FAQ
+
+
+
+Makefile
s when I add a new source file?
+.c
file, edit the top-level Makefile.build
.
+Add the new file to variable dir_SRC
, where
+dir is the directory where you added the file. For this
+project, that means you should add it to threads_SRC or
+devices_SRC. Then run make. If your new file
+doesn't get
+compiled, run make clean and then try again.
+Makefile.build
and re-run
+make, the modified
+version should be automatically copied to
+threads/build/Makefile
. The converse is
+not true, so any changes will be lost the next time you run make
+clean from the threads
directory. Unless your changes are
+truly temporary, you should prefer to edit Makefile.build
.
+.h
file does not require editing the Makefile
s.
+warning: no previous prototype for `func' mean?
+static function without
+preceding it by a prototype. Because non-static functions are
+intended for use by other .c
files, for safety they should be
+prototyped in a header file included before their definition. To fix
+the problem, add a prototype in a header file that you include, or, if
+the function isn't actually used by other .c
files, make it
+static.
+TIMER_FREQ times per second. You can
+adjust this value by editing devices/timer.h
. The default is
+100 Hz.
+TIME_SLICE ticks per time slice. This macro is
+declared in threads/thread.c
. The default is 4 ticks.
+
+
+ 2.4.2 Alarm Clock FAQ
+
+
+
+
+
+ 2.4.3 Userprog FAQ
+
+
+
+pintos -p file -- -q.
+pintos -f
)?
+pintos -p ../../examples/echo -- -q
+will exceed the limit. Use pintos -p ../../examples/echo -a echo
+-- -q
to put the file under the name echo
instead.
+pintos -p ../file --, file
isn't copied.
+../file
. You
+probably want to run pintos -p ../file -a file -- instead.
+pintos -q ls.
+objdump (80x86) or i386-elf-objdump
+(SPARC) utility can disassemble entire user
+programs or object files. Invoke it as objdump -d
+file. You can use GDB's
+disassemble command to disassemble individual functions
+(see section D.5 GDB).
+malloc() implementation.
+src/examples/Makefile
, then run make.
+
+
+ 2.4.4 Argument Passing FAQ
+
+
+
+PHYS_BASE, typically 0xc0000000, which
+is also where kernel virtual memory starts.
+But before the processor pushes data on the stack, it decrements the stack
+pointer. Thus, the first (4-byte) value pushed on the stack
+will be at address 0xbffffffc.
+PHYS_BASE fixed?
+PHYS_BASE values that are
+any multiple of 0x10000000 from 0x80000000 to 0xf0000000,
+simply via recompilation.
+
+
+ 2.5 80x86 Calling Convention
+
+
+
+PUSH assembly language instruction.
+Arguments are pushed in right-to-left order.
+*--sp = value
.
+CALL, does both.
+EAX.
+RET
+instruction.
+f() that takes three int arguments.
+This diagram shows a sample stack frame as seen by the callee at the
+beginning of step 3 above, supposing that f() is invoked as
+f(1, 2, 3). The initial stack address is arbitrary:
+ +----------------+
+ 0xbffffe7c | 3 |
+ 0xbffffe78 | 2 |
+ 0xbffffe74 | 1 |
+stack pointer --> 0xbffffe70 | return address |
+ +----------------+
+
+
+ 2.5.1 Program Startup Details
+
+_start(), in
+lib/user/entry.c
, as the entry point for user programs. This
+function is a wrapper around main() that calls exit() if
+main() returns:
+ void
+_start (int argc, char *argv[])
+{
+ exit (main (argc, argv));
+}
+/bin/ls -l foo bar
.
+First, break the command into words: /bin/ls
,
+-l
, foo
, bar
. Place the words at the top of the
+stack. Order doesn't matter, because they will be referenced through
+pointers.
+argv. The null pointer sentinel ensures that argv[argc]
+is a null pointer, as required by the C standard. The order ensures
+that argv[0] is at the lowest virtual address. Word-aligned
+accesses are faster than unaligned accesses, so for best performance
+round the stack pointer down to a multiple of 4 before the first push.
+argv (the address of argv[0]) and argc,
+in that order. Finally, push a fake "return address": although the
+entry function will never return, its stack frame must have the same
+structure as any other.
+PHYS_BASE is 0xc0000000:
+
| Address | Name | Data | Type | +
| 0xbffffffc | argv[3][...] | bar\0 | char[4] |
+
| 0xbffffff8 | argv[2][...] | foo\0 | char[4] |
+
| 0xbffffff5 | argv[1][...] | -l\0 | char[3] |
+
| 0xbfffffed | argv[0][...] | /bin/ls\0 | char[8] |
+
| 0xbfffffec | word-align | 0 | uint8_t |
+
| 0xbfffffe8 | argv[4] | 0 | char * |
+
| 0xbfffffe4 | argv[3] | 0xbffffffc | char * |
+
| 0xbfffffe0 | argv[2] | 0xbffffff8 | char * |
+
| 0xbfffffdc | argv[1] | 0xbffffff5 | char * |
+
| 0xbfffffd8 | argv[0] | 0xbfffffed | char * |
+
| 0xbfffffd4 | argv | 0xbfffffd8 | char ** |
+
| 0xbfffffd0 | argc | 4 | int |
+
| 0xbfffffcc | return address | 0 | void (*) () |
+
+ +In this example, the stack pointer would be initialized to +0xbfffffcc. +
+
+
+As shown above, your code should start the stack at the very top of
+the user virtual address space, in the page just below virtual address
+PHYS_BASE (defined in threads/vaddr.h
).
+
+
+You may find the non-standard hex_dump() function, declared in
+<stdio.h>
, useful for debugging your argument passing code.
+Here's what it would show in the above example:
+
+ +
bfffffc0 00 00 00 00 | ....| +bfffffd0 04 00 00 00 d8 ff ff bf-ed ff ff bf f5 ff ff bf |................| +bfffffe0 f8 ff ff bf fc ff ff bf-00 00 00 00 00 2f 62 69 |............./bi| +bffffff0 6e 2f 6c 73 00 2d 6c 00-66 6f 6f 00 62 61 72 00 |n/ls.-l.foo.bar.| + |
+ +We already know one way that the operating system +can regain control from a user program: interrupts from timers and I/O +devices. These are "external" interrupts, because they are caused +by entities outside the CPU (see section A.4.3 External Interrupt Handling). +
++ +The operating system also deals with software exceptions, which are +events that occur in program code (see section A.4.2 Internal Interrupt Handling). These can be errors such as a page fault or division by +zero. Exceptions are also the means by which a user program +can request services ("system calls") from the operating system. +
+
+
+In the 80x86 architecture, the int
instruction is the
+most commonly used means for invoking system calls. This instruction
+is handled in the same way as other software exceptions. In Pintos,
+user programs invoke int $0x30
to make a system call. The
+system call number and any additional arguments are expected to be
+pushed on the stack in the normal fashion before invoking the
+interrupt (see section 2.5 80x86 Calling Convention).
+
+
+Thus, when the system call handler syscall_handler() gets control,
+the system call number is in the 32-bit word at the caller's stack
+pointer, the first argument is in the 32-bit word at the next higher
+address, and so on. The caller's stack pointer is accessible to
+syscall_handler() as the esp
member of the
+struct intr_frame passed to it. (struct intr_frame is on the kernel
+stack.)
+
+
+The 80x86 convention for function return values is to place them
+in the EAX register. System calls that return a value can do
+so by modifying the eax
member of struct intr_frame.
+
+ +You should try to avoid writing large amounts of repetitive code for +implementing system calls. Each system call argument, whether an +integer or a pointer, takes up 4 bytes on the stack. You should be able +to take advantage of this to avoid writing much near-identical code for +retrieving each system call's arguments from the stack. + +
| [ << ] | +[ >> ] | +[Top] | +[Contents] | +[Index] | +[ ? ] | +