Summary for Midterm:
it’s
open book, open notes, can bring posted
solutions and your own hw.
Note practice midterm, solutions linked to class web page.
Is there a theme connecting all the
things we’ve covered so
far? I think so, as
follows. We have
concentrated on the transitions, or
handoffs, between the three major players, the user code, kernel code,
and
hardware. A major
concern is user-kernel
separation, needed to keep the system well-understood and secure. This separation is
implemented by being very
careful about transitions. The
user code
is kept “bottled up” in its virtual machine.
System calls communicate a small amount of information
(the syscall # and
arguments, return value) across the user-kernel
boundary and back.
Note that the CPU is running in kernel mode or user mode at each point
in time (or each CPU, for multiprocessors). It will continue to
run that way until a particular event takes place: an interrupt, or
execution of a system call, or execution of iret (or equivalent), or
some exception (including page faults, which we will study later).
Hardware-user
code
relationship
- The only
“device registers” that user code is allowed to
access are the CPU general registers.
The UART registers, the disk controller registers,
etc., are hidden away, invisible to the user code. The
user code may only use them via system calls.
- The user code runs
only in the user mode of the CPU (except in our homework projects,
where we have simplified things), so it may only use the non-privileged
instructions of the CPU. The
system call instruction itself is (of course!) a non-privileged
instruction.
- The user code can
see the IF bit in EFLAGS, but it can’t change it. All interrupts are handled
by the kernel. They
can (and usually do) happen during user code execution, but are
invisible to the user code (unless it is doing very high precision
timing.)
- All the user
program can see is a flat address space, with code, data, stack, and
DLLs, and the CPU. In
the user code are the system call instructions.
- The user code can
use multiple CPUs via threads, setup and managed via system calls.
Hardware-kernel
code
relationship
- The kernel runs in
the “kernel mode” of the CPU, and thus can do all
CPU instructions.
- The device
registers are accessible to the kernel, and the kernel contains
“device drivers” that are software modules devoted
to managing a certain kind of hardware.
- The kernel handles
all interrupts, so all interrupt handlers are
kernel code.
- The kernel uses memory for code, data, much like other programs, but has many stacks, one for each thread.
- The kernel can do the cli, sti
instructions (or equivalent on other CPUs), and so IF=1 part of the
time, IF=0 other times as the kernel is running. Putting IF=0 is
a kind of kernel mutex usable for uniprocessors.
User-Kernel
relationship
- The whole user
virtual machine is set up so that the user code can be trusted to use
the CPU for hundreds of instructions at a time without intervention of
the kernel. This is
key to performance.
- The three major
ways to go from user code to kernel execution is by system calls, page
faults, and interrupts. Other
traps are possible too, such as illegal-instruction traps. All these
cause the execution of the CPU’s interrupt/trap cycle, with
its access to the IDT (or similar table for another CPU) to specify
what kernel function to execute.
- System call
instructions are part of the hardware assistance needed to allow user
code to safely call on the kernel.
Hardware/Software
terms that sometimes cause confusion on exams
instruction: like mov, push, in,
out, etc., belong to “instruction set”, used in
assembly language
register: array of hardware bits,
part of CPU, memory,
i/o devices, holds a value in its bits
CPU register:
like eax, ecx, esp, etc. for x86.
device
register: like UART’s
transmit register, receiver reg,
LSR
port
or i/o port: 16-bit number
providing an address for various device registers, in x86 architecture. Ex: 0x2f8 for COM2’s
transmit register.
memory
address: 32-bit number of a
byte of memory, in separate space from i/o ports.
interrupt
vector: the address of
the interrupt handler, held in IDT[nn],
and
specifying the entry point of the assembler interrupt handler
command:
string used to tell a
program what to do, ex: “ls”
is a shell command
system
call: two meanings, system
API call such as write(fd,
buf,
nbytes), certain
instruction execution (int,
ta)
user
stack: one for each thread,
holds execution state of user code.
kernel
stack: one for each thread,
holds execution state of current system call execution, or is empty
interrupt
stack: we assume this is
built on top of the kernel stack of the thread executing at the time of
the
int.
Midterm Reading: midterm is open books, open notes,
handouts, open solutions—yours or mine or both.
Tan.,
Ch.
1, but light on history
Ch 2 to pg. 123, plus 132-146 (not
explicitly covered in
class, but most of the essential concepts have been covered)
Ch 5
to pg. 294.
Ch. 10 (UNIX) to pg. 704, then sec. 10.7, UNIX Security
Ch. 11 (Windows): 11.2 Win32 API, skip registry, 11.3 to pg. 779, 11.4 to pg. 802 (processes, threads), 11.8 Windows Security
Lecture notes through today.
hw1
ideas:
- interrupt programming: PIC, IDT,
int. handler, etc.
- interrupt-driven
i/o: read-ahead, write-behind, also vs. programmed i/o
- use of cli/sti for mutex, need for it with queue
accessed two ways
- use of kprintf for debugging (itself
using programmed i/o with interrupts off)
- device independent
i/o system, and how to do it in C (array of structs
with function pointers)
- not really an
“OS”, just a library of useful things
- also, first part,
system calls in Solaris
hw2
ideas:
- mechanism of
system calls: put args
and syscall# in
registers, special instruction, use of IDT
- trap vector,
system call trap handler: as, C parts, like int
handler, but runs with IF=1 to start
- startup module for
user program
- library envelope
routine for system calls
- first OS: should
be able to classify each piece of code as user or kernel
hw3—not yet!