Title: SYSC 5701 Operating System Methods for RealTime Applications
1SYSC 5701Operating System Methods for Real-Time
Applications
2Recall Process Creation
- allocate code, data, and stack to the process
- other resources too? (I/O devices?)
- allocate a thread of control
- managed by the kernel
3Code, Data, Stack
- what if processes replicate behaviour?
- execute the same code?
- will memory manager permit this?
- in a strict process model
- processes do not share memory
- each must have its own copy of code data and
stack space
4Heavyweight Process
- a heavyweight process requires all of the above
code, data, stack - heavyweight processes are strict
- do not share resources
5Lightweight Processes
- can share code and data
- must have own stack and thread of control
- less overhead during lightweight kernel activity
- faster context switch and IPC ?
- application programmers must manage sharing of
data ?
6Application Design Philosophy
- heavyweight processes encapsulate large-grained
parallel activities that are loosely coupled - i.e. interact infrequently, minimal data sharing
- lightweight processes encapsulate finer-grained
parallel activities that are tightly coupled - i.e. interact frequently, lots of data sharing
7Performance Goals
- heavyweight switching and IPC
- ? more expensive
- looser coupling less heavyweight IPC and
switching - lightweight switching and IPC
- ? less expensive
- tighter coupling more lightweight IPC and
switching
8Thread
- A thread is a lightweight process created in the
context of a heavyweight process - only the threads in the context of the same
heavyweight process can access the code and data
of that process
9Thread Management
- by kernel
- outside of kernel
- hybrid developing trend!
101. Threads Managed by Kernel
- kernel has two classes of processes
- lightweight (thread) and heavyweight (process)
- different services for each
- threads of a process are autonomous
- a thread may become blocked, but just that thread
is blocked (not the entire process)
11Kernel Managed Threads
- heavyweight process does not really execute as a
single thread of control - ? a container for managing threads
- the process has a set of threads
- the active elements of the process
- kernel manages both process and thread scheduling
122. Threads Outside of Kernel
- process has single thread of control
- managed by kernel
- single control thread is shared among threads
- managed by thread manager
- unknown to kernel!
- this sort of thread called user-thread (fiber?)
- thread manager resides outside of kernel
- often a run-time library supplied by
language/environment vendors
13User Threads
- if strict process model each process must have
its own copy of thread manager (no code sharing!) - if a thread makes an IPC call via the kernel and
becomes blocked - ? kernel blocks the process ! ?
- (kernel has no knowledge of threads!) ?
143. Hybrid Threads
- thread concept known to kernel, but user-threads
are managed outside of kernel - thread scheduler and kernel cooperate scheduler
thread for the process is known to the kernel - special interactions supported between scheduler
thread and kernel
15Hybridcont
- when a thread invokes a kernel service and is
blocked - kernel pseudo blocks thread records relevant
blocking criteria, but instead of blocking
process ... - kernel returns control to relevant scheduler
thread - scheduler thread blocks the thread (outside of
kernel!) and schedules a different thread - net result thread is blocked, process not
blocked - when criteria met to unblock original thread
- kernel informs relevant scheduler thread
- scheduler thread unblocks the thread and makes
thread scheduling decisions
16Hybrid Thread Blocking
process
user threads
scheduler thread
blocking call
notify scheduler thread
kernel
save info
17Thread Scheduling Issues
- time-slice ?
- suitability to real-time apps ?
- preempt vs. non-preempt
- voluntary relinquish?
- all the same-old issues
- priority? timed services? etc.
18Extended Process Model with Threads
heavyweight kernel processes
user threads
? ? ?
? ? ?
? ? ?
lightweight processes (kernel threads)
19Kernel-Mode Threads/Processes
- common in large, general-purpose o/s
- not as common in real-time applications
- modern o/s often manage I/O subsystems
- e.g. disk I/O subsystem
- require supervisor permission to access
restricted I/O devices - must execute in kernels supervisor context
20O/S Layer Above Kernel
- may include processes that are not visible
outside of the o/s but exist inside the o/s - these processes are created (and run) in the
kernels context to permit access to restricted
h/w - called kernel-mode processes
21Kernel-Mode Process Layer
application processes
kernel-mode processes
kernel
22Similar Thread Trend
- when process invokes kernel service kernel
blocks process and creates a kernel-mode thread
associated with the request - new thread has unique stack (in kernels space)
shares access to kernel code and data - must have enough stack space reserved in kernel
to permit a kernel-mode thread for each process ? - kernel-mode thread executes with supervisor
privileges on behalf of the requesting process
23Kernel-Mode Threads
- kernel manages kernel-mode threads
- kernel threads can be preempted
- more concurrency
- still need to protect access to kernels shared
data structures! - has overheads! ?
- hasnt hit real-time kernels in a big way (yet)
24Threads ExampleCU Threads
- developed by Prof. John Neilson, SCS, Carleton
Univ. - POSIX-like threads
- library of C functions that manage threads
- allows concurrent threads running on a single CPU
- preemptive or non-premptive priority
- threads run-until-block/exit/yield
- user-defined C functions provide thread
functionality
25CU Threads (more)
- timed service sleep
- must disable timer to do I/O
- e.g. C printf implementation uses underlying
run-time support (DOS ?) - DOS has no guarantee of re-entrant, protected I/O
access ? - loss of timing accuracy?
26Sync Comm
- supports semaphores for synchronization
- may use shared memory for thread communication
- recall shared memory assumed by threads!
27Partner Sync. Concept
- can attach threads to create partnerships
- each thread can have, at most, one attached
partner - partners cooperate to accomplish objectives
- additional synchronization support beyond
semaphores
28Some CU Thread Management Primitives
- cu_thread_fork fork a thread from the current
- thread starts a new unattached thread
- new thread has no partners partnerless
- cu_thread_attach thread becomes partner
- cu_thread_detach thread becomes partnerless
- any previously attached partner is
disassociated - cu_thread_join merge with (i.e. wait for
termination of) partner thread - ? synchronization !
29Primitivescont
- cu_thread_exit terminate thread and wait for
- partner to terminate (LIMBO state)
- cu_thread_yield voluntary yield of processor
- cu_thread_priority dynamically change threads
priority - cu_thread_sleep sleep for a specified duration
- cu_sema_alloc create a semaphore
- cu_sema_free delete a semaphore
- cu_sema_wait wait on a semaphore
- cu_sema_signal signal a semaphore
30CU Thread State Machine (non-preemptive)
yield
exit detached
DONE
forced transitions
yield, sleep, exit, wait, join
fork
READY
RUNNING
join
exit ! detached
self transitions
wait
signal
LIMBO
join
WAIT
sleep
time resolution approx. 1/10 sec
exit
JOIN
timeout
SLEEP
31CU Threads Implementation
- threads managed in a fixed size array of control
blocks - cu_thread_t thread_table Max_Threads
- each thread control block (entry in thread_table)
is a struct - thread id index of tcb in thread_table
32Thread Control Block
FREE, RTR, etc. (recall other possible state
values)
state
critical flag
boolean
initially to a function with one integer
argument
code pointer
int arg
stack base pointer
context
context save buffer
priority
table index used to link control blocks in a
list
next
joiner
table index of thread to join
value
several purposes thread memory (sleep ticks,
return value, etc.)
queue
if blocked index into semaphore table where
thread is blocked
33Other Manager Variables
- int thread_count active thread count
- cu_thread_t cpt pointer to active
thread control block - int rtrq index of first thread in rtr
queue - int sleepq index to first sleeping
thread - int preempt flag indicates if preemption
used
34Semaphores
- semaphores also managed in a fixed size array of
control blocks - semaphore struct
state
FREE, USED
first
index of first thread blocked
last
index of last thread blocked
value
counter value
35(Some) Thread Primitives In More Detail
- int cu_thread_init( int preempt_flag )
- must be called once when main code of process
runs - initializes thread manager variables
- converts main into a thread
- creates an idle thread
- busy waiting for each process ??
- hooks in to timer interrupt interrupt 1/10
second - returns status OK or SYSERR
36Fork (Create) Thread
- int cu_thread_fork( void (code )(int arg), int
arg ) - forks a new UNATTACHED thread
- new thread is READY put into rtrq
- finds a FREE thread control block
- returns thread id or SYSERR
- initializes control block for new thread
- priority same as current thread
37Forkcont
- if stack not yet allocated (for control block),
then get space from heap will reuse space!
only allocated once all given back when process
is destroyed - set stack pointer to bottom of stack space
- new thread is placed in wrapper
- ? ensures thread calls exit after termination
- static void wrapper ( )
- ( (ctp ? code ) ) (ctp ? arg ) ?
- cu_thread_exit ( OK )
-
38Thread Detach
- int cu_thread_detach ( int tid )
- thread (tid) is DETACHED
- if thread already in LIMBO then delete it
- thread control block is FREE to be re-used
- stack already allocated reuse stack
- returns OK or SYSERR
39Thread Join
- int cu_thread_join ( int tid )
- merge with thread tid (wait for thread tid to
exit) - if tid is already in LIMBO then cleanup tid and
return tids value (termination code from
control block) - (else) tids joiner (in control block) current
thread id - and current thread state JOIN (blocked
waiting)
40Thread Exit
- void cu_thread_exit ( int exit_status )
- terminate thread and wait for partner (?) to
terminate - if DETACHED this is call from wrapper done!
- once DETACHED can never ATTACH again
- if ATTACHED or UNATTACHED (possible later
attachment?) go into LIMBO and save exit_status - save exit_status in value field of control block
- must be explicitly called by process main
before main terminates (why? main does not use
wrapper!)
41Yield Processor
- void cu_thread_yield ( )
- voluntary yield of processor
- put process in rtrq
- schedule new process from front of rtrq
42Set Thread Priority
- void cu_thread_priority ( int tid, int priority )
- dynamically changes tids priority
- if tid in rtrq may result in change in position
in rtrq - if preemption used may result in thread context
switch
43Thread Sleep
- void cu_thread_sleep ( int duration )
- caller goes into sleepq (blocked)
- duration of 1/10 of second ticks to sleep
- duration 0 ? yield
- thread is in (at most) one queue next field in
control blocks used to link threads (regardless
of which queue) - value field in control block used for relative
time management in sleepq
44Create Semaphore
- int cu_sema_alloc ( int value )
- create a semaphore find FREE one in semaphore
table - initial counter value value
- returns semaphore id (table index) or SYSERR
45Semaphore Wait
- void cu_sema_wait ( int sid )
- wait on semaphore sid
- if sids counter value gt 0 then decrement and
continue - if sids counter value lt 0 then BLOCKED put
thread into sids queue
46Context Switching
- must save some registers
- high-level languages often require only a subset
of registers be respected/protected - e.g. Borland C on 8086 (cu threads target)
- must save CSIP, DS, SSSP, BP
- SI and DI if use register variables
- AX, BX, CX, DX no useful values maintained
between C statements
47Borland C Example
- optimizations? assume non-preemptive
- if compile in small memory model
- CS, SS and DS never change
- if no register variables
- ? no need to save SI and DI
- context switch, only need to save
- BP, SP ? (IP saved in call to manager)
48BUT . . . What about Timer Interrupts?
- could interrupt at any point perhaps in the
middle of a high-level language statement? (all
registers might have useful values) - CU thread package provides only sleep as timed
service when timeout, thread goes READY and
into rtrq must protect sleepq rtrq access! - if preemption allowed context switch must be
more thorough - if preemption allowed all thread package
services must be protected
49Timer and Protecting CU Thread Services
- at start of each service current threads
critical flag (field in control block) set TRUE - at end of each service critical flag set FALSE
- thread timer ISR maintains a variable called
missed - in ISR checks to see if current thread is in
critical code if yes, increment missed (to
indicate missed time service that tick) and exit - i.e. missed
50CU Thread Time Resolution?
- if timer not interrupting critical code
decrement number of ticks left for first thread
in sleepq by - 1 missed, then reset missed 0
- relative time management issues?
- ends up with sloppy timing, but minimal
overhead -
- hmm . . . practical?
- realistic timing resolution?