CS452 - Real-Time Programming - Spring 2012
Lecture 4 - Tasks & Kernels
Pubilc Service Annoucements
- Due date for assignment 0
- Some clean-up needed in course account, cleaned up course
introduction.
- RTS/CTS
- The train controller asserts RTS when its UART is ready to receive
a byte.
- RTS is connected to the ARM UART's CTS bit.
- You check the modem status register
- You transmit a byte to the train controller only when CTS is
asserted
- You do not transmit when CTS is negated.
- News group:
uw.cs.cs452.
- Course email account:
cs452@cgl.uwaterloo.ca
- Documentation expected for assignment 0
- Partners
Kernel of a Real-time Operating System
Introduction
The base unit of a polling loop is
Think of action as the performance of a task, such as washing
dishes. Think of condition as a signal from outside that tells
you it's time to perform the task.
Actions are not independent of one another: washing dishes requires hot
water, dish soap, etc., which are provided by other actions. Communication is
needed.
Thus tasks need some support.
- the ability to execute instructions
- code with the pc pointing into it
- state, in the form of memory
- the ability to communicate with one another
- pass data
- synchronization
- the ability to receive information from the real world
- provide data
- possibly nothing but symchronization
These basic needs are provided by the kernel of an operating system. The
kernel we create in cs452 is a microkernel, because it provides these
capabilities and nothing more.
We build the microkernel in four assignments
- task creation and scheduling
- inter-task communication
- an interrupt primitive
- complex servers
The real-time operating system we build consists of
- an uninterruptible microkernel, plus
- interruptible device-handling server tasks that run in user-space with
permissions allowing them to access hardware.
What Does a Microkernel Provide?
Tasks
- Program is conceived as a collection of co-operating tasks
- Provide applications with modularity. Task structure as a method of
program organization is discussed about the time you are finishing the
OS.
- Consist of
- instructions, common to all tasks of the same kind,
- global constants, such as strings used for
formatting messages, and
- local state, different state in different tasks of the same kind,
which requires a separate block of memory for each instantiation of a
task..
- How tasks work together
- synchronization
- communication
- combined into one mechanism: message passing
- Why are tasks important?
- Thinking about one thing at a time is easy.
- Think about the options you expect to have after you
graduate.
- Thinking about more than one thing at a time is hard.
- Keep on thinking, and at the same time listen to me talking
about tasks
- Parenthetical remark. While walking you may have been talking
to somebody, or thinking about something. You did both
effortlessly. How?
- Thinking about more than one thing at a time, in real-time, is very
hard
- Think about turning the wheel, peddling and balancing while
learning to ride a bicycle
- How did you learn to coordinate all these activities in
real-time?
- Tasks allow a programmer to produce each component of a activity
into a sequential set of instructions that includes communication
with other tasks..
Communication
Communication has two aspects
- sharing information, requesting service
- synchronization
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
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
Interrupts
Input from the outside world
- Provide the information you polled for
- ISR OS design, which is essentially a jump table, which separates
testing from acting
interrupt entry point:
calculate action_entry_point;
jump to act_entry_point;
entry_point1:
action1;
entry_point2:
action2;
...
entry_pointn:
actionn;
These are the same actions you implemented in your polling loop,
- and the have all the same problems.
- Somthing to think about
- Polling loop was single-threaded
- You were guaranteed not to be in the middle of a
computation when you got the signal to start another one.
- ISRs are not necessarily single-threaded
- You could get back the polling loop by turning off
interrupts during the action.
- No hierarchy of importance among ISRs
- Polling loop hierarchy was in the polling structure
- Selective interrupt masking can reproduce the
hierarchy,
- But then you have to save state
Tasks
What is a task?
- A set of instructions
- Current state, which is changed by executing instructions, which
includes
- values of its variables, which are automatic variables maintained
on the stack
- contents of its registers
- other processor state such as the PSR
- processor mode
- condition codes
- etc.
- its run state and other things maintained by the kernel
Two tasks can use the same set of instructions, but
- every task has its own state
- Therefore, no static variables
The kernel keeps track of every task's state
- In essence, servicing a request amounts to changing the state of one or
more 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, in the
memory of the task, 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
- Links to queues on which the task is located
- The kernel uses these to find the task when it is ervicing a
request
Possible states of the task
- Active: running or about to run
- On a single processor system only one task can ever be active.
- But we would like to generalize smoothly to more than one
processor.
- Ready: can run if scheduled
- Need a queue used by the scheduler when deciding which task should
be the next active task
- Blocked: waiting for something to happen
- Need several queues, one for each thing that could happen
Kernel Structure
The kernel is just a function like any other, but which runs forever.
kernel( ) {
initialize( ); // includes starting the first user task
FOREVER {
request = getNextRequest( );
handle( request );
}
}
Where is the OS?
- requests come from running user tasks
- one type of request creates a task
- There needs to be a first task that gets everything going
All the interesting stuff inside done by the kernel is hidden inside
getNextRequest.
int getNextRequest( ) {
return activate( schedule( ) ); //the active task doesn't change
}
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
Return to: