Skip to content

Commit 1cf28c0

Browse files
author
adrerl
committed
Implement process scheduler and management features
- Introduced a new state module for managing process states, including waiters for stdin, input, pipes, and futexes. - Added proactive cleanup of stale waiters to prevent bit accumulation over long system uptime. - Implemented a tick module to handle scheduling ticks, process state transitions, and signal delivery. - Created wait and wake modules for managing process blocking and waking mechanisms. - Enhanced process management with functions for blocking on sleep, waiting for child processes, and reaping terminated processes. - Improved signal handling to allow processes to respond to signals appropriately, including termination and continuation. - Optimized process selection logic to ensure fair scheduling and prevent starvation.
1 parent e558361 commit 1cf28c0

14 files changed

Lines changed: 1756 additions & 1690 deletions

File tree

hwinit/src/process/mod.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
//! (to allocate its kernel stack via MemoryRegistry, not via Vec/Box).
2727
2828
pub mod context;
29+
mod schedular;
2930
pub mod scheduler;
3031
pub mod signals;
3132
pub mod vma;
@@ -99,6 +100,10 @@ impl ProcessState {
99100
///
100101
/// Stored in a fixed-size static slot; no heap allocation for the descriptor
101102
/// itself. The kernel stack is allocated from MemoryRegistry.
103+
///
104+
/// Cache-aligned (64-byte) to prevent false sharing of scheduler-hot fields
105+
/// on SMP when multiple cores update different process metadata fields.
106+
#[repr(C, align(64))]
102107
pub struct Process {
103108
// identity
104109
pub pid: u32,
@@ -136,6 +141,10 @@ pub struct Process {
136141
// scheduling
137142
/// Scheduling priority (lower = higher priority; 0 = real-time).
138143
pub priority: u8,
144+
/// Remaining weighted quanta in the current RR epoch.
145+
pub sched_budget_left: u8,
146+
/// Ticks spent Ready but not selected; used for starvation forcing.
147+
pub sched_wait_ticks: u32,
139148
/// Accumulated CPU ticks (for the task manager display).
140149
pub cpu_ticks: u64,
141150
/// Accumulated TSC cycles this process was *actively* running (not in HLT).
@@ -247,6 +256,8 @@ impl Process {
247256
heap_region: (0, 0),
248257
pages_allocated: 0,
249258
priority: 128,
259+
sched_budget_left: 0,
260+
sched_wait_ticks: 0,
250261
cpu_ticks: 0,
251262
cpu_tsc: 0,
252263
run_start_tsc: 0,
Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
use super::state::{
2+
clear_waiter_all, set_percpu_fpu_ptr, set_this_core_pid, this_core_pid, IDLE_TSC_TOTAL, KERNEL_CR3,
3+
KERNEL_HLT_ENTRY_TSC, LIVE_COUNT, PROCESS_TABLE, PROCESS_TABLE_LOCK, SCHEDULER_READY,
4+
TIMED_BLOCK_COUNT, TSC_FREQUENCY,
5+
};
6+
use crate::cpu::gdt::{KERNEL_CS, KERNEL_DS};
7+
use crate::process::{BlockReason, CpuContext, Process, ProcessState, Signal, MAX_PROCESSES};
8+
use crate::serial::{put_hex32, puts};
9+
use core::sync::atomic::Ordering;
10+
11+
pub unsafe fn set_tsc_frequency(freq: u64) {
12+
TSC_FREQUENCY = freq;
13+
}
14+
15+
pub fn tsc_frequency() -> u64 {
16+
unsafe { TSC_FREQUENCY }
17+
}
18+
19+
pub fn mark_kernel_hlt() {
20+
KERNEL_HLT_ENTRY_TSC.store(crate::cpu::tsc::read_tsc(), Ordering::Relaxed);
21+
}
22+
23+
pub fn idle_tsc_total() -> u64 {
24+
IDLE_TSC_TOTAL.load(Ordering::Relaxed)
25+
}
26+
27+
pub fn inc_timed_block_count() {
28+
TIMED_BLOCK_COUNT.fetch_add(1, Ordering::Relaxed);
29+
}
30+
31+
pub unsafe fn init_scheduler() {
32+
if SCHEDULER_READY {
33+
puts("[SCHED] already initialized\n");
34+
return;
35+
}
36+
37+
let mut kernel_proc = Process::empty();
38+
kernel_proc.pid = 0;
39+
kernel_proc.set_name("kernel");
40+
kernel_proc.state = ProcessState::Running;
41+
kernel_proc.priority = 0;
42+
kernel_proc.running_on = 0;
43+
44+
let cr3: u64;
45+
core::arch::asm!("mov {}, cr3", out(reg) cr3, options(nostack, nomem));
46+
kernel_proc.cr3 = cr3 & 0x000F_FFFF_FFFF_F000;
47+
KERNEL_CR3 = kernel_proc.cr3;
48+
49+
PROCESS_TABLE[0] = Some(kernel_proc);
50+
LIVE_COUNT.store(1, Ordering::SeqCst);
51+
set_this_core_pid(0);
52+
SCHEDULER_READY = true;
53+
54+
if let Some(p) = PROCESS_TABLE[0].as_mut() {
55+
let fpu_ptr = &mut p.fpu_state as *mut crate::process::context::FpuState as u64;
56+
set_percpu_fpu_ptr(fpu_ptr);
57+
}
58+
59+
puts("[SCHED] initialized - kernel is PID 0\n");
60+
}
61+
62+
pub unsafe fn spawn_kernel_thread(
63+
name: &str,
64+
entry_fn: u64,
65+
priority: u8,
66+
) -> Result<u32, &'static str> {
67+
if !SCHEDULER_READY {
68+
return Err("scheduler not initialized");
69+
}
70+
71+
PROCESS_TABLE_LOCK.lock();
72+
73+
let slot_idx = (1..MAX_PROCESSES)
74+
.find(|&i| {
75+
PROCESS_TABLE[i]
76+
.as_ref()
77+
.map(|p| p.is_free())
78+
.unwrap_or(true)
79+
})
80+
.ok_or_else(|| {
81+
PROCESS_TABLE_LOCK.unlock();
82+
"process table full"
83+
})?;
84+
85+
let pid = slot_idx as u32;
86+
87+
let mut proc = Process::empty();
88+
proc.pid = pid;
89+
proc.set_name(name);
90+
proc.parent_pid = this_core_pid();
91+
proc.priority = priority;
92+
proc.state = ProcessState::Ready;
93+
94+
let cr3: u64;
95+
core::arch::asm!("mov {}, cr3", out(reg) cr3, options(nostack, nomem));
96+
proc.cr3 = cr3 & 0x000F_FFFF_FFFF_F000;
97+
98+
if let Err(e) = proc.alloc_kernel_stack() {
99+
PROCESS_TABLE_LOCK.unlock();
100+
return Err(e);
101+
}
102+
103+
proc.context = CpuContext::new_kernel_thread(
104+
entry_fn,
105+
proc.kernel_stack_top,
106+
KERNEL_CS as u64,
107+
KERNEL_DS as u64,
108+
);
109+
110+
let _ = (pid, entry_fn);
111+
crate::serial::log_info("SCHED", 770, "kernel thread spawned");
112+
113+
PROCESS_TABLE[slot_idx] = Some(proc);
114+
LIVE_COUNT.fetch_add(1, Ordering::Relaxed);
115+
116+
PROCESS_TABLE_LOCK.unlock();
117+
Ok(pid)
118+
}
119+
120+
pub unsafe fn exit_process(code: i32) -> ! {
121+
let pid = this_core_pid();
122+
123+
PROCESS_TABLE_LOCK.lock();
124+
if let Some(Some(proc)) = PROCESS_TABLE.get_mut(pid as usize) {
125+
terminate_process_inner(proc, code);
126+
}
127+
PROCESS_TABLE_LOCK.unlock();
128+
129+
core::arch::asm!("sti", options(nostack, nomem));
130+
loop {
131+
core::arch::asm!("hlt", options(nostack, nomem));
132+
}
133+
}
134+
135+
pub(super) unsafe fn terminate_process_inner(proc: &mut Process, code: i32) {
136+
let child_pid = proc.pid;
137+
let parent_pid = proc.parent_pid;
138+
139+
clear_waiter_all(child_pid);
140+
141+
crate::syscall::handler::release_fb_lock_if_holder(child_pid);
142+
143+
proc.state = ProcessState::Zombie;
144+
proc.exit_code = Some(code);
145+
LIVE_COUNT.fetch_sub(1, Ordering::Relaxed);
146+
147+
puts("[SCHED] PID ");
148+
put_hex32(child_pid);
149+
puts(" exited code=");
150+
put_hex32(code as u32);
151+
puts("\n");
152+
153+
if let Some(Some(parent)) = PROCESS_TABLE.get_mut(parent_pid as usize) {
154+
if let ProcessState::Blocked(BlockReason::WaitChild(waited)) = parent.state {
155+
if waited == child_pid {
156+
parent.state = ProcessState::Ready;
157+
}
158+
}
159+
parent.pending_signals.raise(Signal::SIGCHLD);
160+
}
161+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
pub mod lifecycle;
2+
pub mod spawn;
3+
pub mod state;
4+
pub mod tick;
5+
pub mod wait;
6+
pub mod wake;
7+
8+
pub use lifecycle::{
9+
exit_process, idle_tsc_total, inc_timed_block_count, init_scheduler, mark_kernel_hlt,
10+
set_tsc_frequency, spawn_kernel_thread, tsc_frequency,
11+
};
12+
pub use spawn::{spawn_user_process, spawn_user_thread};
13+
pub use state::{get_kernel_cr3, get_earliest_deadline, try_set_earliest_deadline, ProcessInfo, Scheduler, SCHEDULER};
14+
pub use tick::scheduler_tick;
15+
pub use wait::{block_sleep, try_wait_child, wait_for_child};
16+
pub use wake::{wake_futex_waiters, wake_input_reader, wake_pipe_readers, wake_stdin_waiters};
17+
18+
pub(crate) use state::{PROCESS_TABLE, PROCESS_TABLE_LOCK};
19+
pub(crate) use state::{
20+
clear_input_waiter, mark_futex_waiter, mark_input_waiter, mark_pipe_waiter,
21+
mark_stdin_waiter,
22+
};

0 commit comments

Comments
 (0)