CS452 - Real-Time Programming - Spring 2013
Lecture 13 - AwaitEvent, Task Structure
Pubilc Service Annoucements
- Due date for kernel 3: 12 June.
Clock Server, Task Structure
A New Kernel Primitive: int AwaitEvent( int EventType )
How is AwaitEvent Used?
- There should (almost) always be a task blocked on AwaitEvent for every
interrupt type. Why?
- A server cannot call AwaitEvent. Why?
- We call the task that calls AwaitEvent a Notifier. Why?
- Code for a typical Notifier
void notifier( ) {
// Initialization, find server, find evtType, initialize device
FOREVER {
data = AwaitEvent( evtType );
Send( server, &data, ... );
}
}
- Code for a typical server
void server( ) {
notifier = Create( HIGHEST, ... );
// other initialization
Send( notifier, &evtType, ... );
FOREVER {
Receive( &requester, &request, ... );
switch ( request.type ) {
case NOTIFIER:
Reply( notifier );
data = request.data;
// use data
case CLIENT:
...
}
}
}
More About AwaitEvent
Argument
- Somewhere there is a list of event types
- List depends on hardware.
- Application programmer knows the list.
- Kernel can respond to each event type on the list
- This is not very portable
- The list would normally be the union of all types occurring on all
hardware
- This is the Windows problem
Processing in the kernel
- Initialization
- Kernel initialization has IRQ masked
- Kernel initializes ICU
- For each device
- Initialize whatever is not initialized in the notifier
- Kernel starts first user task
- Eventually, Notifier is created
- Notifier
- initializes device
- turns on interrupt(s) in the device
- turns on interrupt(s) in the ICU
- calls AwaitEvent
1, 2 & 3, above, may be done either by the kernel or by the
notifier
- Procedure
- Kernel
- identifies interrupt source
- identifies the correct Notifier
- acquires volatile data
- re-enables interrupt in the device
- re-enables interrupt in the CPU during task activation (eg,
movs)
- puts volatile data into AwaitEvent's return value
- Makes Notifier ready
- Notifier
- collects and packages data
- sends to server
- Eventually Server
- Replies to Notifier
- Acts on the data
- Advantage
- Clean consistent user code
- Disadvantage
- Kernel has to know a lot about the hardware.
- Hardware knowledge split between Notifier and kernel
HALT versus an Idle Task
What do you do when there are no tasks to run?
- Idle task
- lowest priority
- diagnose system: at least % of time idle should be known.
- search for ETI
- HALT
- turns off CPU clock
- save power (battery)
- provided two ways
- through System Controller Co-processor
- through the TS-7200 clock controller
- IRQ path is asynchronous, so it works when the clock is off
Clock Server
Primitives
int Time( )
- Clock server starts at zero when it initializes
- Unit of time is tick
int Delay( int ticks )
- Note error returns
- You might want to add an error for negative arguments
- ticks is usually calculated, and a negative value is an early
warning of falling behind.
int DelayUntil( int ticks )
- Can be constructed from the above two primitives.
Pseudo-implementation
void clock( ) {
// Create Notifier and send any initialization data
// Initialize self
FOREVER {
Receive( &requester, &request, ... );
switch ( request.type ) {
case NOTIFIER:
Reply( notifier, ... )
// update time
case TIME_REQUEST:
Reply( requester, time,... )
case DELAY_REQUEST:
// Add requester to list of suspended tasks
}
// Reply to any timed-out tasks
}
}
Comments:
- You need a common request data structure for client and notifier
requests, or possibly a union.
- You should notice a typical server pattern.
- Server receives from Notifier
- Data is used to update internal state.
- Tasks currently suspended are checked
- Any suspended client that can be service is given service.
- Server receives from Client
- If what the client wants is available the server gives
service.
- Otherwise the client is suspended.
It's common to sort the list of suspended tasks. Why?
- When does it pay off to keep a list sorted?
Return to: