CS452 - Real-Time Programming - Fall 2010
Lecture 7 - Creating a Task
Pubilic Service Announcements
- Re-organized web pages
- Performance criteria for the kernel
- Bounded execution time: e.g. scheduling code, number of priorities
versus number of tasks
- Fast path from external events to user code that responds to
them
- Small kernel size
- Fast kernel service: Create, SRR (Send/Receive/Reply)
- -3 return value doesn't make sense
- You might want to use it anyway.
After the Software Interrupt
In the kernel
The order matters
kernel entry:
- Change to system state
- Save the user state
- on its stack
- This might include the arguments, which you may or may not need
later.
- Put
sp_usr
in a scratch register
- Return to supervisor mode
- Get the request
ldr r3, [lr, #-4]
- Retrieve the kernel state, which should not include the scratch
registers
- You now have the kernel frame pointer
- You can use it to put stuff in kernel memory
- Put what you want in the task's TD
active
is indexed off the frame pointer
active
is a pointer to the TD of the requester
- Some where above you must have picked up the arguments
- must be done after 4. Why?
- must be done before 8. Why?
- Return from
getNextRequest( active )
and get to work
- Don't forget to store the return value in the TD
Before the Software Interrupt
After a while it's time to leave the kernel
- Schedule the next task to run
- i.e. get the value of
active
- Call
GetNextRequest( active )
- Save kernel state on kernel stack
- From TD
- set spsr_svc
- set lr_svc
- get sp_usr
- Set return value by overwriting r0 on user stack
- Switch to system mode
- Load registers from user stack
- Return to supervisor mode
- Let it go
movs pc, lr
- You are now in the stub code
- Put the return value where gcc wants it
- Return from the stub code
After a while it comes to a kernel primitive
- gcc saves the scratch registers to memory
- gcc puts the arguments into the scratch registers
- bl to your stub code
- gcc saves caller state
- put the arguments in the right place
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
Get an unused TD and memory for its stack
- memory could be associated with TD during initialization
- actually a form of constant time memory allocation
- unless you implement Destroy
Mostly filling in fields in the TD.
- task id
- stack pointer
- SPSR
- link register
- parent tid
- return value
- dummy
- different return value for the active task, which goes in its
TD
- state
- install in the ready queues
Must also create the initial stack
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
What follows just seems to say the same thing again. But we might as well
leave it here because some student might find it useful.
How do we implement the API for user code?
- This requires a bit of assembly language.
- In assembly language all arguments & return values are words.
- usually pointers
- stored in registers
- What happens when there are too many arguments?
- What happens when
Create
is called?
- Two arguments
- stored in
r0
and r1
- which is which?
- Immediately placed on the stack, referred to by
fp
- Create puts them in the special place you choose
swi
switches context into the kernel.
What happens when Create
returns?
- When the kernel is finished manipulating
TD
s and
PQ
s
- it knows the tid to be returned
- it places the tid in the caller's return value in its
TD
When the caller is next activated,
- as part of activation the return value is put in your special place,
usually
r0
- user code has to get it and put it in the compiler's special place, r0
for gcc.
Return to: