schedule(), and resumes whichever process is at the head of the queue.
There is no priority, no aging, and no preemption other than the periodic timer IRQ.
ProcessQueue
The queue is represented by a single struct defined inscheduler.h:
scheduler.h
Points to the last node in the circular list. The head of the queue (the process that will run next) is always
tail->next. NULL when the queue is empty.Number of processes currently in the queue.
os.c:
os.c
Scheduler API
sched_queue_init
tail to NULL and count to 0. Must be called before any other scheduler function.
scheduler.c
sched_enqueue
scheduler.c
next is set to itself — forming a one-element circular list. When the queue already has nodes, the new node is spliced between the current tail and the current head.
sched_dequeue
tail->next). Returns NULL if the queue is empty. Handles the single-element special case by setting tail to NULL.
scheduler.c
head->next is set to NULL to prevent stale pointer use.
sched_peek
NULL if the queue is empty. Useful for inspecting the next process to run without disturbing the queue state.
scheduler.c
sched_rotate
tail to tail->next, effectively moving the current head to the tail position. This rotates the queue without a dequeue/enqueue pair. Only operates when the queue has at least two elements.
scheduler.c
sched_is_empty
tail == NULL), zero otherwise.
scheduler.c
Circular list structure
The intrusivenext pointer in each Process struct links the PCBs directly without any separate list node allocation. After enqueuing p1 then p2, the structure is:
tail->next = head = p1. After one sched_rotate, tail moves to p1 and the new head becomes p2.
The schedule() function
schedule() in os.c is called from within the IRQ handler (via root.s) on every timer tick. It re-enqueues the current process and dequeues the next one:
os.c
Re-enqueue current process
If
CurrProcess is not NULL, its state is set to PROCESS_READY and it is appended to the tail of ready_queue. This gives every process a fair turn.Dequeue next process
The process at the head of the queue (
tail->next) is removed and becomes the new CurrProcess.schedule() is called from assembly (root.s) inside irq_handler after the timer interrupt is acknowledged. At that point, the outgoing process’s full register state has already been saved to its PCB, so re-enqueuing it is safe.