CS452 - Real-Time Programming - Spring 2012
Lecture 8 - Create, Initialization
Pubilc Service Annoucements
- Due date for assignment 1
- Victoria Day holiday
Initializing the Kernel
Set up the Hardware
- busy-wait io
- low memory
- Where is the kernel entry?
- Turn off interrupts in the ICU
- This should be unnecessary, but what if the previous kernel turned
them on?
- Later you will initialize the ICU differently.
Prepare the Kernel Data Structures
Where is the kernel's stack pointer, right now? What does the stack look
like?
- Do you want it there? Would you rather have it somewhere else?
- This is your last chance to change it. (If you decide to change it you
might want to keep what you are replacing around. Why?)
The kernel data structures
- an array of empty ready queues
- a poimter to the TD of the active task
- an array of TDs
- a free list of pointers to free TDs
Prepare the Memory to be Used by Tasks
- task memory
Create the First User Task
Can run with interrupts turned off for now (belt and braces) but will need
to be turned on later.
Reminder. The place where the kernel starts executing has the global name
main, which cannot be re-used.
Creating a Task
In creating a task you have to do two things
- Get and initialize resources needed by the task
- Make the task look as if it had just entered the kernel
- it's ready to execute when it's scheduled
Things you need to do
Allocate resources
Get an unused TD and unused memory for its stack
- actually a form of constant time memory allocation
Initialize resources
Mostly filling in fields in the TD. Here are the ones that ought to be in
the TD.
- task id
- priority
- stack pointer
- parent tid
- state
- install in the ready queues
This looks like six words, which could be squeezed into fewer. (There will
be more later.)
- If you are thinking about the cache, how can you make the array of task
descriptors so that they are cache-aligned.
Here are the ones that could be in the TD, or could be on the stack.
- SPSR
- link register
- return value
Initialize the stack
Here's something I always do.
- Put distinctive values into registers to start off with, like 00000000,
11111111, 22222222, etc.
- This makes solving pointer misalignment problems easier
The Create Function
You also need a int Create( int priority, void (*code) ( ) )
function to call from user tasks.
Although it's no more than a wrapper there are a few problems to solve.
- Passing arguments
- On entry the arguments are somewhere, usually r0 & r1
- You have to put them where the kernel can find them.
- gcc's function extry code immediately puts them on the stack.
- In assembly you can find them using the frame pointer.
- Jumping into the kernel
- Getting the return value from the kernel and returning it.
- You find it where the kernel put it
- gcc's function exit code expects it to be indexed off the frame
pointer
- from where it does into r0
Other Primitives
These primitives exist mostly so that we, which includes you, can ensure
that task creation and scheduling are working when there is not much else
implemented.
Tid MyTid( )
Self-explanatory
- Doesn't block, but does reschedule.
A question, to which there is a correct answer, or more specifically, a
correct (answer, reason) pair.
- Should the Tid be stored in user space?
Tid MyParentTid( )
Self-explanatory
- Doesn't block, but does reschedule.
Where is the parent Tid, and how does the kernel find it?
void Pass( )
Doesn't block: task calling Pass( ) remains ready to
execute.
Does reschedule.
When is Pass( ) a NOP?
void Exit( )
Calling task is removed from all queues, but its resources are not
reclaimed or reused.
That is, the task goes into a zombie state, in which it cannot be active
or ready, but continues to own all its resources.
How Should Execution Terminate?
Nicely.
When there are no tasks left on the ready queues, it goes back to
RedBoot.
- This behaviour changes when hardware interrupts are implemented.
Return to: