CS452 - Real-Time Programming - Spring 2009
Lecture 4 - Kernel, Tasks
Practical Detail
RS-232
Train RS-232
Three wire works fine as long as both ends of the wire are much faster
than the wire itself. But the train controller is very slow. Why?
- Wait for mechanical sensors.
- High electrical noise
The train controller needs to be able to say STOP. How is this done?
Software Flow Control
XOFF, XON
Hardware Flow Control
CTS, RTS
DTR, DSR
What Happens if You Omit Flow Control
Kernel of a Real-time Operating System
Diagram
What Does the Kernel Provide
Tasks
- Provide applications with modularity, which is a higher level at which
they can be organized
- Consist of
- instructions, common to all tasks
- global state, common to all tasks
- local state, different state in different tasks
- How tasks work together
- synchronization
- communication
- combined into one mechanism: message passing
Communication
Communication has two sides
- sharing information
- handshaking
We use Send/Receive/Reply (SRR) to do both.
- Send blocks
- Receive blocks: is synchronous with the call to send
- Reply doesn't block: is synchronous with the return from send
Diagram
Synchronization
- Between tasks
- Coordination of execution in co-operating tasks
- Uses SRR
- With internal events
- Real-time by synchronizing with a real-time clock: e.g. clock
server
- Ordering execution: e.g. name server, bounded buffer
- Uses SRR
- With external events
Kernel Structure
The kernel is just a function like any other, but which runs forever.
kernel( ) {
initialize( );
FOREVER {
request = getNextRequest( );
handle( request );
}
}
This hides all the interesting stuff inside
int getNextRequest( ) {
active = schedule( ); //active is a pointer to a TD
nextRequest = activate( active ); //the active task doesn't change
return nextRequest;
}
What's inside activate( active )?
- transfer of control to the active task
- execution to completion of the active task
- `to completion' means until the active task sends a request to the
kernel
- transfer of control back to the kernel
- getting the request
The hard part to get right is `transfer of control'
- which we call a context switch
How I Get Started Writing a Context Switch
1. Start with a program that calls a function
void func( ) {
printf( "func: running\n" );
}
void main( ) {
printf( "main: beginning\n" );
func( );
printf( "main: ending\n" );
}
- Compile and run it.
- Compile with the -S flag and look at the assembly code.
2. Find out how to put a PC into an interrupt vector
What is an interrupt vector?
- Add to the assembly code a line that puts the address of the first
instruction of
func( ) into the interrupt vector
- Compile the assembly code and run it.
3. Change to calling func() using an interrupt
- Replace the instruction in main that calls func( ) to
int
n
- Replace the
ret instruction in func( ) with
iret.
4. You have just written a context switch, now you need to dress it
up.
- Add stuff to
main( ) so that it has context
- Save the context of
main( ) on the stack just before
int n.
- Restore the context of
main( ) from the stack just after
int n.
5. Add a return value from func( ) and pick it up in main( ).
To go beyond this we need to think about tasks.
Tasks
Kernel maintains a task descriptor (TD) for each created task. That is, to
create a task the kernel must allocate a TD and initialize it. The TD
normally contains
- The task's stack pointer, which points to a private stack containing
- PC
- other registers
- local variables
all ready to be reloaded whenever the task next runs.
- Possibly the return value for when the task is next activated
- The task's parent
- The task's state
Possible states of the task
- Active: running or about to run
- Ready: can run if scheduled
- Blocked: waiting for something to happen
Diagram
Return to: