Skip to content

Instantly share code, notes, and snippets.

@thomascswalker
Last active March 16, 2025 23:06
Show Gist options
  • Select an option

  • Save thomascswalker/235345323fdb2cb8e29231544583962f to your computer and use it in GitHub Desktop.

Select an option

Save thomascswalker/235345323fdb2cb8e29231544583962f to your computer and use it in GitHub Desktop.
OSDev Scheduler Problem
static uint32_t i = 0;
static uint32_t j = 0;
void process1()
{
while (1)
{
printf("Process 1 is running: %d\n", i++);
}
}
void process2()
{
while (1)
{
printf("Process 2 is running: %d\n", j++);
}
}
EXTERN void kmain(MultibootInfo* info, uint32_t magic)
{
...
Scheduler::add(process1);
Scheduler::add(process2);
while (1)
{
}
}
void PIT::callback(CPUState* regs)
{
Scheduler::schedule();
}
section .text
global switchContext
; switchContext - Switches the CPU context from the current
; process to the next process.
; Parameters:
; prev - Pointer to the CPUState structure of the current process.
; next - Pointer to the CPUState structure of the next process.
;
switchContext:
cli
; Move the pointers to prev and next from the stack into
; registers edi and esi, respectively.
mov edi, [esp + 4] ; EDI ← pointer to CPUState "prev"
mov esi, [esp + 8] ; ESI ← pointer to CPUState "next"
; -------------------------------------- ;
; Store the current CPUState (into prev) ;
; -------------------------------------- ;
; Save segment registers into prev CPUState:
mov [edi + 0], gs ; Save gs (offset 0)
mov [edi + 4], fs ; Save fs (offset 4)
mov [edi + 8], es ; Save es (offset 8)
mov [edi + 12], ds ; Save ds (offset 12)
; Save general-purpose registers using pusha.
pushfd ; Push EFLAGS on stack
pusha ; Push general-purpose registers
; At this point, the stack looks like (top-to-bottom):
; EDI, ESI, EBP, ESP, EBX, EDX, ECX, EAX, then EFLAGS.
pop dword [edi + 24] ; Pop EBP into prev->ebp
pop dword [edi + 28] ; Pop original ESP into prev->esp
pop dword [edi + 32] ; Pop EBX into prev->ebx
pop dword [edi + 36] ; Pop EDX into prev->edx
pop dword [edi + 40] ; Pop ECX into prev->ecx
pop dword [edi + 44] ; Pop EAX into prev->eax
pop dword [edi + 64] ; Pop EFLAGS into prev->eFlags
; ---------------------- ;
; Load the next CPUState ;
; ---------------------- ;
; Restore segment registers from next CPUState:
mov gs, [esi + 0] ; Load gs from next->gs
mov fs, [esi + 4] ; Load fs from next->fs
mov es, [esi + 8] ; Load es from next->es
mov ds, [esi + 12] ; Load ds from next->ds
; Restore base pointer and stack pointer from next CPUState.
mov ebp, [esi + 24] ; Next->ebp
mov esp, [esi + 28] ; Next->esp
; Restore general-purpose registers in the reverse stack order.
mov ebx, [esi + 32] ; Next->ebx
mov edx, [esi + 36] ; Next->edx
mov ecx, [esi + 40] ; Next->ecx
mov eax, [esi + 44] ; Next->eax
; Restore EFLAGS from next CPUState.
push dword [esi + 64] ; Push next->eFlags on stack
popfd ; Pop EFLAGS from stack
; --------------------------------------- ;
; Jump to the instruction pointer of next ;
; --------------------------------------- ;
sti
jmp dword [esi + 56] ; Jump to next->eip
static List<Process*> g_queue;
static uint32_t g_nextPID = 0; // Contains the next free PID
void Scheduler::add(EntryPoint func)
{
Process* process = (Process*)std::malloc(sizeof(Process));
process->func = func; // Set the function pointer
process->pid = g_nextPID++; // Assign a unique PID
process->stack = (uint8_t*)std::malloc(STACK_SIZE); // Allocate a stack for the process
CPUState* frame = (CPUState*)((uint32_t)process->stack + STACK_SIZE);
process->state = frame; // Set the CPU state for the process
// Set the instruction pointer to the function
frame->eip = (uint32_t)func;
// Set the stack pointer to the top of the stack
frame->esp = (uint32_t)(process->stack + STACK_SIZE - sizeof(CPUState));
// Set the interrupt flag (IF) and reserved flag (RF)
frame->eFlags = 0x202;
frame->userEsp = (uint32_t)(process->stack + STACK_SIZE - sizeof(CPUState));
// Set general-purpose registers
frame->ebp = 0;
frame->eax = 0;
frame->ebx = 0;
frame->ecx = 0;
frame->edx = 0;
frame->edi = 0;
frame->esi = 0;
// Set the segment registers
frame->cs = SEG_KERNEL_CODE;
frame->ss = SEG_KERNEL_DATA;
frame->ds = SEG_KERNEL_DATA;
frame->es = SEG_KERNEL_DATA;
frame->fs = SEG_KERNEL_DATA;
frame->gs = SEG_KERNEL_DATA;
g_queue.addBack(process);
}
void Scheduler::schedule()
{
if (g_queue.isEmpty())
{
return;
}
auto curr = g_queue.getFront();
if (!curr)
{
return;
}
auto next = curr->getNext();
if (curr == next)
{
return;
}
Process* currProc = curr->getValue();
Process* nextProc = next->getValue();
debug("Switching processes from %d to %d", currProc->pid, nextProc->pid);
switchContext(currProc->state, nextProc->state);
g_queue.rotate(1);
}
Process* System::Scheduler::getCurrentProcess() { return g_queue.getFront()->getValue(); }
uint32_t System::Scheduler::getProcessCount() { return g_queue.size(); }
#define SEG_KERNEL_CODE 0x08
#define SEG_KERNEL_DATA 0x10
#define SEG_USER_CODE 0x1B
#define SEG_USER_DATA 0x23
struct CPUState
{
uint32_t gs, fs, es, ds; // Offsets 0, 4, 8, 12
uint32_t edi, esi, ebp, esp, ebx, edx, ecx, eax; // OFfsets 16, 20, 24, 28, 32, 36, 40, 44
uint32_t intNo, errCode; // Offsets 48, 52
uint32_t eip, cs, eFlags, userEsp, ss; // Offsets 56, 60, 64, 68, 72
};
// Defined in `scheduling.s`.
EXTERN void switchContext(CPUState* prev, CPUState* next);
namespace Scheduler
{
void add(EntryPoint func);
void schedule();
Process* getCurrentProcess();
uint32_t getProcessCount();
}; // namespace Scheduler
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment