Rate-monotonic scheduling

From Infogalactic: the planetary knowledge core
Jump to: navigation, search

In computer science, rate-monotonic scheduling (RMS)[1] is a scheduling algorithm used in real-time operating systems (RTOS) with a static-priority scheduling class.[2] The static priorities are assigned on the basis of the cycle duration of the job: the shorter the cycle duration is, the higher is the job's priority.

These operating systems are generally preemptive and have deterministic guarantees with regard to response times. Rate monotonic analysis is used in conjunction with those systems to provide scheduling guarantees for a particular application.

Introduction

A simple version of rate-monotonic analysis assumes that threads have the following properties:

  • No resource sharing (processes do not share resources, e.g. a hardware resource, a queue, or any kind of semaphore blocking or non-blocking (busy-waits))
  • Deterministic deadlines are exactly equal to periods
  • Static priorities (the task with the highest static priority that is runnable immediately preempts all other tasks)
  • Static priorities assigned according to the rate monotonic conventions (tasks with shorter periods/deadlines are given higher priorities)
  • Context switch times and other thread operations are free and have no impact on the model

It is a mathematical model that contains a calculated simulation of periods in a closed system, where round-robin and time-sharing schedulers fail to meet the scheduling needs otherwise. Rate monotonic scheduling looks at a run modeling of all threads in the system and determines how much time is needed to meet the guarantees for the set of threads in question.

Liu & Layland (1973) proved that for a set of n periodic tasks with unique periods, a feasible schedule that will always meet deadlines exists if the CPU utilization is below a specific bound (depending on the number of tasks). The schedulability test for RMS is:

U = \sum_{i=1}^{n} \frac{C_i}{T_i} \leq n({2}^{1/n} - 1)

where Ci is the computation time, Ti is the release period (with deadline one period later), and n is the number of processes to be scheduled. For example, U ≤ 0.8284 for two processes. When the number of processes tends towards infinity, this expression will tend towards:

\lim_{n \rightarrow \infty} n(\sqrt[n]{2} - 1) = \ln 2 \approx 0.693147\ldots

Therefore, a rough estimate is that RMS can meet all of the deadlines if CPU utilization is less than 69.32%. The other 30.7% of the CPU can be dedicated to lower-priority non real-time tasks. It is known that a randomly generated periodic task system will meet all deadlines when the utilization is 85% or less,[3] however this fact depends on knowing the exact task statistics (periods, deadlines) which cannot be guaranteed for all task sets.

The rate-monotonic priority assignment is optimal, meaning that if any static-priority scheduling algorithm can meet all the deadlines, then the rate-monotonic algorithm can too. The deadline-monotonic scheduling algorithm is also optimal with equal periods and deadlines, in fact in this case the algorithms are identical; in addition, deadline monotonic scheduling is optimal when deadlines are less than periods.[4] For the task model in which deadlines can be greater than periods, Audsley's algorithm endowed with an exact schedulability test for this model finds an optimal priority assignment.[5]

Avoiding priority inversion

In many practical applications, resources are shared and the unmodified RMS will be subject to priority inversion and deadlock hazards. In practice, this is solved by disabling preemption or by priority inheritance. Alternative methods are to use lock free algorithms or avoid the sharing of a mutex/semaphore across threads with different priorities. This is so that resource conflicts cannot result in the first place.

Disabling of preemption

  • The OS_ENTER_CRITICAL() and OS_EXIT_CRITICAL() primitives that lock CPU interrupts in a real-time kernel, e.g. MicroC/OS-II
  • The splx() family of primitives which nest the locking of device interrupts (FreeBSD 5.x/6.x),

Priority inheritance

  • The basic priority inheritance protocol[6] promotes the priority of the task that holds the resource to the priority of the task that requests that resource at the time the request is made. Upon release of the resource, the original priority level before the promotion is restored. This method does not prevent deadlocks and suffers from chained blocking. That is, if a high priority task accesses multiple shared resources in sequence, it may have to wait (block) on a lower priority task for each of the resources.[7] The real-time patch to the Linux kernel includes an implementation of this protocol.[8]
  • The highest locker protocol raises the priority of the task during its use of a resource to the highest among the priorities of all tasks that ever use that resource. The priority ceiling for each resource can be precomputed at system design time. This protocol prevents deadlocks and bounds the blocking time to at most the length of one lower priority critical section.[9]
  • The priority ceiling protocol[10] enhances the basic priority inheritance protocol by assigning a ceiling priority to each semaphore, which is the priority of the highest job that will ever access that semaphore. A job cannot preempt a lower priority critical section if its priority is lower than the ceiling priority for that section. This method prevents deadlocks and bounds the blocking time to at most the length of one lower priority critical section. This method can be suboptimal, in that it can cause unnecessary blocking. The priority ceiling protocol is available in the VxWorks real-time kernel.

Priority inheritance algorithms can be characterized by two parameters. First, is the inheritance lazy (only when essential) or immediate (boost priority before there is a conflict). Second is the inheritance optimistic (boost a minimum amount) or pessimistic (boost by more than the minimum amount):

pessimistic optimistic
immediate OS_ENTER_CRITICAL() / OS_EXIT_CRITICAL() splx(), highest locker
lazy priority ceiling protocol, basic priority inheritance protocol

In practice there is no mathematical difference (in terms of the Liu-Layland system utilization bound) between the lazy and immediate algorithms, and the immediate algorithms are more efficient to implement, and so they are the ones used by most practical systems.[citation needed]

An example of usage of basic priority inheritance is related to the "Mars Pathfinder reset bug" [11][12] which was fixed on Mars by changing the creation flags for the semaphore so as to enable the priority inheritance.

Example

Process Execution Time Period
P1 1 8
P2 2 5
P3 2 10

The utilization will be:

\frac{1}{8} + \frac{2}{5} + \frac{2}{10} = 0.725

The sufficient condition for 3\, processes, under which we can conclude that the system is schedulable is:

U = 3(2^\frac{1}{3} - 1) = 0.77976\ldots

Since 0.725 < 0.77976\ldots the system is surely schedulable.

But remember, this condition is not a necessary one. So we cannot say that a system with higher utilization is not schedulable with this scheduling algorithm.

See also

References

  1. Lua error in package.lua at line 80: module 'strict' not found..
  2. Lua error in package.lua at line 80: module 'strict' not found., http://oreilly.com/catalog/linuxkernel/chapter/ch10.html#85347.
  3. Lua error in package.lua at line 80: module 'strict' not found..
  4. Lua error in package.lua at line 80: module 'strict' not found..
  5. Lua error in package.lua at line 80: module 'strict' not found.
  6. Lua error in package.lua at line 80: module 'strict' not found..
  7. Lua error in package.lua at line 80: module 'strict' not found.
  8. Lua error in package.lua at line 80: module 'strict' not found.
  9. Lua error in package.lua at line 80: module 'strict' not found.
  10. Lua error in package.lua at line 80: module 'strict' not found..
  11. http://research.microsoft.com/~mbj/Mars_Pathfinder/
  12. http://anthology.spacemonkeys.ca/archives/770-Mars-Pathfinder-Reset-Bug.html

Further reading

  • Lua error in package.lua at line 80: module 'strict' not found..
  • Lua error in package.lua at line 80: module 'strict' not found.
  • Lua error in package.lua at line 80: module 'strict' not found., Chapter 6.
  • Lua error in package.lua at line 80: module 'strict' not found..
  • Lua error in package.lua at line 80: module 'strict' not found.

External links