feat: Simple executor

This commit is contained in:
ParrotXray 2025-10-18 20:30:41 +08:00
parent 593713e1a9
commit af04b184f4
8 changed files with 278 additions and 9 deletions

69
Cargo.lock generated
View File

@ -110,6 +110,21 @@ version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2fd1289c04a9ea8cb22300a459a72a385d7c73d3259e2ed7dcb2af674838cfa9"
[[package]]
name = "conquer-once"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5d008a441c0f269f36ca13712528069a86a3e60dffee1d98b976eb3b0b2160b4"
dependencies = [
"conquer-util",
]
[[package]]
name = "conquer-util"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e763eef8846b13b380f37dfecda401770b0ca4e56e95170237bd7c25c7db3582"
[[package]]
name = "crc"
version = "3.3.0"
@ -125,6 +140,21 @@ version = "2.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5"
[[package]]
name = "crossbeam-queue"
version = "0.3.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0f58bbc28f91df819d0aa2a2c00cd19754769c2fad90579b3592b1c9ba7a3115"
dependencies = [
"crossbeam-utils",
]
[[package]]
name = "crossbeam-utils"
version = "0.8.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28"
[[package]]
name = "cure-builder"
version = "0.1.0"
@ -139,6 +169,9 @@ version = "0.1.0"
dependencies = [
"acpi",
"bootloader_api",
"conquer-once",
"crossbeam-queue",
"futures-util",
"lazy_static",
"linked_list_allocator",
"raw-cpuid",
@ -179,6 +212,30 @@ version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c"
[[package]]
name = "futures-core"
version = "0.3.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e"
[[package]]
name = "futures-task"
version = "0.3.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988"
[[package]]
name = "futures-util"
version = "0.3.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81"
dependencies = [
"futures-core",
"futures-task",
"pin-project-lite",
"pin-utils",
]
[[package]]
name = "getrandom"
version = "0.3.3"
@ -305,6 +362,18 @@ dependencies = [
"bitflags 2.9.4",
]
[[package]]
name = "pin-project-lite"
version = "0.2.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b"
[[package]]
name = "pin-utils"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
[[package]]
name = "proc-macro2"
version = "1.0.101"

View File

@ -12,6 +12,20 @@ raw-cpuid = "11.6.0"
acpi = "6.0.1"
linked_list_allocator = "0.10.5"
[dependencies.crossbeam-queue]
version = "0.3.12"
default-features = false
features = ["alloc"]
[dependencies.conquer-once]
version = "0.4.0"
default-features = false
[dependencies.futures-util]
version = "0.3.31"
default-features = false
features = ["alloc"]
#[package.metadata.bootloader]
#map-physical-memory = true
#physical-memory-offset = "0xFFFF800000000000"

View File

@ -9,12 +9,13 @@ use crate::tty::tty;
use crate::kprintln;
use crate::kernel::k_main;
use crate::klibc::logger::{init, LogLevel, LoggerConfig};
use crate::klibc::malloc;
use crate::klibc::mem;
use crate::{log_debug, log_error, log_info, log_trace, log_warn};
use crate::drivers::keyboard;
use crate::hal::{acpi, cpu, rtc, timer};
use crate::hal::apic::{ioapic, lapic};
use crate::hal::cpu::cpu_enable_interrupts;
use crate::task::executor;
fn _logger_init() {
init(
@ -64,7 +65,7 @@ fn _memory_init(
let bitmap_size = ((usable_end - usable_start) / 4096 + 7) / 8;
let bitmap_pages = (bitmap_size as usize + 4095) / 4096;
if let Some(bitmap_addr) = malloc::kmalloc(
if let Some(bitmap_addr) = mem::kmalloc(
bitmap_pages * 4096,
&mut mapper,
&mut frame_allocator
@ -284,6 +285,8 @@ pub fn _kernel_init(boot_info: &'static mut BootInfo) -> ! {
log_info!("System initialization complete!");
kprintln!();
let mut executor = executor::Executor::new();
k_main::_kernel_main();
} else {

View File

@ -1,4 +1,4 @@
pub mod print;
pub mod logger;
pub mod string;
pub mod malloc;
pub mod mem;

View File

@ -15,8 +15,8 @@ use x86_64::{
VirtAddr,
};
mod hal;
mod klibc;
pub mod hal;
pub mod klibc;
pub mod arch;
pub mod mm;
pub mod tty;
@ -44,10 +44,10 @@ fn panic(info: &PanicInfo) -> ! {
kprintln!();
log_fatal!("{}", info);
kprintln!();
log_fatal!(" CR0: 0x{:016x}", cpu::cpu_r_cr0());
log_fatal!(" CR2: 0x{:016x}", cpu::cpu_r_cr2());
log_fatal!(" CR3: 0x{:016x}", cpu::cpu_r_cr3());
log_fatal!(" CR4: 0x{:016x}", cpu::cpu_r_cr4());
log_fatal!("CR0: 0x{:016x}", cpu::cpu_r_cr0());
log_fatal!("CR2: 0x{:016x}", cpu::cpu_r_cr2());
log_fatal!("CR3: 0x{:016x}", cpu::cpu_r_cr3());
log_fatal!("CR4: 0x{:016x}", cpu::cpu_r_cr4());
loop {
cpu::cpu_halt();

135
kernel/src/task/executor.rs Normal file
View File

@ -0,0 +1,135 @@
use alloc::{collections::BTreeMap, sync::Arc, task::Wake};
use core::task::{Context, Poll, Waker};
use crossbeam_queue::ArrayQueue;
use super::{Task, TaskId};
use crate::hal::cpu;
/// Task executor that drives tasks to completion
pub struct Executor {
/// Binary tree of tasks
tasks: BTreeMap<TaskId, Task>,
/// Queue of tasks ready to run
task_queue: Arc<ArrayQueue<TaskId>>,
/// Cache of wakers for tasks
waker_cache: BTreeMap<TaskId, Waker>,
}
impl Executor {
/// Create a new executor
pub fn new() -> Self {
Executor {
tasks: BTreeMap::new(),
task_queue: Arc::new(ArrayQueue::new(100)),
waker_cache: BTreeMap::new(),
}
}
/// Spawn a new task
///
/// Adds the provided task to the executor
///
/// # Safety
/// * panics if the task ID is already in the executor
/// * panics if the task queue is full
///
/// # Arguments
/// * `task` - [`Task`] to spawn
pub fn spawn(&mut self, task: Task) {
let task_id = task.id;
if self.tasks.insert(task.id, task).is_some() {
panic!("task with same ID already in tasks");
}
self.task_queue.push(task_id).expect("queue full");
}
/// Run the executor
///
/// Continuously runs tasks until there are no more tasks to run
/// and then sleeps until an interrupt is received
pub fn run(&mut self) -> ! {
loop {
self.run_ready_tasks();
self.sleep_if_idle();
}
}
/// Run all tasks that are ready to run
fn run_ready_tasks(&mut self) {
// destructure `self` to avoid borrow checker errors
let Self {
tasks,
task_queue,
waker_cache,
} = self;
while let Some(task_id) = task_queue.pop() {
let task = match tasks.get_mut(&task_id) {
Some(task) => task,
None => continue, // task no longer exists
};
let waker = waker_cache
.entry(task_id)
.or_insert_with(|| TaskWaker::new(task_id, task_queue.clone()));
let mut context = Context::from_waker(waker);
match task.poll(&mut context) {
Poll::Ready(()) => {
// task done -> remove it and its cached waker
tasks.remove(&task_id);
waker_cache.remove(&task_id);
}
Poll::Pending => {}
}
}
}
/// Sleep if there are no tasks to run
///
/// If there are no tasks to run, disable interrupts and halt the CPU until
/// an interrupt is received.
fn sleep_if_idle(&self) {
cpu::cpu_disable_interrupts();
if self.task_queue.is_empty() {
cpu::cpu_halt()
} else {
cpu::cpu_enable_interrupts()
}
}
}
/// Waker for a task
struct TaskWaker {
/// Task ID
task_id: TaskId,
/// Task queue
task_queue: Arc<ArrayQueue<TaskId>>,
}
impl TaskWaker {
/// Create a new task waker
#[allow(clippy::new_ret_no_self)]
fn new(task_id: TaskId, task_queue: Arc<ArrayQueue<TaskId>>) -> Waker {
Waker::from(Arc::new(TaskWaker {
task_id,
task_queue,
}))
}
/// Wake the task
fn wake_task(&self) {
self.task_queue.push(self.task_id).expect("task_queue full");
}
}
impl Wake for TaskWaker {
/// Wake the task
fn wake(self: Arc<Self>) {
self.wake_task();
}
/// Wake the task by reference
fn wake_by_ref(self: &Arc<Self>) {
self.wake_task();
}
}

View File

@ -0,0 +1,48 @@
//! A simple notion of a task, which is a future that can be polled by an
//! executor.
extern crate alloc;
use alloc::boxed::Box;
use core::{
future::Future,
pin::Pin,
sync::atomic::{AtomicU64, Ordering},
task::{Context, Poll},
};
pub mod executor;
/// A unique identifier for a task.
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
struct TaskId(u64);
impl TaskId {
/// Create a new, unique task ID.
fn new() -> Self {
static NEXT_ID: AtomicU64 = AtomicU64::new(0);
TaskId(NEXT_ID.fetch_add(1, Ordering::Relaxed))
}
}
/// A task that can be executed by an executor.
pub struct Task {
/// A unique identifier for the task.
id: TaskId,
/// The future that the task will execute
future: Pin<Box<dyn Future<Output = ()>>>,
}
impl Task {
/// Create a new task from a future.
pub fn new(future: impl Future<Output = ()> + 'static) -> Task {
Task {
id: TaskId::new(),
future: Box::pin(future),
}
}
/// Poll the task.
fn poll(&mut self, context: &mut Context) -> Poll<()> {
self.future.as_mut().poll(context)
}
}