CS162 Spring 2001
Section 107
Jeryl Soto Contemprato
cs162-td@cory.eecs.berkeley.edu
Discussion Notes
27 February 2001
Admin
- Midterm
1: 2050 VLSB, 5-630pm
- Quiz
and midterm questions aren’t just from the lecture notes; some originate
in the text
Nachos
- grading1.html
- Correctness
(lax because there are many ways to do something & get expected
result) v. design (more subjective because we are looking for efficiency)
- We
wanted to keep riders & elevators simple, but they shouldn’t be too
dumb (i.e. stopping at each floor) otherwise defeats the point of event
modeling
- Riders
should only get on elevator if they are going in same direction
- Elevators
will be tested w/different riders, and vice versa => elevators &
riders should not know of each other (should not share data)
- To
submit phase 1 (due Thu 1 Mar at 2359), run
- submit
proj1-code
- from
within your nachos directory.
- Within
24 hrs after due date (Fri 2 Mar at 2359), run
- submit
proj1-final-design
- from
directory containing proj1-design.ps
- Ensure
it’s readable using ghostview before submitting
Deadlock
- Definitions:
- resources:
things needed by a thread get the job done (CPU time, disk space)
- preemptable: resource we can (and most likely) will
take away—sharable (CPU time, memory once we implement virtual mem)
- non-preemptable: resource that must be reserved for a
particular thread/set of data (disk space)
- Note
that right to enter critical section is a type of resource
- starvation: when a thread waits indefinitely; may
run in the future but unlikely due to other threads having higher
priority
- deadlock:
circular waiting for resources
- example: Thread A and thread B each need
resource x & y. A gets x
first, B gets y first. A waits
for y, B waits for x. Circular
waiting-->deadlock.
- as
long as deadlock may happen with a given set of code, it is
obviously risky
- Deadlock
implies starvation (since threads are waiting indefinitely); starvation
does not imply deadlock (since there may not be circular waiting;
starvation w/o deadlock usually means a thread waits for a resource, and
the resource becomes available from time to time, but the thread doesn’t
have a chance to get it because other threads get it first)
- Conditions
for deadlock (all 4 required for deadlock)
1)
Mutual exclusion:
at least 1 resource in consideration has to be nonsharable, so that only
1 process at a time can use it. So if
another ps. requests it, that requester blocks until rsc is released
2)
Hold & wait:
Need a process holding at least 1 resource & waiting to get add’l
rscs but are currently held by other ps.s
3)
No preemption:
resources cannot be preempted (kinda like #1)
4)
Circular wait:
like #2, we have hold & wait except it occurs in every process
(given some set of processes) in a roundabout fashion.
- Solutions
to deadlock: detection (& correction) v. prevention
- Detection
- Scan
resource allocation graph
- detect
cycles in graph
- “Fix
them” (draw)
- Kill
a thread causing deadlock (not always practical—hard to return system
to consistent state)
- Roll
back actions of the deadlocked threads (transactions—used in databases)
- Give
the user a choice
- Expensive
to detect, and expensive to fix (have to re-check after every fix)
- Prevention
- Involves
getting rid of one of the 4 conditions for deadlock
- Infinite
resources (yeah right)
- No
sharing in the first place—completely independent threads (with every
thread assigned a limited # of resources)
- Don’t
allow wait by denying any requests that can’t be fulfilled rather
than blocking (should cause thread to release any resources already
held) a la phone company’s busy
signal
- Preempt
resources (make formerly non-preemptible resource preemptible, such as
making memory virtual)
- Require
all threads to request all necessary resources at beginning of execution
(but it’s hard to predict the future, and inefficient when threads
over-estimate)
- Make
everyone use same order when accessing resources
- Combination
of techniques is typical
- Banker’s
Algorithm
- Used
because requiring all threads to request all necessary resources
before they execute is inefficient
- Instead
we have threads tell the OS the maximum number of each resource
they will need
- Named
because it can be used in banking to guarantee that the bank never
allocates $ so that there is no possible way to satisfy needs of all
customers
- We
will only grant a request if there is at least one way to make the
request without going into deadlock; if deadlock is inevitable then we
make the thread wait
- Given
(i.e. this info is available at beginning of problem/execution)
- table
of processes, with fields indicating each process’ current
allocation (Alloc) of each
resource & each process’ max need (maxNeed) for each resource
- current
amount of each resource available (Avail), does not include resources
that have already been granted (Alloc)
- Add
this field to the table , which indicates how much of a resource a
process may still need: Need =
maxNeed – Alloc
- What
happens on a resource request? Resource
request algorithm
1)
Thread X requests resource.
If the Request > Need[x], then raise an error (A’s maxNeed claim
exceeded)
2)
If Request > Avail, then thread A must wait until there is
enough available (i.e. wait until Request <= Avail)
3)
“Simulate” allocation of the requested resource by modifying
the resource allocation table:
- Avail
:= Avail – request
- Alloc[x]
= Alloc[x] + request
- Need[x]
= Need[x] - request
Now we check if resulting state
is safe. If so, we actually make
the allocation. If not, undo the
simulation and make X wait on the resource again (we’ll try again when someone
returns some of the resource back to the system)
- How
do we check that a resource state is safe? Safety algorithm
1)
We are determining state for one resource. So initialize Work := Avail (a working
variable for how much of a resource will be available), and a vector Finish
(with one entry for every process) so that Finish[x] := false for all x
2)
Find an process x so that both
a.
Finish[x] = false
b.
Need[x] <= Work
This looks for a thread that has
not “finished” in this simulation and whose need is less than the currently
available amount of resource for this simulation, in other words it’s looking
for a thread that hasn’t “finished” but whose future requests we can
support. If we can’t find any such
process, jump to 4.
3)
We found a process whose future requests (i.e. amount process
will need = Need) we can support for now.
So we pretend that the process x has finished by returning the resources
was holding to Work, so that Work := Work + Alloc[x], and set finish[x] =
true. Go back to step 2 to try and hook
up another process
4)
If we got here that means either all processes can finish, or
that we were unable to supply the need for at least one process (even after
“finishing” as many other processes as we could and “returning” those
resources). If finish[x] = true for all
x then we know we’re safe.
- Sample
problem: MT1 Fall 2000 p. 7
- Sample
problem (if we have at least 30 min. left): MT 1 Fall 1999 p. 11
CPU Scheduling
- Policy
Goals
- Metrics
are very important; they give us a measurement of effectiveness. This is how we judge/justify
optimizations we make.
- Minimize
response time: elapsed real,
physical time for a job to run, from the time it arrives in the queue to
the time it finishes
- Maximize
throughput: useful
operations/jobs per second (CPU utilization)
- Minimize
overhead: overhead is not
useful from user POV
- Efficient
use of system resources:
overlap I/O & computation (amongst other things), make sure
we actually use our hardware
- Fairness: this is questionable, and highly
subjective
- Standard
Policies
- FIFO/FCFS/Run
until done
- First
in first out/first come first serve
- One
program/thread keeps CPU until finished or blocked
- Pros?
- Cons?
- Short
jobs stuck behind long jobs
- Round
Robin
- Add
a timer and give every job a specific time slice/quantum
- After
time slice move thread to back of queue
- Why
do we do this?
- So
short jobs that arrive during a long job have a chance to run
- Pros? Better for short jobs
- Cons?
Poor when jobs are same length
- Why? Average response time is
higher for RR
- What
happens if we adjust quantum:
- Too
big -> degenerate to FIFO
- Too
small -> kill throughput coz too much overhd
- SJF/SRTF
- Shortest
job first (aka STCF shortest time to completion first)
- Shortest
remaining time first (aka STRCF shortest remaining time to
completion first, SRPT shortest remaining processing time)
- They
both run the shorter job
- What’s
the difference? Preemption
- SJF
simply runs shorter job; if while it’s running that job, an even
shorter job comes in, it will get put in the queue (will still get put
in front of longer jobs though)
- SRTF
will preempt a job if a new arrival is shorter.
- Why
do we want short jobs out of the system?
- We
want to get short jobs out of the system, because user expects
them to be done faster. Long
jobs are expected to take longer anyway. Plus, we get better average response time.
- Pros? Optimal (average response time)
- Cons? Hard or impossible to predict future,
unfair
- What
is the best we can do?
- SJF
(non-preemptive) SRTF (preemptive)
- FIFO
is best if all jobs are same length; SRTF becomes FIFO in this case
anyway
- Varying
length: SRTF is the best, and
RR does better than FIFO, since short jobs don’t get stuck behind long
jobs
- In
the end we like SRTF, but too bad we can’t really predict the future
- Multilevel
feedback
- Adaptive
policies: Use past to predict
future behavior and change policy based on that past behavior
- I/O
bound before, I/O bound in future
- Longer
a process has been running, the longer we can expect it to run
- SET: shortest elapsed time. Uses elapsed time as a predictor of
remaining time.
- Rather
than tracking times, we can have multi-level feedback queues
- Multiple
queues, each one with different priority; round robin at each level
- Round
robin time slice INCREASES exponentially at lower priorities (since they
will get to run fewer times due to low priority)
- So
short-running jobs stay near the top of this scheme and get spit out
faster than long running jobs, which move below.
- Lottery
- Give
every job a number of lottery tickets
- More
tickets for short jobs to improve chances of running
- On
every time slice randomly pick a ticket to run
- CPU
time allotted to a job is proportional to # tickets given to a job
- As
load changes, we have graceful degradation.
- MT 1
Fall 2000 p.5
- MT 2
Spring 2000 p. 2