CS452 - Real-Time Programming - Spring 2012

Lecture 7 - Create, Scheduling

Pubilc Service Annoucements

  1. Due date for assignment 1
  2. Partners

After the Software Interrupt

In the kernel

The order matters, except for the last two

  1. Save the user state
  2. Get the request
  3. Retrieve the kernel state

There is more than one way to do almost everything in this list, and I have chosen this way of describing what is to be done because it's simplest to describe, not because it's necessarily best!.

At this point the kernel is ready to handle the request.

Handling the Request

What needs to be done

  1. Check for errors
  2. Manipulating TDs
  3. Sometimes, copying bytes from one address space to another.

Saving up the return value

The task that made the request may not be the next one to run.


There are two important issues for scheduling

  1. When do we reschedule?
  2. Who do we activate when we schedule

When to schedule

Every time we are in the kernel, so the issue is `When do we enter the kernel?'

Three possibilities

  1. Tasks run to completion, which means until they make a request for kernel services
  2. Event-driven pre-emption, which means when hardware makes a request for service
  3. Time-slicing

We do 1 & 2, but not 3, because our tasks co-operate. Time-slicing is needed when tasks are adversarial.

Who to Schedule

Whoever is needed to meet all the deadlines

Because this is not an easy problem, we don't want to solve it within the kernel. What the kernel does should be fast (=constant time) and not resource constrained.

Inexpensive (=constant time)ways to schedule

Least expensive first

  1. active task decides = co-routines
  2. round robin
  3. priorities
    1. fixed at compile time
    2. fixed when task is created
    3. re-fixed every time task is scheduled
      • Do you have a good algorithm?

The number of priorities should be small, but not too small.

Tasks at the same priority should have the same precedence.

Scheduling algorithm

  1. Find the highest priority non-empty ready queue.
  2. Schedule the first task in the queue.

    The state of the most recently scheduled (running) task is ACTIVE, not READY.

    The kernel maintains a pointer to the TD of the active task so it knows which task is making the current request.

  3. When a task is made ready it is put at the end of its ready queue.


Array of ready queues, one for each priority.

Each ready queue is a list with a head pointer (for extraction)and a tail pointer (for insertion).

Hint. The Art of Computer Programming (Donald Knuth) says that circular queues are better. Why?

Implementation decisions

  1. How many priorities
  2. Which task should have which priority
  3. What to do when there is no ready task

The queues of typical running system

  1. Highest priority:
  2. Medium priority
  3. Low priority
  4. Lowest priority

Before the Software Interrupt

After a while it's time to leave the kernel

  1. Schedule the next task to run
  2. Call GetNextRequest( active )

Inside GetNextRequest

  1. From TD, or the user stack
  2. Save kernel state on kernel stack
  3. Set return value by overwriting r0 on user stack
  4. Switch to system mode
  5. Load registers from user stack
  6. Return to supervisor mode
  7. Let it go
    movs   pc, lr

The instruction after this one is normally the kernel entry.

Making the Stub that Wraps swi

For each kernel primitive there must be a function available in usr code: the kernel's API.

What gcc does for you

Before calling Create

  1. gcc saves the scratch registers to memory.
  2. gcc puts the arguments into the scratch registers, and possibly on the stack.

While calling Create

  1. bl to the entry point of Create

While executing Create

  1. gcc saves the registers that it thinks will be altered during execution of the function.
  2. your code gets executed
  3. gcc restores the registers it saved, and only those registers.

Exiting from Create

  1. mov pc, lr, or equivalent, is executed, returning the execution to the instruction following bl

After calling Create

  1. gcc stores register r0, the return value, in the variable to which the result of Create is assigned.

What the code you write does

  1. Moves the arguments from gcc's locations to whatever convention you choose for your kernel
  2. Does swi n, where n is the code for Create.
  3. Moves the return value from your kernel's conventional location to r0.

Creating a Task

In creating a task you have to do two things

  1. Get and initialize resources needed by the task
  2. Make the task look as if it had just entered the kernel

Things you need to do

Get an unused TD and memory for its stack

Mostly filling in fields in the TD.

  1. task id
  2. stack pointer
  3. SPSR
  4. link register
  5. parent tid
  6. return value
  7. state
  8. install in the ready queues

Must also initialize the 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.

  1. Passing arguments
  2. Jumping into the kernel
  3. Getting the return value from the kernel and returning it.

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( )


A question, to which there is a correct answer, or more specifically, a correct (answer, reason) pair.

Tid MyParentTid( )


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?


When there are no tasks left on the ready queues, it goes back to RedBoot.

Return to: