11. Context Switching - The Heart of Multitasking¶
We've built a solid foundation with drivers, graphics, and memory management. Now comes the magic: Multitasking. How can a single CPU core run multiple programs "at the same time"?
The answer is Context Switching. The OS saves the state (context) of the current task, loads the state of another task, and resumes it.
The CPU Context¶
The "context" of a running program consists mainly of its CPU registers and Stack.
On ARM64, the calling convention (AAPCS64) defines which registers must be preserved by a function call (Callee-saved registers). When we switch tasks, we only need to save these:
x19-x28: General purpose registersx29(FP): Frame Pointerx30(LR): Link Register (Return address)sp: Stack Pointer
The other registers (x0-x18) are "Caller-saved", meaning a task expects them to be overwritten if it calls a function (like schedule()), so we don't need to save them explicitly for cooperative multitasking.
Process Control Block (PCB)¶
We need a data structure to track each process.
The Switch Function (cpu_switch_to)¶
This is the most critical piece of assembly code in the OS. It takes two pointers: prev (where to save current state) and next (where to load new state).
When ret executes, it jumps to the address stored in x30 (LR). For a newly created task, we artificially set LR to the function entry point.
Cooperative Multitasking¶
In this article, we implement Cooperative Multitasking. Tasks must voluntarily yield the CPU by calling schedule().
In the next article, we'll use the System Timer to interrupt tasks automatically (Preemptive Multitasking).
Building and Testing¶
1. Build¶
2. Deploy¶
Copy multitasking_demo.img to SD card (rename to kernel8.img).
3. Expected Output¶
You will see the tasks alternating execution!
What's Next?¶
We have multiple tasks running! But they have to be "nice" and yield the CPU. In the next article, we will implement a Scheduler that uses the timer interrupt to forcibly switch tasks, creating a true time-sharing system.