|
Mode: user & kernel mode |
|
Space
|
kernel space: system wide & shared
by all processes
|
kernel segment (kernel code, data
structure, kernel address map) |
|
|
user space: per process & private
|
u area, text, data, stack, shared
memory region |
|
|
|
Kernel accesses kernel space and u area |
|
User process can only access its own user
space except the u area |
|
Execution context
|
process context: kernel executes on
behalf of a process, e.g., system call |
|
system context: kernel executes
system-wide task, e.g., disk or network interrupt handling |
|
|
Mode and context are orthogonal |
|
User address space: text segment, data seg,
stack seg, shared memory regions |
|
Control information: u area, proc structure,
kernel stack, address translation map(= page & segment table) |
|
Credentials: UID, GID |
|
Environment Variables: saved at the bottom of
user stack |
|
Hardware context: saved in PCB, which is, in
turn, saved at the u area |
|
User level library package such as C-thread of
Mach or POSIX pthread provides functions such as:
|
creation, synchronization, scheduling,
and destruction of user threads |
|
|
User thread can be implemented on top of
process or LWP |
|
Kernel is unaware of user threads and only
schedules in terms of processes or LWP (Fig 3-5 (a) & (b)) |
|
User thread becomes possible because
|
a process can have multiple execution
contexts and switch among those contexts using setjmp, longjmp, and
signal |
|
the execution context can be saved and
restored at the user level as Condor does |
|
|
Resource
|
Private user stack, user-level register
context, and other state info like signal mask |
|
|
Synchronization at user level eliminates the
thread switch at kernel level
|
When a thread needs to block on a lock,
thread library puts the thread into the corresponding queue and
schedules another user thread |
|
|
Blocking system calls
|
A user thread makes a blocking system
call -> |
|
Replaced the call to non-blocking calls
-> |
|
Another thread gets scheduled -> |
|
The non-blocking system call completes
-> interrupts occurs -> |
|
The blocked thread gets scheduled again |
|
|
Pros
|
Performance (Table 3-1) |
|
Virtually unlimited number of threads
-> the number of threads = application's concurrency level ->
better programming model |
|
A system can have several thread
libraries each of which can be used for different purposes |
|
|
Cons
|
No true parallelism without support from
kernel thread |
|
No protection among threads within a
process or a LWP -> threads should be programmed as cooperative |
|
Information barrier between library and
kernel
|
A thread (on an LWP) holding lock
can be scheduled out by kernel to schedule in another thread
(on another LWP) waiting for the lock |
|
A thread (on an LWP) of high
priority can be scheduled out by kernel to schedule in another
thread (on another LWP) of lower priority |
|
|
|
Combines the advantages of kernel and user
thread |
|
Basic principle: have close integration
between user threads and kernel |
|
Clear mission allocation
|
Kernel: processor allocation |
|
Library: schedule threads by
multiplexing them on processors given from kernel |
|
|
Library -> Kernel: information that affects
processor allocation
|
request for processors |
|
relinquish of processors |
|
|
Kernel -> Library: information that affects
thread scheduling
|
allocation and deallocation of
processors |
|
status changes of threads, which affects
scheduling |
|
|
upcall: call from kernel to library |
|
scheduler activation: virtual processor given
to library by kernel
|
Logically similar to LWP: has user &
kernel stack |
|
Main difference from LWP: one-to-one
relationship to processors |
|
|
Handling of blocking operation of threads
|
Thread running in kernel blocks -> |
|
Kernel preempts the
processor(=activation) -> |
|
Kernel creates a new activation and make
an upcall to library -> |
|
Library saves the context of the
deactivated thread from the old activation -> |
|
Library schedules another thread on the
new activation -> |
|
Blocking operation completes -> |
|
Kernel makes a new activation (or
preempts an activation from the process) -> |
|
Kernel notifies the library of the
blocking operation having been completed (and preemption of the
activation) -> |
|
Library puts the thread having been
blocked (and the thread of preempted activation) into ready list
-> |
|
Library select a thread from ready list
to run and attaches it to the new activation |
|
|
Each LWP is bound to a kernel thread during
its life-time |
|
Traditional proc and u area of process model
should be changed
|
proc: hold all per-process data,
including the process-specific part of traditional u area |
|
lwp data: per-LWP data
|
user level register context:
meaningful only when the LWP is not running |
|
system call arguments, results,
and error code |
|
signal handling information |
|
resource usage and profiling data,
virtual time alarms, user time and CPU usage |
|
pointer to the kernel thread attached |
|
pointer to the proc structure |
|
|
|
All LWP share signal handlers but each may
have individual sigmask |
|
Traps--synchronous signals like segfault--are
delivered to the causing LWP |
|
Interrupt--asynchronous signals can be
delivered to any LWP that has not masked the signal |
|
Semaphores
are rather low level and error prone: need careful program, otherwise race
condition or deadlock |
|
Monitor
|
Can
be thought as an object wrapped by mutex.down() & mutex.down() |
|
Condition
variable instead of integer semaphore |
|
|
Diff
between condition variable & (integer) semaphore
|
up()
Vs. signal()
|
up:
increase the semaphore value -> excess up operations get
remembered |
|
signal:
wake up a process waiting for the condition variable, no
effect if no process is waiting |
|
|
down()
Vs. wait()
|
down:
block the caller only if value == 0 |
|
wait:
always block the caller until next signal(), also release
mutex |
|
|
|
Bounded
Buffer - in Java syntax
|
synchronized
insert(object item) {
|
if
(buffer is full)
|
nonfull.wait(); |
|
|
addElement(item); |
|
nonempty.signal(); |
|
|
} |
|
synchronized
delete() {
|
if
(buffer is empty)
|
nonempty.wait(); |
|
|
deleteElement(); |
|
nonfull.signal(); |
|
|
} |
|
|
Notes
on signal
|
When
a waiter is awaken, the condition it was waiting for is guaranteed
-> |
|
No
process should sneak between signal() and return from wait() ->
|
Signaling
mechanism should be absolutely reliable & Only one waiter
should be awaken & The signaler should block immediately
-> |
|
Upon
waiter leaving the monitor, signaler should be awaken or be
able to contend to grab mutex released by the waiter |
|
|
Inefficient:
unnecessary context switches signaler->waiter->signaler |
|
|
Notify
|
Wake
up a waiter, but signaler does NOT block -> |
|
Other
processes can sneak into between signal and the return from wait
call -> |
|
Waiter
should check whether the condition is still true after returning
from wait call
|
'if'
block of the above code snippet should be replaced by 'while' |
|
|
|
Monitor
in Java
|
No
monitor modifier for the entire object. Instead each method can be
tagged with 'synchronized' |
|
Only
one implicit condition variable per object -> notifyAll instead
of notify |
|
|
Metrics
|
Waiting time - the amount of time the
job is runnable and not running |
|
Penalty ratio - elapsed-time /
ideal-time |
|
|
FCFS
|
Favors long burst, i.e., CPU-bound jobs
|
Short job's penalty ratio is too
big |
|
|
Favoring long job leads convoy effect |
|
Convoy effect
|
short jobs are waiting behind a
long job |
|
CPU busy & I/O devices idle
<-> CPU idel & I/O busy |
|
|
|
Shortest Job First
|
Optimal with respect to average waiting
time |
|
Don't know how long the job will run
-> Approx. algorithm needed |
|
The length of burst is guessed from
history
|
guess = ( guess + previousBurst )
/ 2 |
|
|
|
Shortest Remaining Job First
|
Preemptive version of SJF |
|
Min ( shortest job in the ready queue,
remaining time for the job currently running to finish ) |
|
|
Round-robin & Process Sharing
|
Round-robin becomes process sharing when
quantum becomes zero |
|
|
Priority scheduling
|
Both preemptive and nonpreemptive
scheduling are possible |
|
Priority can be given externally or
calculated internally |
|
Multi-Level Feedback Queue
|
Short jobs are favored |
|
Long jobs get longer quantum and
run with less context switch |
|
|
|
Analysis
|
Queuing theory: arrival rate vs service
rate |
|