Optimize the initialization logic during Asterinas init phase

This commit is contained in:
Chen Chengjun 2025-08-26 03:07:03 +00:00 committed by Tate, Hongliang Tian
parent 3882eb4000
commit b1bbd6c3fe
22 changed files with 159 additions and 126 deletions

View File

@ -23,39 +23,42 @@ use crate::{
};
/// Init the device node in fs, must be called after mounting rootfs.
pub fn init() -> Result<()> {
pub fn init_in_first_process(ctx: &Context) -> Result<()> {
let fs = ctx.thread_local.borrow_fs();
let fs_resolver = fs.resolver().read();
let null = Arc::new(null::Null);
add_node(null, "null")?;
add_node(null, "null", &fs_resolver)?;
let zero = Arc::new(zero::Zero);
add_node(zero, "zero")?;
add_node(zero, "zero", &fs_resolver)?;
tty::init();
let tty = Arc::new(tty::TtyDevice);
add_node(tty, "tty")?;
add_node(tty, "tty", &fs_resolver)?;
let console = tty::system_console().clone();
add_node(console, "console")?;
add_node(console, "console", &fs_resolver)?;
for (index, tty) in tty::iter_n_tty().enumerate() {
add_node(tty.clone(), &format!("tty{}", index))?;
add_node(tty.clone(), &format!("tty{}", index), &fs_resolver)?;
}
#[cfg(target_arch = "x86_64")]
ostd::if_tdx_enabled!({
add_node(Arc::new(tdxguest::TdxGuest), "tdx_guest")?;
add_node(Arc::new(tdxguest::TdxGuest), "tdx_guest", &fs_resolver)?;
});
let random = Arc::new(random::Random);
add_node(random, "random")?;
add_node(random, "random", &fs_resolver)?;
let urandom = Arc::new(urandom::Urandom);
add_node(urandom, "urandom")?;
add_node(urandom, "urandom", &fs_resolver)?;
pty::init()?;
pty::init_in_first_process(&fs_resolver)?;
shm::init()?;
shm::init_in_first_process(&fs_resolver)?;
Ok(())
}

View File

@ -19,19 +19,14 @@ use spin::Once;
static DEV_PTS: Once<Path> = Once::new();
pub fn init() -> Result<()> {
let fs = FsResolver::new();
pub fn init_in_first_process(fs_resolver: &FsResolver) -> Result<()> {
let dev = fs_resolver.lookup(&FsPath::try_from("/dev")?)?;
// Create the "pts" directory and mount devpts on it.
let devpts_path =
dev.new_fs_child("pts", InodeType::Dir, InodeMode::from_bits_truncate(0o755))?;
let devpts_mount = devpts_path.mount(DevPts::new())?;
let dev = fs.lookup(&FsPath::try_from("/dev")?)?;
let devpts_path = {
// Create the "pts" directory and mount devpts on it.
let devpts_path =
dev.new_fs_child("pts", InodeType::Dir, InodeMode::from_bits_truncate(0o755))?;
let devpts_mount = devpts_path.mount(DevPts::new())?;
Path::new_fs_root(devpts_mount)
};
DEV_PTS.call_once(|| devpts_path);
DEV_PTS.call_once(|| Path::new_fs_root(devpts_mount));
// Create the "ptmx" symlink.
let ptmx = dev.new_fs_child(

View File

@ -10,11 +10,8 @@ use crate::{
};
/// Initializes "/dev/shm" for POSIX shared memory usage.
pub fn init() -> Result<()> {
let dev_path = {
let fs = FsResolver::new();
fs.lookup(&FsPath::try_from("/dev")?)?
};
pub fn init_in_first_process(fs_resolver: &FsResolver) -> Result<()> {
let dev_path = fs_resolver.lookup(&FsPath::try_from("/dev")?)?;
// Create the "shm" directory under "/dev" and mount a ramfs on it.
let shm_path =

View File

@ -102,11 +102,8 @@ impl DeviceId {
///
/// If the parent path is not existing, `mkdir -p` the parent path.
/// This function is used in registering device.
pub fn add_node(device: Arc<dyn Device>, path: &str) -> Result<Path> {
let mut dev_path = {
let fs_resolver = FsResolver::new();
fs_resolver.lookup(&FsPath::try_from("/dev").unwrap())?
};
pub fn add_node(device: Arc<dyn Device>, path: &str, fs_resolver: &FsResolver) -> Result<Path> {
let mut dev_path = fs_resolver.lookup(&FsPath::try_from("/dev").unwrap())?;
let mut relative_path = {
let relative_path = path.trim_start_matches('/');
if relative_path.is_empty() {
@ -157,7 +154,7 @@ pub fn add_node(device: Arc<dyn Device>, path: &str) -> Result<Path> {
/// Delete the device node from FS for the device.
///
/// This function is used in unregistering device.
pub fn delete_node(path: &str) -> Result<()> {
pub fn delete_node(path: &str, fs_resolver: &FsResolver) -> Result<()> {
let abs_path = {
let device_path = path.trim_start_matches('/');
if device_path.is_empty() {
@ -166,10 +163,8 @@ pub fn delete_node(path: &str) -> Result<()> {
String::from("/dev") + "/" + device_path
};
let (parent_path, name) = {
let fs_resolver = FsResolver::new();
fs_resolver.lookup_dir_and_base_name(&FsPath::try_from(abs_path.as_str()).unwrap())?
};
let (parent_path, name) =
fs_resolver.lookup_dir_and_base_name(&FsPath::try_from(abs_path.as_str()).unwrap())?;
parent_path.unlink(&name)?;
Ok(())

View File

@ -4,11 +4,7 @@ use core::sync::atomic::{AtomicU8, Ordering};
use aster_util::slot_vec::SlotVec;
use super::{
file_handle::FileLike,
fs_resolver::{FsPath, FsResolver, AT_FDCWD},
utils::{AccessMode, InodeMode},
};
use super::file_handle::FileLike;
use crate::{
events::{Events, IoEvents, Observer, Subject},
fs::utils::StatusFlags,
@ -35,34 +31,6 @@ impl FileTable {
}
}
pub fn new_with_stdio() -> Self {
let mut table = SlotVec::new();
let fs_resolver = FsResolver::new();
let tty_path = FsPath::new(AT_FDCWD, "/dev/console").expect("cannot find tty");
let stdin = {
let flags = AccessMode::O_RDONLY as u32;
let mode = InodeMode::S_IRUSR;
fs_resolver.open(&tty_path, flags, mode.bits()).unwrap()
};
let stdout = {
let flags = AccessMode::O_WRONLY as u32;
let mode = InodeMode::S_IWUSR;
fs_resolver.open(&tty_path, flags, mode.bits()).unwrap()
};
let stderr = {
let flags = AccessMode::O_WRONLY as u32;
let mode = InodeMode::S_IWUSR;
fs_resolver.open(&tty_path, flags, mode.bits()).unwrap()
};
table.put(FileTableEntry::new(Arc::new(stdin), FdFlags::empty()));
table.put(FileTableEntry::new(Arc::new(stdout), FdFlags::empty()));
table.put(FileTableEntry::new(Arc::new(stderr), FdFlags::empty()));
Self {
table,
subject: Subject::new(),
}
}
pub fn len(&self) -> usize {
self.table.slots_len()
}

View File

@ -29,7 +29,9 @@ use crate::{
fs::{
exfat::{ExfatFS, ExfatMountOptions},
ext2::Ext2,
fs_resolver::FsPath,
file_table::FdFlags,
fs_resolver::{FsPath, FsResolver},
utils::{AccessMode, InodeMode},
},
prelude::*,
};
@ -51,7 +53,7 @@ fn start_block_device(device_name: &str) -> Result<Arc<dyn BlockDevice>> {
}
}
pub fn lazy_init() {
pub fn init() {
registry::init();
sysfs::init();
@ -64,21 +66,57 @@ pub fn lazy_init() {
exfat::init();
overlayfs::init();
rootfs::init();
}
pub fn init_in_first_kthread(fs_resolver: &FsResolver) {
rootfs::init_in_first_kthread(fs_resolver).unwrap();
}
pub fn init_in_first_process(ctx: &Context) {
//The device name is specified in qemu args as --serial={device_name}
let ext2_device_name = "vext2";
let exfat_device_name = "vexfat";
let fs = ctx.thread_local.borrow_fs();
let fs_resolver = fs.resolver().read();
if let Ok(block_device_ext2) = start_block_device(ext2_device_name) {
let ext2_fs = Ext2::open(block_device_ext2).unwrap();
let target_path = FsPath::try_from("/ext2").unwrap();
println!("[kernel] Mount Ext2 fs at {:?} ", target_path);
self::rootfs::mount_fs_at(ext2_fs, &target_path).unwrap();
self::rootfs::mount_fs_at(ext2_fs, &target_path, &fs_resolver).unwrap();
}
if let Ok(block_device_exfat) = start_block_device(exfat_device_name) {
let exfat_fs = ExfatFS::open(block_device_exfat, ExfatMountOptions::default()).unwrap();
let target_path = FsPath::try_from("/exfat").unwrap();
println!("[kernel] Mount ExFat fs at {:?} ", target_path);
self::rootfs::mount_fs_at(exfat_fs, &target_path).unwrap();
self::rootfs::mount_fs_at(exfat_fs, &target_path, &fs_resolver).unwrap();
}
// Initialize the file table for the first process.
let tty_path = FsPath::new(fs_resolver::AT_FDCWD, "/dev/console").expect("cannot find tty");
let stdin = {
let flags = AccessMode::O_RDONLY as u32;
let mode = InodeMode::S_IRUSR;
fs_resolver.open(&tty_path, flags, mode.bits()).unwrap()
};
let stdout = {
let flags = AccessMode::O_WRONLY as u32;
let mode = InodeMode::S_IWUSR;
fs_resolver.open(&tty_path, flags, mode.bits()).unwrap()
};
let stderr = {
let flags = AccessMode::O_WRONLY as u32;
let mode = InodeMode::S_IWUSR;
fs_resolver.open(&tty_path, flags, mode.bits()).unwrap()
};
let mut file_table_ref = ctx.thread_local.borrow_file_table_mut();
let mut file_table = file_table_ref.unwrap().write();
file_table.insert(Arc::new(stdin), FdFlags::empty());
file_table.insert(Arc::new(stdout), FdFlags::empty());
file_table.insert(Arc::new(stderr), FdFlags::empty());
}

View File

@ -4,6 +4,7 @@ use core2::io::{Cursor, Read};
use cpio_decoder::{CpioDecoder, FileType};
use lending_iterator::LendingIterator;
use libflate::gzip::Decoder as GZipDecoder;
use ostd::boot::boot_info;
use spin::Once;
use super::{
@ -29,8 +30,8 @@ impl Read for BoxedReader<'_> {
}
/// Unpack and prepare the rootfs from the initramfs CPIO buffer.
pub fn init(initramfs_buf: &[u8]) -> Result<()> {
init_root_mount();
pub fn init_in_first_kthread(fs_resolver: &FsResolver) -> Result<()> {
let initramfs_buf = boot_info().initramfs.expect("No initramfs found!");
let reader = {
let mut initramfs_suffix = "";
@ -53,7 +54,6 @@ pub fn init(initramfs_buf: &[u8]) -> Result<()> {
reader
};
let mut decoder = CpioDecoder::new(reader);
let fs = FsResolver::new();
loop {
let Some(entry_result) = decoder.next() else {
@ -76,9 +76,9 @@ pub fn init(initramfs_buf: &[u8]) -> Result<()> {
// The mkinitramfs script uses `find` command to ensure that the entries are
// sorted that a directory always appears before its child directories and files.
let (parent, name) = if let Some((prefix, last)) = entry_name.rsplit_once('/') {
(fs.lookup(&FsPath::try_from(prefix)?)?, last)
(fs_resolver.lookup(&FsPath::try_from(prefix)?)?, last)
} else {
(fs.root().clone(), entry_name)
(fs_resolver.root().clone(), entry_name)
};
let metadata = entry.metadata();
@ -106,22 +106,26 @@ pub fn init(initramfs_buf: &[u8]) -> Result<()> {
}
}
// Mount DevFS
let dev_path = fs.lookup(&FsPath::try_from("/dev")?)?;
let dev_path = fs_resolver.lookup(&FsPath::try_from("/dev")?)?;
dev_path.mount(RamFS::new())?;
println!("[kernel] rootfs is ready");
Ok(())
}
pub fn mount_fs_at(fs: Arc<dyn FileSystem>, fs_path: &FsPath) -> Result<()> {
let target_path = FsResolver::new().lookup(fs_path)?;
pub fn mount_fs_at(
fs: Arc<dyn FileSystem>,
fs_path: &FsPath,
fs_resolver: &FsResolver,
) -> Result<()> {
let target_path = fs_resolver.lookup(fs_path)?;
target_path.mount(fs)?;
Ok(())
}
static ROOT_MOUNT: Once<Arc<Mount>> = Once::new();
pub fn init_root_mount() {
pub(super) fn init() {
ROOT_MOUNT.call_once(|| -> Arc<Mount> {
let rootfs = RamFS::new();
Mount::new_root(rootfs)

View File

@ -97,6 +97,6 @@ impl IpcPermission {
}
}
pub(super) fn init() {
semaphore::init();
pub(super) fn init_in_first_kthread() {
semaphore::init_in_first_kthread();
}

View File

@ -6,6 +6,6 @@
pub mod posix;
pub mod system_v;
pub(super) fn init() {
system_v::init();
pub(super) fn init_in_first_kthread() {
system_v::init_in_first_kthread();
}

View File

@ -15,6 +15,6 @@ bitflags! {
}
}
pub(super) fn init() {
sem_set::init();
pub(super) fn init_in_first_kthread() {
sem_set::init_in_first_kthread();
}

View File

@ -354,7 +354,7 @@ static ID_ALLOCATOR: Once<SpinLock<IdAlloc>> = Once::new();
/// Semaphore sets in system
static SEMAPHORE_SETS: RwLock<BTreeMap<key_t, SemaphoreSet>> = RwLock::new(BTreeMap::new());
pub(super) fn init() {
pub(super) fn init_in_first_kthread() {
ID_ALLOCATOR.call_once(|| {
let mut id_alloc = IdAlloc::with_capacity(SEMMNI + 1);
// Remove the first index 0

View File

@ -38,7 +38,7 @@ use ostd::{
use process::{spawn_init_process, Process};
use sched::SchedPolicy;
use crate::{prelude::*, thread::kernel_thread::ThreadOptions};
use crate::{fs::fs_resolver::FsResolver, prelude::*, thread::kernel_thread::ThreadOptions};
extern crate alloc;
extern crate lru;
@ -91,7 +91,7 @@ pub fn main() {
// Spawn the first kernel thread on BSP.
let mut affinity = CpuSet::new_empty();
affinity.add(CpuId::bsp());
ThreadOptions::new(init_thread)
ThreadOptions::new(first_kthread)
.cpu_affinity(affinity)
.sched_policy(SchedPolicy::Idle)
.spawn();
@ -105,12 +105,27 @@ pub fn init() {
#[cfg(target_arch = "x86_64")]
net::init();
sched::init();
fs::rootfs::init(boot_info().initramfs.expect("No initramfs found!")).unwrap();
device::init().unwrap();
syscall::init();
#[cfg(any(target_arch = "x86_64", target_arch = "riscv64"))]
vdso::init();
process::init();
fs::init();
}
fn init_in_first_kthread(fs_resolver: &FsResolver) {
// Work queue should be initialized before interrupt is enabled,
// in case any irq handler uses work queue as bottom half
thread::work_queue::init_in_first_kthread();
#[cfg(target_arch = "x86_64")]
net::init_in_first_kthread();
fs::init_in_first_kthread(fs_resolver);
ipc::init_in_first_kthread();
}
fn init_in_first_process(ctx: &Context) {
device::init_in_first_process(ctx).unwrap();
fs::init_in_first_process(ctx);
process::init_in_first_process(ctx);
#[cfg(any(target_arch = "x86_64", target_arch = "riscv64"))]
vdso::init_in_first_process();
}
fn ap_init() {
@ -133,21 +148,14 @@ fn ap_init() {
.spawn();
}
fn init_thread() {
fn first_kthread() {
println!("[kernel] Spawn init thread");
// Work queue should be initialized before interrupt is enabled,
// in case any irq handler uses work queue as bottom half
thread::work_queue::init();
#[cfg(target_arch = "x86_64")]
net::lazy_init();
fs::lazy_init();
ipc::init();
// driver::pci::virtio::block::block_device_test();
let thread = ThreadOptions::new(|| {
println!("[kernel] Hello world from kernel!");
})
.spawn();
thread.join();
// TODO: After introducing the mount namespace, use an initial mount namespace to create
// the `FsResolver`, and the initial mount namespace should be passed to the first process.
let fs_resolver = FsResolver::new();
init_in_first_kthread(&fs_resolver);
print_banner();

View File

@ -6,7 +6,7 @@ mod poll;
mod sched;
pub use init::{init, iter_all_ifaces, loopback_iface, virtio_iface};
pub use poll::lazy_init;
pub(super) use poll::init_in_first_kthread;
pub type Iface = dyn aster_bigtcp::iface::Iface<ext::BigtcpExt>;
pub type BoundPort = aster_bigtcp::iface::BoundPort<ext::BigtcpExt>;

View File

@ -13,7 +13,7 @@ use crate::{
WaitTimeout,
};
pub fn lazy_init() {
pub fn init_in_first_kthread() {
for iface in iter_all_ifaces() {
spawn_background_poll_thread(iface.clone());
}

View File

@ -10,6 +10,6 @@ pub fn init() {
}
/// Lazy init should be called after spawning init thread.
pub fn lazy_init() {
iface::lazy_init();
pub fn init_in_first_kthread() {
iface::init_in_first_kthread();
}

View File

@ -35,7 +35,13 @@ pub use rlimit::ResourceType;
pub use term_status::TermStatus;
pub use wait::{do_wait, WaitOptions, WaitStatus};
use crate::context::Context;
pub(super) fn init() {
process::init();
posix_thread::futex::init();
}
pub(super) fn init_in_first_process(ctx: &Context) {
process::init_in_first_process(ctx);
}

View File

@ -43,6 +43,7 @@ pub struct PosixThreadBuilder {
sig_queues: SigQueues,
sched_policy: SchedPolicy,
fpu_context: FpuContext,
is_init_process: bool,
}
impl PosixThreadBuilder {
@ -61,6 +62,7 @@ impl PosixThreadBuilder {
sig_queues: SigQueues::new(),
sched_policy: SchedPolicy::Fair(Nice::default()),
fpu_context: FpuContext::new(),
is_init_process: false,
}
}
@ -104,6 +106,12 @@ impl PosixThreadBuilder {
self
}
#[expect(clippy::wrong_self_convention)]
pub(in crate::process) fn is_init_process(mut self) -> Self {
self.is_init_process = true;
self
}
pub fn build(self) -> Arc<Task> {
let Self {
tid,
@ -119,9 +127,10 @@ impl PosixThreadBuilder {
sig_queues,
sched_policy,
fpu_context,
is_init_process,
} = self;
let file_table = file_table.unwrap_or_else(|| RwArc::new(FileTable::new_with_stdio()));
let file_table = file_table.unwrap_or_else(|| RwArc::new(FileTable::new()));
let fs = fs.unwrap_or_else(|| Arc::new(ThreadFsInfo::default()));
@ -173,7 +182,7 @@ impl PosixThreadBuilder {
);
thread_table::add_thread(tid, thread.clone());
task::create_new_user_task(user_ctx, thread, thread_local)
task::create_new_user_task(user_ctx, thread, thread_local, is_init_process)
})
}
}

View File

@ -4,7 +4,7 @@
use ostd::{cpu::context::UserContext, task::Task, user::UserContextApi};
use super::{Process, Terminal};
use super::Process;
use crate::{
fs::{
fs_resolver::{FsPath, AT_FDCWD},
@ -36,9 +36,6 @@ pub fn spawn_init_process(
set_session_and_group(&process);
// FIXME: This should be done by the userspace init process.
(crate::device::tty::system_console().clone() as Arc<dyn Terminal>).set_control(&process)?;
process.run();
Ok(process)
@ -124,6 +121,7 @@ fn create_init_task(
let thread_builder = PosixThreadBuilder::new(tid, Box::new(user_ctx), credentials)
.thread_name(thread_name)
.process(process)
.fs(Arc::new(fs));
.fs(Arc::new(fs))
.is_init_process();
Ok(thread_builder.build())
}

View File

@ -56,6 +56,13 @@ pub(super) fn init() {
timer_manager::init();
}
pub(super) fn init_in_first_process(ctx: &Context) {
// FIXME: This should be done by the userspace init process.
(crate::device::tty::system_console().clone() as Arc<dyn Terminal>)
.set_control(ctx.process)
.unwrap();
}
/// Process stands for a set of threads that shares the same userspace.
pub struct Process {
// Immutable Part

View File

@ -26,8 +26,9 @@ pub fn create_new_user_task(
user_ctx: Box<UserContext>,
thread_ref: Arc<Thread>,
thread_local: ThreadLocal,
is_init_process: bool,
) -> Task {
fn user_task_entry(user_ctx: UserContext) {
let user_task_entry = move |user_ctx: UserContext| {
let current_task = Task::current().unwrap();
let current_thread = current_task.as_thread().unwrap();
let current_posix_thread = current_thread.as_posix_thread().unwrap();
@ -70,6 +71,10 @@ pub fn create_new_user_task(
task: &current_task,
};
if is_init_process {
crate::init_in_first_process(&ctx);
}
while !current_thread.is_exited() {
// Execute the user code
ctx.thread_local.fpu().activate();
@ -111,7 +116,7 @@ pub fn create_new_user_task(
handle_pending_signal(user_ctx, &ctx, None);
}
}
}
};
let user_task_func = move || user_task_entry(*user_ctx);

View File

@ -174,7 +174,7 @@ impl WorkQueue {
}
/// Initialize global worker pools and work queues.
pub fn init() {
pub fn init_in_first_kthread() {
WORKERPOOL_NORMAL.call_once(|| {
let cpu_set = CpuSet::new_full();
WorkerPool::new(WorkPriority::Normal, cpu_set)

View File

@ -351,7 +351,7 @@ fn init_vdso() {
VDSO.call_once(|| Arc::new(vdso));
}
pub(super) fn init() {
pub(super) fn init_in_first_process() {
init_start_secs_count();
init_vdso();