|
miniOS
x86_64 hobby kernel with SMP, VFS, and POSIX process model
|
#include <miniOS/sched/sched.h>#include <miniOS/syscall.h>#include <miniOS/signal.h>#include <miniOS/fs/vfs.h>#include <miniOS/fs/pipe.h>#include <miniOS/net/unix_sock.h>#include <miniOS/mm/heap.h>#include <miniOS/mm/pmm.h>#include <miniOS/mm/vmm.h>#include <miniOS/arch/x86_64/tss.h>#include <miniOS/arch/x86_64/segment.h>#include <miniOS/arch/x86_64/smp.h>#include <miniOS/arch/x86_64/spinlock.h>#include <miniOS/io.h>#include <string.h>
Functions | |
| static uint64_t | sched_rdtsc (void) |
| void | per_cpu_update_rsp0 (uint64_t rsp0) |
| Update this CPU's TSS RSP0 and cpu_t.kstack_top. | |
| void | __attribute__ ((noreturn)) |
| void | sched_init (void) |
| struct thread * | sched_create_thread (thread_func_t func, void *arg) |
| struct thread * | sched_current (void) |
| struct thread * | sched_next (void) |
| bool | sched_has_user_threads (void) |
| void | sched_signal_thread (struct thread *t, int sig) |
| void | sched_stop_current (int stop_signal) |
| void | sched_ptrace_stop (int reason) |
| void | sched_wake_tid (uint32_t tid) |
| static void | sched_schedule (void) |
| void | sched_tick (void) |
| void | sched_yield (void) |
| struct thread * | sched_create_user_task (uint64_t entry_rip) |
| void | sched_activate_task (struct thread *t) |
|
extern |
| void sched_activate_task | ( | struct thread * | t | ) |
sched_activate_task() - Transfer "current" ownership to @t without context_switch_asm. @t: Thread to activate. Must currently be in THREAD_BLOCKED state.
Sets g_current->state = THREAD_RUNNABLE, then sets g_current = t and t->state = THREAD_RUNNING. Used by kernel_main before enter_ring3() so the user task is correctly identified as "current" when it issues its first syscall or triggers a preemptive switch back to the idle thread.


| struct thread * sched_create_thread | ( | thread_func_t | func, |
| void * | arg ) |
sched_create_thread() - Allocate and initialise a kernel thread. @func: Entry function pointer.
Scans thread_pool[] for the first slot with state == THREAD_DEAD (or unused). Allocates a 16 KiB kernel stack via kmalloc. Sets initial context: RIP=func, RSP=stack_top-8, RDI=arg, CS=0x08, SS=0x10, RFLAGS=0x202. Appends thread to run queue at tail (before idle) for round-robin fairness. Thread starts THREAD_BLOCKED; caller sets THREAD_RUNNABLE when ready.


sched_create_user_task() - Create a ring-3 user thread. @entry_rip: User-mode entry point virtual address.
Calls sched_create_thread() for the kernel scaffolding, then maps USER_STACK_PAGES (64) 4 KiB pages into the user virtual address space just below USER_STACK_TOP=0x7FFFFFFFE000 using vmm_map_page with PAGE_PRESENT|PAGE_WRITE|PAGE_USER flags. Stores the physical base of the first page in user_stack_phys_base for cleanup by sched_exit_current. Sets up TSS RSP0 via tss_set_rsp0() so syscall entries use the thread's kernel stack.


| struct thread * sched_current | ( | void | ) |
sched_current() - Return pointer to the currently running thread.
Returns the global g_current pointer. Called from syscall handlers and context switch paths to identify the active task.


| bool sched_has_user_threads | ( | void | ) |
sched_has_user_threads() - Check if any non-idle thread is still alive.
Returns true if at least one thread slot with index > 0 (i.e., not the idle thread) has a state other than THREAD_DEAD. Used by bsp_idle_resume to decide whether to call qemu_exit or continue halting.

| void sched_init | ( | void | ) |
sched_init() - Initialise the scheduler.
Initialises thread_pool, sets up the run queue (circular singly-linked list with tail pointer), and captures the current boot context as the idle thread. Idle thread uses static storage (not kmalloc'd stack) and the boot stack. Sets g_current = &idle, state = THREAD_RUNNING.


| struct thread * sched_next | ( | void | ) |
sched_next() - Select the next RUNNABLE thread via round-robin.
Starts from current_thread->next and walks the circular run queue, returning the first thread in THREAD_RUNNABLE state. Starting after the current thread (not from run_queue_head) ensures fair round-robin: threads that arrive later in the queue are not starved by earlier threads that are frequently RUNNABLE (e.g. idle, net_poll busy-loop).
If no runnable thread is found after a full traversal, returns the idle thread. Does not modify current_thread.


| void sched_ptrace_stop | ( | int | reason | ) |


|
inlinestatic |

|
static |


| void sched_signal_thread | ( | struct thread * | t, |
| int | sig ) |
sched_signal_thread() - Deliver a signal to a thread, waking it if blocked. @t: Target thread. @sig: Signal number (1-31).
Sets the pending bit and, if the thread is THREAD_WAITING, transitions it to THREAD_RUNNABLE so the scheduler will pick it up. This enables waitpid and other blocking waits to return -EINTR when signalled.


| void sched_stop_current | ( | int | stop_signal | ) |


| void sched_tick | ( | void | ) |
sched_tick() - LAPIC timer ISR callback; advance the scheduler.
Increments the global tick counter. Calls sched_schedule() to perform a context switch when the current thread's quantum expires (or immediately when a higher-priority thread becomes runnable). EOI is sent by the LAPIC timer ISR before calling this function; the context switch is safe.
Context: Must only be called from the LAPIC timer ISR with interrupts disabled via the interrupt gate mechanism. Must not allocate.


| void sched_wake_tid | ( | uint32_t | tid | ) |


| void sched_yield | ( | void | ) |
sched_yield() - Voluntarily yield to the next runnable thread.
Disables interrupts around sched_schedule() to prevent the LAPIC timer from firing inside context_switch_asm during a cooperative yield.
Without this guard, the cooperative path (sched_yield from SYS_fork / sched_wait) runs with IF=1 (sti was issued in syscall_entry before syscall_dispatch). If a timer fires after sched_schedule sets current_thread=next but before context_switch_asm finishes restoring the new thread's registers, the nested sched_schedule saves wrong register values into next->ctx, corrupting the thread on re-schedule.
The preemptive path (sched_tick from the timer ISR) is always called with IF=0 (interrupt gates clear IF on entry), so it is not affected.

