CS452 - Real-Time Programming - Winter 2013
Lecture 4 - Tasks & Kernels
Pubilc Service Annoucements
- Due date for kernel 1: 25 January.
- Partners, groups
Kernel of a Real-time Operating System
Introduction
The base unit of a polling loop is
This looks more than you think like the operating systems you are used to
if you set it up so that one of the actions is to log on a user, which
can install an action that provides an interactive shell to each logged in
user.
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 to do what they do.
- 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, plus some others.
- 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 servicing 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: