Rotary supports pre-emptive, software-based multitasking, allowing multiple threads of execution on a single core.
Task Structures
Each task is defined by a struct task entry, which stores:
- Type (kernel or usermode)
- State (running, waiting, killed, etc.)
- Kernel stack pointers and size
- Virtual address space and mappings
Scheduling
task_schedule() is responsible for performing all task scheduling decisions and invoking context switches. It is invoked periodically by hooking the system’s Programmable Interval Timer. The scheduler:
- Uses a round-robin algorithm, giving each task equal CPU time.
- Cleans up resources for tasks marked as
KILLED. - Calls architecture-specific code to perform context switching.
A priority-based scheduler is planned for future implementation.
Context Switching
Context switches are handled in two stages:
task_schedule()invokesarch_task_switch(), an architecture-specific function.- On x86,
arch_task_switch()updates theESP0field in the Task State Segment (TSS) to set the next task’s kernel stack.
Although hardware-assisted task switching via the TSS is possible, Rotary performs all context switching in software, only using ESP0 to set the correct stack pointer.
The assembly routine task_context_switch:
- Saves the current task’s registers onto its stack or
struct task. - Updates
ESPto point to the next task’s stack. - Restores registers before resuming execution.
This ensures a seamless transition between tasks.
Additional Reading
I’ve written up my approach in developing a multitasking implementation on my blog, which provides significantly more information and detail.