Operating Systems Module 4 – CPU scheduling
Real-time scheduling and priority inversion
Hard, firm and soft deadlines, utilisation tests for rate-monotonic and EDF scheduling, priority inversion and inheritance, and the real-time policies of Linux.
What you will learn
- Distinguish hard, firm and soft real-time requirements
- Test schedulability with rate-monotonic and earliest-deadline-first analysis
- Explain priority inversion and how priority inheritance bounds it
- Use Linux's SCHED_FIFO, SCHED_RR and SCHED_DEADLINE policies safely
Before you start
What you’ll learn · Before you start
What you will learn
- Distinguish hard, firm and soft real-time requirements
- Test schedulability with rate-monotonic and earliest-deadline-first analysis
- Explain priority inversion and how priority inheritance bounds it
- Use Linux's SCHED_FIFO, SCHED_RR and SCHED_DEADLINE policies safely
Before you start
On this page
A flight controller that computes the right correction too late has still failed. In a real-time system a result must be right and on time, and the ROS 2 design notes define such a system by a deterministic schedule, a guarantee that each task finishes by a certain time, rather than by low latency.
Real-time scheduling proves that guarantee before the system runs. This lesson tests task sets by their utilisation, runs rate-monotonic and EDF scheduling side by side, and shows priority inversion, a lock making the most urgent task wait behind a less urgent one.
Hard, firm and soft: what a missed deadline costs
Real-time requirements differ in what a missed deadline costs, from a failed system to a glitch. The ROS 2 design article sorts them three ways:
| Requirement | A missed deadline | Its examples |
|---|---|---|
| hard real-time: A real-time requirement under which a missed deadline counts as a failure of the system, as in an autopilot, so every deadline must be met and proved in advance. | is a failure of the system | autopilots, spacecraft, planetary rovers |
| firm real-time: A real-time requirement under which a result that comes after its deadline is worthless, though the system carries on, as on a robotic assembly line. | makes the late result worthless, but the system carries on | financial forecasts, robotic assembly lines |
| soft real-time: A real-time requirement under which a missed deadline lowers the quality of service without failing the system, as in audio and video playback. | lowers the quality of service | audio and video playback |
Real-time scheduling theory serves hard requirements: it proves in advance that no deadline will be missed. It needs each task’s worst-case execution time: The longest a job of a task can take on the CPU when it runs without interruption. Real-time analysis uses it rather than the average, because one slow job can miss a deadline., never its average, because one slow job is enough to miss.
Checkpoint
Check: Hard, firm and soft real-time requirements
Rate-monotonic priorities need headroom; EDF can fill the CPU
The theory models each task as periodic: a job arrives every T ms, needs at most C ms of CPU and must finish before the next one arrives, so its deadline is the end of its period. RTEMS’s documentation lists these assumptions, with tasks independent and preemptible. A task’s utilisation: The share of one CPU a periodic task needs: its worst-case execution time divided by its period. A task set's utilisation is the sum over its tasks. is C ÷ T, the share of one CPU it needs, and a set’s utilisation U is the sum.
rate-monotonic scheduling: Fixed-priority scheduling of periodic tasks in which the shorter a task's period, the higher its priority. fixes priorities by period: the shorter the period, the higher the priority. It is the optimal fixed-priority rule for such tasks on one CPU: if any fixed priorities meet every deadline, rate-monotonic ones do. earliest deadline first (EDF): Scheduling that always runs the ready job whose deadline is nearest, so priorities change as jobs arrive. On one CPU it meets every deadline whenever the utilisation is at most 1., earliest deadline first, runs the ready job whose deadline is nearest. The program runs both rules on two tasks for 35 ms, after which both periods line up again: T1 needs 2 ms every 5 ms and T2 4 ms every 7 ms. Its 14 versions printed the same lines:
Python 3.14 · main.py
def simulate(tasks: list[tuple[int, int]], horizon: int, rule: str) -> tuple[str, str]:
"""One CPU in 1 ms steps: the timeline and the first missed deadline."""
left = [0] * len(tasks) # work left in each current job
deadline = [0] * len(tasks)
def key(i: int) -> tuple[int, int]:
# RM: the shorter period first; EDF: the earlier deadline. Ties: T1 first.
return (tasks[i][1] if rule == "RM" else deadline[i], i)
timeline = ""
for t in range(horizon + 1):
for i, (work, period) in enumerate(tasks):
if t % period == 0:
if left[i]:
return (
timeline,
f"T{i + 1} misses its deadline at {t} ms, {left[i]} ms short",
)
left[i], deadline[i] = work, t + period
if t == horizon:
break
pick = min((i for i in range(len(tasks)) if left[i]), key=key, default=-1)
if pick >= 0:
left[pick] -= 1
timeline += str(pick + 1) if pick >= 0 else "."
return timeline, f"no deadline missed in {horizon} ms"
tasks, horizon = [(2, 5), (4, 7)], 35 # (work, period) in ms, and the hyperperiod
print("tasks (work/period):", " ".join(f"{w}/{p}" for w, p in tasks))
for rule in ["RM", "EDF"]:
timeline, verdict = simulate(tasks, horizon, rule)
print(f" {rule:<3} {timeline}\n {verdict}") In Python: min over the tasks with work left, keyed on (period or deadline, index), gives a tie to T1, and default=-1 marks a millisecond with nothing to run.
Recorded with CPython 3.14.8 on macOS 26 arm64. It runs in your browser with Pyodide 314.0.7 (CPython 3.14.2). To run it yourself: python3 main.py
JavaScript Node 24 · main.mjs
// One CPU in 1 ms steps: the timeline and the first missed deadline.
function simulate(tasks, horizon, rule) {
const left = tasks.map(() => 0); // work left in each current job
const deadline = tasks.map(() => 0);
let timeline = "";
for (let t = 0; t <= horizon; t++) {
for (const [i, [work, period]] of tasks.entries()) {
if (t % period !== 0) continue;
if (left[i] > 0)
return [timeline, `T${i + 1} misses its deadline at ${t} ms, ${left[i]} ms short`];
[left[i], deadline[i]] = [work, t + period];
}
if (t === horizon) break;
// RM: the shorter period first; EDF: the earlier deadline. Ties: T1 first.
const key = (i) => (rule === "RM" ? tasks[i][1] : deadline[i]);
let pick = -1;
for (let i = 0; i < tasks.length; i++) {
if (left[i] > 0 && (pick < 0 || key(i) < key(pick))) pick = i;
}
if (pick >= 0) left[pick]--;
timeline += pick >= 0 ? String(pick + 1) : ".";
}
return [timeline, `no deadline missed in ${horizon} ms`];
}
const tasks = [
[2, 5],
[4, 7],
]; // (work, period) in ms
const horizon = 35; // the hyperperiod, when both periods end together
console.log(`tasks (work/period): ${tasks.map(([work, period]) => `${work}/${period}`).join(" ")}`);
for (const rule of ["RM", "EDF"]) {
const [timeline, verdict] = simulate(tasks, horizon, rule);
console.log(` ${rule.padEnd(3)} ${timeline}\n ${verdict}`);
} In JavaScript: The loop keeps a task only when its key is strictly smaller than the best so far, so a tie goes to T1; a destructuring assignment releases a job in one statement.
Recorded with Node.js 24.21.0 LTS (V8 13.6) on macOS 26 arm64. It runs in your browser with QuickJS (quickjs-emscripten 0.32.0). To run it yourself: node main.mjs
Java 25 LTS · Main.java
import java.util.ArrayList;
import java.util.List;
import java.util.function.IntUnaryOperator;
public class Main {
record Task(int work, int period) {} // in ms
// One CPU in 1 ms steps: the timeline and the first missed deadline.
static String[] simulate(List<Task> tasks, int horizon, String rule) {
int[] left = new int[tasks.size()]; // work left in each current job
int[] deadline = new int[tasks.size()];
// RM: the shorter period first; EDF: the earlier deadline. Ties: T1 first.
IntUnaryOperator key = i -> rule.equals("RM") ? tasks.get(i).period() : deadline[i];
StringBuilder timeline = new StringBuilder();
for (int t = 0; t <= horizon; t++) {
for (int i = 0; i < tasks.size(); i++) {
if (t % tasks.get(i).period() != 0) {
continue;
}
if (left[i] > 0) {
String miss = "T" + (i + 1) + " misses its deadline at " + t + " ms, " + left[i];
return new String[] {timeline.toString(), miss + " ms short"};
}
left[i] = tasks.get(i).work();
deadline[i] = t + tasks.get(i).period();
}
if (t == horizon) {
break;
}
int pick = -1;
for (int i = 0; i < tasks.size(); i++) {
if (left[i] > 0 && (pick < 0 || key.applyAsInt(i) < key.applyAsInt(pick))) {
pick = i;
}
}
if (pick >= 0) {
left[pick]--;
}
timeline.append(pick >= 0 ? String.valueOf(pick + 1) : ".");
}
return new String[] {timeline.toString(), "no deadline missed in " + horizon + " ms"};
}
public static void main(String[] args) {
List<Task> tasks = List.of(new Task(2, 5), new Task(4, 7));
int horizon = 35; // the hyperperiod, when both periods end together
List<String> shown = new ArrayList<>();
tasks.forEach(t -> shown.add(t.work() + "/" + t.period()));
System.out.println("tasks (work/period): " + String.join(" ", shown));
for (String rule : List.of("RM", "EDF")) {
String[] run = simulate(tasks, horizon, rule);
System.out.println(" " + rule + (rule.length() < 3 ? " " : "") + " " + run[0]);
System.out.println(" " + run[1]);
}
}
} In Java: A record holds each task and an IntUnaryOperator the key of the rule; simulate returns the timeline and the verdict in a two-element array.
Recorded with OpenJDK 25.0.4.1 LTS (Eclipse Temurin) on macOS 26 arm64. It runs in your browser with a TeaVM 0.16.0 build in QuickJS (quickjs-emscripten 0.32.0). To run it yourself: java Main.java
C++23 · main.cpp
#include <format>
#include <print>
#include <string>
#include <utility>
#include <vector>
struct Task {
int work, period; // in ms
};
// One CPU in 1 ms steps: the timeline and the first missed deadline.
std::pair<std::string, std::string>
simulate(const std::vector<Task> &tasks, int horizon, const std::string &rule) {
std::vector<int> left(tasks.size()); // work left in each current job
std::vector<int> deadline(tasks.size());
// RM: the shorter period first; EDF: the earlier deadline. Ties: T1 first.
auto key = [&](std::size_t i) {
return rule == "RM" ? tasks[i].period : deadline[i];
};
std::string timeline;
for (int t = 0; t <= horizon; ++t) {
for (std::size_t i = 0; i < tasks.size(); ++i) {
if (t % tasks[i].period != 0) {
continue;
}
if (left[i] > 0) {
return {timeline,
std::format("T{} misses its deadline at {} ms, {} ms short",
i + 1, t, left[i])};
}
left[i] = tasks[i].work;
deadline[i] = t + tasks[i].period;
}
if (t == horizon) {
break;
}
std::size_t pick = tasks.size();
for (std::size_t i = 0; i < tasks.size(); ++i) {
if (left[i] > 0 && (pick == tasks.size() || key(i) < key(pick))) {
pick = i;
}
}
if (pick < tasks.size()) {
--left[pick];
}
timeline += pick < tasks.size() ? std::to_string(pick + 1) : ".";
}
return {timeline, std::format("no deadline missed in {} ms", horizon)};
}
int main() {
const std::vector<Task> tasks = {{2, 5}, {4, 7}};
const int horizon = 35; // the hyperperiod, when both periods end together
std::string shown;
for (const Task &task : tasks) {
shown += std::format(" {}/{}", task.work, task.period);
}
std::println("tasks (work/period):{}", shown);
for (const std::string rule : {"RM", "EDF"}) {
const auto [timeline, verdict] = simulate(tasks, horizon, rule);
std::println(" {:<3} {}\n {}", rule, timeline, verdict);
}
} In C++: simulate returns a std::pair that main takes apart with a structured binding, and std::format builds the verdict that std::println prints.
Recorded with Apple clang 21.0.0 (C++23) and GCC 16.2.0 on macOS 26 arm64. It runs in your browser with Clang in WebAssembly (YoWASP) 22.0.0-git20542-10. To run it yourself: clang++ -std=c++23 -O2 -Wall -Wextra -Werror -pedantic -fsanitize=address,undefined main.cpp -o main && ./main
C# 14 · .NET 10 · app.cs
Task[] tasks = [new(2, 5), new(4, 7)];
int horizon = 35; // the hyperperiod, when both periods end together
var shown = tasks.Select(t => $"{t.Work}/{t.Period}");
Console.WriteLine($"tasks (work/period): {string.Join(" ", shown)}");
foreach (string rule in new[] { "RM", "EDF" })
{
var (timeline, verdict) = Simulate(tasks, horizon, rule);
Console.WriteLine($" {rule,-3} {timeline}\n {verdict}");
}
// One CPU in 1 ms steps: the timeline and the first missed deadline.
static (string, string) Simulate(Task[] tasks, int horizon, string rule)
{
int[] left = new int[tasks.Length]; // work left in each current job
int[] deadline = new int[tasks.Length];
// RM: the shorter period first; EDF: the earlier deadline. Ties: T1 first.
int Key(int i) => rule == "RM" ? tasks[i].Period : deadline[i];
string timeline = "";
for (int t = 0; t <= horizon; t++)
{
for (int i = 0; i < tasks.Length; i++)
{
if (t % tasks[i].Period != 0)
{
continue;
}
if (left[i] > 0)
{
return (timeline, $"T{i + 1} misses its deadline at {t} ms, {left[i]} ms short");
}
(left[i], deadline[i]) = (tasks[i].Work, t + tasks[i].Period);
}
if (t == horizon)
{
break;
}
int pick = -1;
for (int i = 0; i < tasks.Length; i++)
{
if (left[i] > 0 && (pick < 0 || Key(i) < Key(pick)))
{
pick = i;
}
}
if (pick >= 0)
{
left[pick]--;
}
timeline += pick >= 0 ? $"{pick + 1}" : ".";
}
return (timeline, $"no deadline missed in {horizon} ms");
}
record Task(int Work, int Period); // in ms In C#: The top-level statements come first and the local function Simulate after them; a tuple assignment releases a job in one statement.
Recorded with C# 14 on .NET 10.0.12 (SDK 10.0.401) on macOS 26 arm64. It runs in your browser with the .NET runtime for WebAssembly 10.0.12. To run it yourself: dotnet run app.cs
C23 · main.c
#include <stdio.h>
enum { MAX_TASKS = 8 };
struct task {
int work, period; // in ms
};
// One CPU in 1 ms steps: prints the timeline and the first missed deadline.
static void simulate(const struct task tasks[], int n, int horizon, bool rm) {
int left[MAX_TASKS] = {0}; // work left in each current job
int deadline[MAX_TASKS] = {0};
printf(" %-3s ", rm ? "RM" : "EDF");
for (int t = 0; t <= horizon; t++) {
for (int i = 0; i < n; i++) {
if (t % tasks[i].period != 0) {
continue;
}
if (left[i] > 0) {
printf("\n T%d misses its deadline at %d ms, %d ms short\n", i + 1,
t, left[i]);
return;
}
left[i] = tasks[i].work;
deadline[i] = t + tasks[i].period;
}
if (t == horizon) {
break;
}
// RM: the shorter period first; EDF: the earlier deadline. Ties: T1 first.
int pick = -1;
for (int i = 0; i < n; i++) {
const int key = rm ? tasks[i].period : deadline[i];
if (left[i] > 0 &&
(pick < 0 || key < (rm ? tasks[pick].period : deadline[pick]))) {
pick = i;
}
}
if (pick >= 0) {
left[pick]--;
}
putchar(pick >= 0 ? '1' + pick : '.');
}
printf("\n no deadline missed in %d ms\n", horizon);
}
int main(void) {
static const struct task tasks[] = {{2, 5}, {4, 7}};
const int n = (int)(sizeof tasks / sizeof tasks[0]);
const int horizon = 35; // the hyperperiod, when both periods end together
printf("tasks (work/period):");
for (int i = 0; i < n; i++) {
printf(" %d/%d", tasks[i].work, tasks[i].period);
}
printf("\n");
simulate(tasks, n, horizon, true);
simulate(tasks, n, horizon, false);
return 0;
} In C: simulate prints the timeline as it goes instead of building a string, so a missed deadline ends the line early; putchar prints the task number as a character.
Recorded with Apple clang 21.0.0 (C23) and GCC 16.2.0 on macOS 26 arm64. It runs in your browser with Clang in WebAssembly (YoWASP) 22.0.0-git20542-10. To run it yourself: clang -std=c23 -O2 -Wall -Wextra -Werror -pedantic -fsanitize=address,undefined main.c -o main && ./main
PHP 8.5 · main.php
<?php
declare(strict_types=1);
/**
* One CPU in 1 ms steps: the timeline and the first missed deadline.
* @param list<array{int, int}> $tasks [work, period] in ms
* @return array{string, string}
*/
function simulate(array $tasks, int $horizon, string $rule): array
{
$left = array_fill(0, count($tasks), 0); // work left in each current job
$deadline = $left;
$timeline = '';
for ($t = 0; $t <= $horizon; $t++) {
foreach ($tasks as $i => [$work, $period]) {
if ($t % $period !== 0) {
continue;
}
if ($left[$i] > 0) {
$miss = 'T' . ($i + 1) . " misses its deadline at {$t} ms, {$left[$i]} ms short";
return [$timeline, $miss];
}
[$left[$i], $deadline[$i]] = [$work, $t + $period];
}
if ($t === $horizon) {
break;
}
// RM: the shorter period first; EDF: the earlier deadline. Ties: T1 first.
$key = fn(int $i): int => $rule === 'RM' ? $tasks[$i][1] : $deadline[$i];
$pick = -1;
foreach (array_keys($tasks) as $i) {
if ($left[$i] > 0 && ($pick < 0 || $key($i) < $key($pick))) {
$pick = $i;
}
}
if ($pick >= 0) {
$left[$pick]--;
}
$timeline .= $pick >= 0 ? (string) ($pick + 1) : '.';
}
return [$timeline, "no deadline missed in {$horizon} ms"];
}
$tasks = [[2, 5], [4, 7]];
$horizon = 35; // the hyperperiod, when both periods end together
$shown = array_map(fn(array $task): string => "{$task[0]}/{$task[1]}", $tasks);
echo 'tasks (work/period): ' . implode(' ', $shown) . "\n";
foreach (['RM', 'EDF'] as $rule) {
[$timeline, $verdict] = simulate($tasks, $horizon, $rule);
printf(" %-3s %s\n %s\n", $rule, $timeline, $verdict);
} In PHP: An arrow function (fn) copies the variables it uses when it is created, so $key is created again every millisecond to see the current deadlines.
Recorded with PHP 8.5.11 (CLI) on macOS 26 arm64. It runs in your browser with php-wasm 0.2.0 (PHP 8.5.2). To run it yourself: php main.php
Rust 1.99 · main.rs
// One CPU in 1 ms steps: the timeline and the first missed deadline.
fn simulate(tasks: &[(u32, u32)], horizon: u32, rule: &str) -> (String, String) {
let mut left = vec![0; tasks.len()]; // work left in each current job
let mut deadline = vec![0; tasks.len()];
let mut timeline = String::new();
for t in 0..=horizon {
for (i, &(work, period)) in tasks.iter().enumerate() {
if t % period != 0 {
continue;
}
if left[i] > 0 {
let miss = format!(
"T{} misses its deadline at {t} ms, {} ms short",
i + 1,
left[i]
);
return (timeline, miss);
}
(left[i], deadline[i]) = (work, t + period);
}
if t == horizon {
break;
}
// RM: the shorter period first; EDF: the earlier deadline. Ties: T1 first.
let key = |i: usize| {
if rule == "RM" {
tasks[i].1
} else {
deadline[i]
}
};
match (0..tasks.len())
.filter(|&i| left[i] > 0)
.min_by_key(|&i| (key(i), i))
{
Some(pick) => {
left[pick] -= 1;
timeline += &(pick + 1).to_string();
}
None => timeline.push('.'),
}
}
(timeline, format!("no deadline missed in {horizon} ms"))
}
fn main() {
let tasks = [(2, 5), (4, 7)]; // (work, period) in ms
let horizon = 35; // the hyperperiod, when both periods end together
let shown: Vec<String> = tasks.iter().map(|(w, p)| format!("{w}/{p}")).collect();
println!("tasks (work/period): {}", shown.join(" "));
for rule in ["RM", "EDF"] {
let (timeline, verdict) = simulate(&tasks, horizon, rule);
println!(" {rule:<3} {timeline}\n {verdict}");
}
} In Rust: min_by_key on (key, index) returns the first minimum, which gives a tie to T1, and the match handles the idle millisecond as None.
Recorded with rustc 1.99.0 (edition 2024) on macOS 26 arm64. It runs in your browser with a WebAssembly build on the WASI runner (browser_wasi_shim 0.4.2). To run it yourself: rustc --edition 2024 -O main.rs && ./main
TypeScript 6.0 · main.ts
type Task = [work: number, period: number]; // in ms
// One CPU in 1 ms steps: the timeline and the first missed deadline.
function simulate(tasks: Task[], horizon: number, rule: string): [string, string] {
const left = tasks.map(() => 0); // work left in each current job
const deadline = tasks.map(() => 0);
let timeline = "";
for (let t = 0; t <= horizon; t++) {
for (const [i, [work, period]] of tasks.entries()) {
if (t % period !== 0) continue;
if (left[i] > 0)
return [timeline, `T${i + 1} misses its deadline at ${t} ms, ${left[i]} ms short`];
[left[i], deadline[i]] = [work, t + period];
}
if (t === horizon) break;
// RM: the shorter period first; EDF: the earlier deadline. Ties: T1 first.
const key = (i: number): number => (rule === "RM" ? tasks[i][1] : deadline[i]);
let pick = -1;
for (let i = 0; i < tasks.length; i++) {
if (left[i] > 0 && (pick < 0 || key(i) < key(pick))) pick = i;
}
if (pick >= 0) left[pick]--;
timeline += pick >= 0 ? String(pick + 1) : ".";
}
return [timeline, `no deadline missed in ${horizon} ms`];
}
const tasks: Task[] = [
[2, 5],
[4, 7],
];
const horizon = 35; // the hyperperiod, when both periods end together
console.log(`tasks (work/period): ${tasks.map(([work, period]) => `${work}/${period}`).join(" ")}`);
for (const rule of ["RM", "EDF"]) {
const [timeline, verdict] = simulate(tasks, horizon, rule);
console.log(` ${rule.padEnd(3)} ${timeline}\n ${verdict}`);
} In TypeScript: A labelled tuple type, [work: number, period: number], names the two numbers of each task, and simulate returns a pair of strings.
Recorded with TypeScript 6.0.3 (strict), run by Node.js 24.21.0 on macOS 26 arm64. It runs in your browser with QuickJS (quickjs-emscripten 0.32.0) after Sucrase 3.35.1. To run it yourself: npx -p typescript@6.0.3 tsc --noEmit --strict main.ts && node main.ts
Go 1.27 · main.go
package main
import (
"fmt"
"strconv"
)
type task struct{ work, period int } // in ms
// simulate runs one CPU in 1 ms steps and returns the timeline and the first missed deadline.
func simulate(tasks []task, horizon int, rule string) (string, string) {
left := make([]int, len(tasks)) // work left in each current job
deadline := make([]int, len(tasks))
// RM: the shorter period first; EDF: the earlier deadline. Ties: T1 first.
key := func(i int) int {
if rule == "RM" {
return tasks[i].period
}
return deadline[i]
}
timeline := ""
for t := 0; t <= horizon; t++ {
for i, tk := range tasks {
if t%tk.period != 0 {
continue
}
if left[i] > 0 {
miss := fmt.Sprintf("T%d misses its deadline at %d ms, %d ms short",
i+1, t, left[i])
return timeline, miss
}
left[i], deadline[i] = tk.work, t+tk.period
}
if t == horizon {
break
}
pick := -1
for i := range tasks {
if left[i] > 0 && (pick < 0 || key(i) < key(pick)) {
pick = i
}
}
if pick < 0 {
timeline += "."
continue
}
left[pick]--
timeline += strconv.Itoa(pick + 1)
}
return timeline, fmt.Sprintf("no deadline missed in %d ms", horizon)
}
func main() {
tasks := []task{{2, 5}, {4, 7}}
horizon := 35 // the hyperperiod, when both periods end together
fmt.Print("tasks (work/period):")
for _, tk := range tasks {
fmt.Printf(" %d/%d", tk.work, tk.period)
}
fmt.Println()
for _, rule := range []string{"RM", "EDF"} {
timeline, verdict := simulate(tasks, horizon, rule)
fmt.Printf(" %-3s %s\n %s\n", rule, timeline, verdict)
}
} In Go: key is a closure over rule and deadline, and simulate returns two strings, the Go way to return a pair.
Recorded with Go 1.27.1 on macOS 26 arm64. It runs in your browser with the Go compiler in WebAssembly 1.27.1. To run it yourself: go run main.go
Swift 6.4 · main.swift
/// One CPU in 1 ms steps: the timeline and the first missed deadline.
func simulate(_ tasks: [(work: Int, period: Int)], _ horizon: Int, _ rule: String) -> (
String, String
) {
var left = [Int](repeating: 0, count: tasks.count) // work left in each current job
var deadline = left
// RM: the shorter period first; EDF: the earlier deadline. Ties: T1 first.
let key = { (i: Int) in rule == "RM" ? tasks[i].period : deadline[i] }
var timeline = ""
for t in 0...horizon {
for (i, task) in tasks.enumerated() where t % task.period == 0 {
if left[i] > 0 {
return (timeline, "T\(i + 1) misses its deadline at \(t) ms, \(left[i]) ms short")
}
(left[i], deadline[i]) = (task.work, t + task.period)
}
if t == horizon { break }
guard let pick = tasks.indices.filter({ left[$0] > 0 }).min(by: { key($0) < key($1) }) else {
timeline += "."
continue
}
left[pick] -= 1
timeline += String(pick + 1)
}
return (timeline, "no deadline missed in \(horizon) ms")
}
let tasks = [(work: 2, period: 5), (work: 4, period: 7)]
let horizon = 35 // the hyperperiod, when both periods end together
print("tasks (work/period): \(tasks.map { "\($0.work)/\($0.period)" }.joined(separator: " "))")
for rule in ["RM", "EDF"] {
let (timeline, verdict) = simulate(tasks, horizon, rule)
print(" \(rule)\(rule.count < 3 ? " " : "") \(timeline)\n \(verdict)")
} In Swift: The where clause of the inner loop releases jobs only at multiples of the period, and guard let handles the idle millisecond.
Recorded with Apple Swift 6.4 on macOS 26 arm64. It runs in your browser with a WebAssembly build on the WASI runner (browser_wasi_shim 0.4.2). To run it yourself: swiftc -O main.swift -o main && ./main
Ruby 4.0 · main.rb
# frozen_string_literal: true
# One CPU in 1 ms steps: the timeline and the first missed deadline.
class Simulation
def initialize(tasks, rule)
@tasks = tasks # [work, period] in ms
@rule = rule
@left = [0] * tasks.size # work left in each current job
@deadline = [0] * tasks.size
end
def call(horizon)
timeline = +''
(0..horizon).each do |t|
missed = release(t)
return [timeline, missed] if missed
break if t == horizon
timeline << step
end
[timeline, "no deadline missed in #{horizon} ms"]
end
private
def release(time)
@tasks.each_with_index do |(work, period), i|
next unless (time % period).zero?
miss = "T#{i + 1} misses its deadline at #{time} ms, #{@left[i]} ms short"
return miss if @left[i].positive?
@left[i] = work
@deadline[i] = time + period
end
nil
end
# RM: the shorter period first; EDF: the earlier deadline. Ties: T1 first.
def step
pick = @tasks.each_index.select { |i| @left[i].positive? }.min_by { |i| [key(i), i] }
return '.' unless pick
@left[pick] -= 1
(pick + 1).to_s
end
def key(index) = @rule == 'RM' ? @tasks[index][1] : @deadline[index]
end
tasks = [[2, 5], [4, 7]] # [work, period] in ms
horizon = 35 # the hyperperiod, when both periods end together
puts "tasks (work/period): #{tasks.map { |work, period| "#{work}/#{period}" }.join(' ')}"
%w[RM EDF].each do |rule|
timeline, verdict = Simulation.new(tasks, rule).call(horizon)
puts " #{rule.ljust(3)} #{timeline}", " #{verdict}"
end In Ruby: A small class with one method per step keeps each method short, as RuboCop asks, and min_by with [key, index] gives a tie to T1.
Recorded with CRuby 4.0.7 on macOS 26 arm64. It runs in your browser with ruby.wasm 2.10.1 (Ruby 4.0.0). To run it yourself: ruby main.rb
Kotlin 2.4 · Main.kt
data class Task(
val work: Int,
val period: Int,
) // in ms
// One CPU in 1 ms steps: the timeline and the first missed deadline.
fun simulate(
tasks: List<Task>,
horizon: Int,
rule: String,
): Pair<String, String> {
val left = IntArray(tasks.size) // work left in each current job
val deadline = IntArray(tasks.size)
// RM: the shorter period first; EDF: the earlier deadline. Ties: T1 first.
val key = { i: Int -> if (rule == "RM") tasks[i].period else deadline[i] }
val timeline = StringBuilder()
for (t in 0..horizon) {
for ((i, task) in tasks.withIndex()) {
if (t % task.period != 0) continue
val miss = "T${i + 1} misses its deadline at $t ms, ${left[i]} ms short"
if (left[i] > 0) return "$timeline" to miss
left[i] = task.work
deadline[i] = t + task.period
}
if (t == horizon) break
val pick = tasks.indices.filter { left[it] > 0 }.minByOrNull(key)
if (pick != null) left[pick]--
timeline.append(if (pick != null) "${pick + 1}" else ".")
}
return "$timeline" to "no deadline missed in $horizon ms"
}
fun main() {
val tasks = listOf(Task(2, 5), Task(4, 7))
val horizon = 35 // the hyperperiod, when both periods end together
println("tasks (work/period): ${tasks.joinToString(" ") { "${it.work}/${it.period}" }}")
for (rule in listOf("RM", "EDF")) {
val (timeline, verdict) = simulate(tasks, horizon, rule)
println(" ${rule.padEnd(3)} $timeline\n $verdict")
}
} In Kotlin: minByOrNull returns the first task with the smallest key, so a tie goes to T1, and null marks an idle millisecond.
Recorded with Kotlin 2.4.20 on Java 25.0.4.1 on macOS 26 arm64. It runs in your browser with a WebAssembly build on the WASI runner (browser_wasi_shim 0.4.2). To run it yourself: kotlinc Main.kt -include-runtime -d main.jar && java -jar main.jar
Dart 3.13 · main.dart
typedef Task = ({int work, int period}); // in ms
/// One CPU in 1 ms steps: the timeline and the first missed deadline.
(String, String) simulate(List<Task> tasks, int horizon, String rule) {
final left = List.filled(tasks.length, 0); // work left in each current job
final deadline = List.filled(tasks.length, 0);
// RM: the shorter period first; EDF: the earlier deadline. Ties: T1 first.
int key(int i) => rule == 'RM' ? tasks[i].period : deadline[i];
var timeline = '';
for (var t = 0; t <= horizon; t++) {
for (final (i, task) in tasks.indexed) {
if (t % task.period != 0) continue;
if (left[i] > 0) {
return (
timeline,
'T${i + 1} misses its deadline at $t ms, ${left[i]} ms short',
);
}
left[i] = task.work;
deadline[i] = t + task.period;
}
if (t == horizon) break;
var pick = -1;
for (var i = 0; i < tasks.length; i++) {
if (left[i] > 0 && (pick < 0 || key(i) < key(pick))) pick = i;
}
if (pick >= 0) left[pick]--;
timeline += pick >= 0 ? '${pick + 1}' : '.';
}
return (timeline, 'no deadline missed in $horizon ms');
}
void main() {
final List<Task> tasks = [(work: 2, period: 5), (work: 4, period: 7)];
const horizon = 35; // the hyperperiod, when both periods end together
print(
'tasks (work/period): ${tasks.map((t) => '${t.work}/${t.period}').join(' ')}',
);
for (final rule in ['RM', 'EDF']) {
final (timeline, verdict) = simulate(tasks, horizon, rule);
print(' ${rule.padRight(3)} $timeline\n $verdict');
}
} In Dart: A record typedef names the work and period of each task, and simulate returns a record of two strings that main takes apart with a pattern.
Recorded with Dart 3.13.5 on macOS 26 arm64. It runs in your browser with a WebAssembly build by dart2wasm 3.13.5. To run it yourself: dart run main.dart
What every language printed
tasks (work/period): 2/5 4/7
RM 1122211
T2 misses its deadline at 7 ms, 1 ms short
EDF 1122221122221121122211222211221122.
no deadline missed in 35 ms
Runs on this device, in your browser. The first run downloads Python (about 13.5 MB), which is kept for the next runs.
Runs on this device, in your browser. The first run downloads Python (about 13.5 MB), which is kept for the next runs. Runs on this device, in your browser. The first run downloads JavaScript (about 0.6 MB), which is kept for the next runs. Runs on this device: a JavaScript build of this program (about 11 KB), made when the page was built. The first run also downloads the JavaScript runner (about 0.6 MB), which is kept for the next runs. To change the code, copy it and run it with the command below. Runs on this device: a WebAssembly build of this program (about 152 KB), made when the page was built. Edit runs your changes in the browser: the first edited run downloads the C and C++ compiler (about 29.1 MB), which is kept for the next runs. It is best on a computer. Runs on this device, in your browser. The first run downloads the C and C++ compiler (about 29.1 MB), which is kept for the next runs. It is best on a computer. Runs on this device: a .NET build of this program (about 4 KB), made when the page was built. The first run also downloads .NET (about 3.6 MB), which is kept for the next runs. Edit runs your changes in the browser: the first edited run downloads .NET (about 8.9 MB), which is kept for the next runs. Runs on this device, in your browser. The first run downloads .NET (about 8.9 MB), which is kept for the next runs. Runs on this device: a WebAssembly build of this program (about 8 KB), made when the page was built. Edit runs your changes in the browser: the first edited run downloads the C and C++ compiler (about 29.1 MB), which is kept for the next runs. It is best on a computer. Runs on this device, in your browser. The first run downloads the C and C++ compiler (about 29.1 MB), which is kept for the next runs. It is best on a computer. Runs on this device, in your browser. The first run downloads PHP (about 4.3 MB), which is kept for the next runs. Runs on this device: a WebAssembly build of this program (about 21 KB), made when the page was built. To change the code, copy it and run it with the command below. Runs on this device, in your browser. The first run downloads the JavaScript runner (about 0.9 MB), which is kept for the next runs. Runs on this device: a WebAssembly build of this program (about 717 KB), made when the page was built. Edit runs your changes in the browser: the first edited run downloads the Go compiler (about 18.7 MB), which is kept for the next runs. Runs on this device, in your browser. The first run downloads the Go compiler (about 18.7 MB), which is kept for the next runs. Runs on this device: a WebAssembly build of this program (about 1.7 MB), made when the page was built. To change the code, copy it and run it with the command below. Runs on this device, in your browser. The first run downloads Ruby (about 10.2 MB), which is kept for the next runs. Runs on this device: a WebAssembly build of this program (about 183 KB), made when the page was built. To change the code, copy it and run it with the command below. Runs on this device: a WebAssembly build of this program (about 15 KB), made when the page was built. To change the code, copy it and run it with the command below.Your run, in this browser
Under rate-monotonic priorities, T1’s second job preempts T2 at 5 ms, and T2 reaches its deadline at 7 ms with 1 ms of work left. EDF lets T2 run on at 5 ms, because its deadline of 7 ms is nearer than T1’s new one of 10 ms, and meets every deadline.
Liu and Layland proved two tests for this model in 1973. Rate-monotonic priorities meet every deadline of n tasks if U ≤ n(2^(1/n) − 1): 1 for one task, 0.828 for two and 0.780 for three, falling towards ln 2, about 0.693. EDF on one CPU meets every deadline if and only if U ≤ 1, as the kernel’s deadline documentation also states. The program’s tasks have U = 2/5 + 4/7 = 0.971, above 0.828 and below 1, so only EDF had a guarantee. The bound is sufficient, not necessary: RTEMS notes that, on average, random task sets stay schedulable up to a utilisation of about 0.88. For fixed priorities, response-time analysis decides the sets that the bound leaves open.
Checkpoint
Check: Rate-monotonic and EDF scheduling and their utilisation tests
Priority inversion: a lock that reverses priorities
A high-priority task H that needs a lock held by a low-priority task L must wait for L to release it, a short wait as long as L runs. If a medium-priority task M becomes ready, it preempts L, and H waits for M as well, though M never touches the lock. This is priority inversion: A high-priority task waiting while lower-priority tasks run, typically because it needs a lock that a low-priority task holds while a medium-priority task keeps that task off the CPU., and the kernel’s rt-mutex design document calls it unbounded when M can keep L off the CPU for an undetermined time:
The drawing shows the whole scene; its 7 steps are listed below.
Every step as text
- 0 ms: L runs. Only L has arrived, so it runs its first millisecond of work, outside the lock.
- 1 ms: L takes the lock. L takes the lock and starts its critical section, 3 ms of work inside it.
- 2 ms: H preempts L. H arrives and preempts L at once, because its priority is higher. L keeps the lock while it waits.
- 3 ms: H blocks, and M runs. H needs the lock, which L holds, so H blocks. M arrives at the same moment and runs, because its priority is higher than L's. H is now waiting for M, which never touches the lock.
- 9 ms: M finishes. M's 6 ms are done, so L runs again, the rest of its critical section.
- 11 ms: L releases the lock. L leaves its critical section and releases the lock, and H takes it and runs at once.
- 13 ms: H finishes. H is done at 13 ms, 11 ms after it arrived, although it needed only 3 ms of work. L runs its last millisecond after it.
Priority inversion reset the Mars Pathfinder lander’s computer during its mission in 1997. Glenn Reeves, who led its flight software team, traced the resets to a low-priority weather task holding a lock that a high-priority bus task needed while medium-priority tasks ran. The fix was priority inheritance: A locking rule under which a task holding a lock runs at the priority of the highest-priority task waiting for that lock, until it releases it.: a task holding a lock that a higher-priority task waits for runs at the waiter’s priority, so H waits only for the rest of L’s critical section. POSIX mutexes offer it as PTHREAD_PRIO_INHERIT. A priority ceiling, the other fix, raises a task to the highest priority among the lock’s users as soon as it takes the lock.
On Linux, SCHED_FIFO and SCHED_RR give fixed priorities from 1 to 99 that run before every normal task. SCHED_DEADLINE runs EDF, holding each task to the CPU time it reserved.
Checkpoint
Check: Priority inversion and priority inheritance
Practise: test a task set
Write rm_guaranteed(tasks) and edf_schedulable(tasks), the two utilisation tests. Their sample tests include the
lesson’s two tasks and a set above the rate-monotonic bound that EDF still schedules:
Exercise · Easy · Python
Test a task set with the rate-monotonic bound and with EDF's limit
Write two functions in schedulable.py. Each takes tasks, a list of (work, period) pairs in milliseconds for periodic tasks on one CPU, each of which must finish its work before its next period starts.
rm_guaranteed(tasks)returnsTruewhen the Liu and Layland bound guarantees the set under rate-monotonic priorities: its utilisation is at most n(2^(1/n) − 1) for n tasks.edf_schedulable(tasks)returnsTruewhen EDF meets every deadline: the utilisation is at most 1.
The lesson's two tasks have a utilisation of 0.971, so rm_guaranteed([(2, 5), (4, 7)]) returns False and edf_schedulable([(2, 5), (4, 7)]) returns True.
Starter code · schedulable.py
def rm_guaranteed(tasks):
"""True when the Liu and Layland bound guarantees the (work, period) tasks under RM."""
# Replace this line with your code.
return False
def edf_schedulable(tasks):
"""True when EDF meets every deadline of the (work, period) tasks on one CPU."""
# Replace this line with your code.
return False The sample tests · test_schedulable.py
from schedulable import edf_schedulable, rm_guaranteed
def test_the_lessons_two_tasks():
"""gives no rate-monotonic guarantee at 0.971, though EDF meets every deadline"""
assert not rm_guaranteed([(2, 5), (4, 7)])
assert edf_schedulable([(2, 5), (4, 7)])
def test_a_light_set():
"""guarantees three tasks at a utilisation of 0.65, under the bound of 0.780"""
assert rm_guaranteed([(1, 4), (1, 5), (2, 10)])
assert edf_schedulable([(1, 4), (1, 5), (2, 10)])
def test_above_the_bound_and_below_one():
"""gives no guarantee to three tasks at 0.833, which EDF still schedules"""
assert not rm_guaranteed([(1, 4), (2, 6), (3, 12)])
assert edf_schedulable([(1, 4), (2, 6), (3, 12)])
def test_an_overloaded_cpu():
"""refuses a set that needs 110 % of the CPU under both rules"""
assert not rm_guaranteed([(3, 5), (3, 6)])
assert not edf_schedulable([(3, 5), (3, 6)]) A hint
A task's utilisation is its work divided by its period, and a set's utilisation is the sum over its tasks. Compare it with n * (2 ** (1 / n) - 1) for rate-monotonic priorities and with 1 for EDF, with <=: a set exactly at the limit passes.
Results of the sample tests
| Test | Result | Details |
|---|
What your code printed
The sample tests run on this device, in your browser (Pyodide): nothing is sent to mysmartcopilot.com. The first run downloads Python (about 13.5 MB), which is kept for the next runs. A check in your browser is feedback for you, not proof that the code is right for every input.
The self-check below covers all three ideas, weakest first.
Practice that adapts to you
Practise this lesson
11 questions about 6 ideas of this lesson. Each answer is explained at once, and what you get right is kept in this browser.
Response-time analysis: the exact test for fixed priorities Pro: opens with a Pro, Premium or Ultimate pass
The utilisation bound answers only yes or not proven. This part computes each task’s worst-case response time, which decides every set exactly, and works through a set that the bound cannot decide.
In this part:
- The worst case starts with every task released at once
- Worked: three tasks the bound cannot decide
- Deadlines shorter than periods
1 runnable example (Python) · about 4 minutes
Opening this part with your pass…
This part opens with any MySmartCoPilot pass.
- Pro pass · 1 week₹79$2.99
- 30 days₹179$5.99
- 1 year₹1,699$45
One payment; a pass never renews. The price shown is the total you pay: no fees or charges are added at checkout.
Passes can’t be bought right now, so this part can’t be opened at the moment. The free part above stands on its own. If you have a pass, enter your licence key.
It also opens every other in-depth part in Learn and every Pro tool. Sales are final, except where the law gives you a right to withdraw, for example in the EU and the UK (refund policy).
Inheritance and priority ceilings, worked through Pro: opens with a Pro, Premium or Ultimate pass
Priority inheritance bounds a wait once it starts, and the priority ceiling protocol raises the holder before anyone waits. This part runs the inversion with and without inheritance in 14 languages, works the ceiling protocol by hand and maps both onto POSIX mutexes and Linux.
In this part:
- The inversion, run with and without inheritance
- The ceiling protocol, by hand
- Inheritance in POSIX, in Linux and on Pathfinder
1 runnable example · about 8 minutes
Opening this part with your pass…
This part opens with any MySmartCoPilot pass.
- Pro pass · 1 week₹79$2.99
- 30 days₹179$5.99
- 1 year₹1,699$45
One payment; a pass never renews. The price shown is the total you pay: no fees or charges are added at checkout.
Passes can’t be bought right now, so this part can’t be opened at the moment. The free part above stands on its own. If you have a pass, enter your licence key.
It also opens every other in-depth part in Learn and every Pro tool. Sales are final, except where the law gives you a right to withdraw, for example in the EU and the UK (refund policy).
Linux's real-time policies, used safely Pro: opens with a Pro, Premium or Ultimate pass
Linux gives time-critical work three policies and a safety margin. This part sets each policy up, shows what stops a runaway real-time thread from locking up the machine and chooses a policy for a job.
In this part:
- SCHED_FIFO and SCHED_RR: fixed priorities from 1 to 99
- What stops a runaway real-time thread
- SCHED_DEADLINE: EDF with a budget
about 8 minutes
Opening this part with your pass…
This part opens with any MySmartCoPilot pass.
- Pro pass · 1 week₹79$2.99
- 30 days₹179$5.99
- 1 year₹1,699$45
One payment; a pass never renews. The price shown is the total you pay: no fees or charges are added at checkout.
Passes can’t be bought right now, so this part can’t be opened at the moment. The free part above stands on its own. If you have a pass, enter your licence key.
It also opens every other in-depth part in Learn and every Pro tool. Sales are final, except where the law gives you a right to withdraw, for example in the EU and the UK (refund policy).
Interview questions
-
Fresher to mid · Warm-up
What do the utilisation tests of rate-monotonic and EDF scheduling say, and why can a task set above the rate-monotonic bound still meet every deadline?
For periodic tasks on one CPU, each due by the end of its period, a set's utilisation is the sum of each task's worst-case work divided by its period. Liu and Layland showed that rate-monotonic priorities, the shorter period first, meet every deadline of n tasks if the utilisation is at most n(2^(1/n) − 1): 0.828 for two tasks and 0.780 for three, falling towards about 0.693. EDF, which runs the nearest deadline first, meets every deadline if and only if the utilisation is at most 1. The rate-monotonic test is only sufficient: it looks at nothing but the utilisation, so it must hold for the worst periods there are, and a particular set above it can still fit. The lesson's tasks of 2 ms every 5 ms and 4 ms every 7 ms, at 0.971, missed a deadline under rate-monotonic priorities and none under EDF.
-
Mid to senior
A drone's flight controller runs its control loop at the highest priority, telemetry at a medium priority and a logger at a low one. The logger and the control loop share a mutex. In flight tests the control loop sometimes misses its 5 ms deadline by tens of milliseconds. What is happening, and how do you fix it?
-
Mid to senior
Three periodic tasks share one core: 1 ms of work every 4 ms, 2 ms every 6 ms and 3 ms every 12 ms, each due by the end of its period. With fixed priorities, is the set schedulable? Show how you decide, and say what changes under EDF.
-
Senior to staff
A team runs the audio mixing thread of a small single-core Linux device under SCHED_FIFO at priority 80. A bug makes it loop forever: the device becomes almost unresponsive but never quite freezes. Explain why, and how you would run real-time threads on such devices safely.
Key takeaways
- A real-time system is judged by deadlines: a hard requirement fails on one missed deadline, a firm one wastes the late result and a soft one loses quality.
- Rate-monotonic priorities are guaranteed up to U ≤ n(2^(1/n) − 1), and EDF on one CPU up to U ≤ 1; the program’s two tasks at 0.971 missed a deadline under the first and none under the second.
- Priority inversion lets medium-priority work delay a high-priority task that waits for a lock; priority inheritance bounds the wait to the holder’s critical section.
Terms in this lesson
- earliest deadline first (EDF)
- Scheduling that always runs the ready job whose deadline is nearest, so priorities change as jobs arrive. On one CPU it meets every deadline whenever the utilisation is at most 1.
- firm real-time
- A real-time requirement under which a result that comes after its deadline is worthless, though the system carries on, as on a robotic assembly line.
- hard real-time
- A real-time requirement under which a missed deadline counts as a failure of the system, as in an autopilot, so every deadline must be met and proved in advance.
- priority inheritance
- A locking rule under which a task holding a lock runs at the priority of the highest-priority task waiting for that lock, until it releases it.
- priority inversion
- A high-priority task waiting while lower-priority tasks run, typically because it needs a lock that a low-priority task holds while a medium-priority task keeps that task off the CPU.
- rate-monotonic scheduling
- Fixed-priority scheduling of periodic tasks in which the shorter a task's period, the higher its priority.
- soft real-time
- A real-time requirement under which a missed deadline lowers the quality of service without failing the system, as in audio and video playback.
- utilisation
- The share of one CPU a periodic task needs: its worst-case execution time divided by its period. A task set's utilisation is the sum over its tasks.
- worst-case execution time
- The longest a job of a task can take on the CPU when it runs without interruption. Real-time analysis uses it rather than the average, because one slow job can miss a deadline.
References
- Scheduling Algorithms for Multiprogramming in a Hard-Real-Time Environment (C. L. Liu and James W. Layland (Journal of the ACM))
- RTEMS Classic API Guide: Rate Monotonic Manager, background (The RTEMS Project)
- RTEMS Classic API Guide: key concepts, locking protocols (The RTEMS Project)
- Finding Response Times in a Real-Time System (M. Joseph and P. Pandya (The Computer Journal))
- Priority Inheritance Protocols: An Approach to Real-Time Synchronization (L. Sha, R. Rajkumar and J. P. Lehoczky (IEEE Transactions on Computers))
- What really happened on Mars: the authoritative account of the Pathfinder resets (Glenn E. Reeves, Mars Pathfinder flight software)
- Introduction to Real-time Systems (ROS 2 Design (Open Robotics))
- Deadline Task Scheduling (The Linux Kernel Organization)
- Real-Time group scheduling (The Linux Kernel Organization)
- RT-mutex implementation design (The Linux Kernel Organization)
- RT-mutex subsystem with PI support (The Linux Kernel Organization)
- Real-time preemption: theory of operation (The Linux Kernel Organization)
- sched(7): overview of CPU scheduling (Linux man-pages project (man7.org))
- sched_setattr(2): set and get scheduling policy and attributes (Linux man-pages project (man7.org))
- chrt(1): manipulate the real-time attributes of a process (Linux man-pages project (man7.org))
- getrlimit(2): get and set resource limits, RLIMIT_RTPRIO and RLIMIT_RTTIME (Linux man-pages project (man7.org))
- pthread_mutexattr_getprotocol, pthread_mutexattr_setprotocol: the protocol attribute of mutexes (The Open Group (POSIX.1-2024))
- Linux 6.12 (PREEMPT_RT merged, deadline servers) (KernelNewbies)
- Linux 6.13 (lazy preemption) (KernelNewbies)
Report a problem with this lesson
Kept only in this browser. Your Learn progress