mirror of
https://github.com/asterinas/asterinas.git
synced 2025-06-23 01:13:23 +00:00
Introduce CPU clock and CPU timer
This commit is contained in:
committed by
Tate, Hongliang Tian
parent
f1c1011f2b
commit
c84efe7a90
@ -1,5 +1,7 @@
|
|||||||
// SPDX-License-Identifier: MPL-2.0
|
// SPDX-License-Identifier: MPL-2.0
|
||||||
|
|
||||||
|
use core::sync::atomic::{AtomicBool, Ordering};
|
||||||
|
|
||||||
use align_ext::AlignExt;
|
use align_ext::AlignExt;
|
||||||
use log::debug;
|
use log::debug;
|
||||||
#[cfg(feature = "intel_tdx")]
|
#[cfg(feature = "intel_tdx")]
|
||||||
@ -14,6 +16,7 @@ use crate::arch::{
|
|||||||
};
|
};
|
||||||
use crate::{
|
use crate::{
|
||||||
cpu::{CpuException, PageFaultErrorCode, PAGE_FAULT},
|
cpu::{CpuException, PageFaultErrorCode, PAGE_FAULT},
|
||||||
|
cpu_local,
|
||||||
trap::call_irq_callback_functions,
|
trap::call_irq_callback_functions,
|
||||||
vm::{
|
vm::{
|
||||||
kspace::{KERNEL_PAGE_TABLE, LINEAR_MAPPING_BASE_VADDR, LINEAR_MAPPING_VADDR_RANGE},
|
kspace::{KERNEL_PAGE_TABLE, LINEAR_MAPPING_BASE_VADDR, LINEAR_MAPPING_VADDR_RANGE},
|
||||||
@ -22,6 +25,17 @@ use crate::{
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
cpu_local! {
|
||||||
|
static IS_KERNEL_INTERRUPTED: AtomicBool = AtomicBool::new(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns true if this function is called within the context of an IRQ handler
|
||||||
|
/// and the IRQ occurs while the CPU is executing in the kernel mode.
|
||||||
|
/// Otherwise, it returns false.
|
||||||
|
pub fn is_kernel_interrupted() -> bool {
|
||||||
|
IS_KERNEL_INTERRUPTED.load(Ordering::Acquire)
|
||||||
|
}
|
||||||
|
|
||||||
/// Only from kernel
|
/// Only from kernel
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
extern "sysv64" fn trap_handler(f: &mut TrapFrame) {
|
extern "sysv64" fn trap_handler(f: &mut TrapFrame) {
|
||||||
@ -43,7 +57,9 @@ extern "sysv64" fn trap_handler(f: &mut TrapFrame) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
IS_KERNEL_INTERRUPTED.store(true, Ordering::Release);
|
||||||
call_irq_callback_functions(f);
|
call_irq_callback_functions(f);
|
||||||
|
IS_KERNEL_INTERRUPTED.store(false, Ordering::Release);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -80,6 +80,7 @@ pub fn init() {
|
|||||||
device::init().unwrap();
|
device::init().unwrap();
|
||||||
vdso::init();
|
vdso::init();
|
||||||
taskless::init();
|
taskless::init();
|
||||||
|
process::init();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn init_thread() {
|
fn init_thread() {
|
||||||
|
@ -32,3 +32,7 @@ pub use program_loader::{check_executable_file, load_program_to_vm};
|
|||||||
pub use rlimit::ResourceType;
|
pub use rlimit::ResourceType;
|
||||||
pub use term_status::TermStatus;
|
pub use term_status::TermStatus;
|
||||||
pub use wait::{wait_child_exit, WaitOptions};
|
pub use wait::{wait_child_exit, WaitOptions};
|
||||||
|
|
||||||
|
pub(super) fn init() {
|
||||||
|
process::init();
|
||||||
|
}
|
||||||
|
@ -11,6 +11,7 @@ use crate::{
|
|||||||
Credentials, Process,
|
Credentials, Process,
|
||||||
},
|
},
|
||||||
thread::{status::ThreadStatus, task, thread_table, Thread, Tid},
|
thread::{status::ThreadStatus, task, thread_table, Thread, Tid},
|
||||||
|
time::{clocks::ProfClock, TimerManager},
|
||||||
};
|
};
|
||||||
|
|
||||||
/// The builder to build a posix thread
|
/// The builder to build a posix thread
|
||||||
@ -94,6 +95,11 @@ impl PosixThreadBuilder {
|
|||||||
let thread = Arc::new_cyclic(|thread_ref| {
|
let thread = Arc::new_cyclic(|thread_ref| {
|
||||||
let task = task::create_new_user_task(user_space, thread_ref.clone());
|
let task = task::create_new_user_task(user_space, thread_ref.clone());
|
||||||
let status = ThreadStatus::Init;
|
let status = ThreadStatus::Init;
|
||||||
|
|
||||||
|
let prof_clock = ProfClock::new();
|
||||||
|
let virtual_timer_manager = TimerManager::new(prof_clock.user_clock().clone());
|
||||||
|
let prof_timer_manager = TimerManager::new(prof_clock.clone());
|
||||||
|
|
||||||
let posix_thread = PosixThread {
|
let posix_thread = PosixThread {
|
||||||
process,
|
process,
|
||||||
is_main_thread,
|
is_main_thread,
|
||||||
@ -106,6 +112,9 @@ impl PosixThreadBuilder {
|
|||||||
sig_context: Mutex::new(None),
|
sig_context: Mutex::new(None),
|
||||||
sig_stack: Mutex::new(None),
|
sig_stack: Mutex::new(None),
|
||||||
robust_list: Mutex::new(None),
|
robust_list: Mutex::new(None),
|
||||||
|
prof_clock,
|
||||||
|
virtual_timer_manager,
|
||||||
|
prof_timer_manager,
|
||||||
};
|
};
|
||||||
|
|
||||||
Thread::new(tid, task, posix_thread, status)
|
Thread::new(tid, task, posix_thread, status)
|
||||||
|
@ -21,6 +21,7 @@ use crate::{
|
|||||||
prelude::*,
|
prelude::*,
|
||||||
process::signal::constants::SIGCONT,
|
process::signal::constants::SIGCONT,
|
||||||
thread::{thread_table, Tid},
|
thread::{thread_table, Tid},
|
||||||
|
time::{clocks::ProfClock, Timer, TimerManager},
|
||||||
util::write_val_to_user,
|
util::write_val_to_user,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -62,6 +63,15 @@ pub struct PosixThread {
|
|||||||
/// FIXME: This field may be removed. For glibc applications with RESTORER flag set, the sig_context is always equals with rsp.
|
/// FIXME: This field may be removed. For glibc applications with RESTORER flag set, the sig_context is always equals with rsp.
|
||||||
sig_context: Mutex<Option<Vaddr>>,
|
sig_context: Mutex<Option<Vaddr>>,
|
||||||
sig_stack: Mutex<Option<SigStack>>,
|
sig_stack: Mutex<Option<SigStack>>,
|
||||||
|
|
||||||
|
/// A profiling clock measures the user CPU time and kernel CPU time in the thread.
|
||||||
|
prof_clock: Arc<ProfClock>,
|
||||||
|
|
||||||
|
/// A manager that manages timers based on the user CPU time of the current thread.
|
||||||
|
virtual_timer_manager: Arc<TimerManager>,
|
||||||
|
|
||||||
|
/// A manager that manages timers based on the profiling clock of the current thread.
|
||||||
|
prof_timer_manager: Arc<TimerManager>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PosixThread {
|
impl PosixThread {
|
||||||
@ -148,10 +158,39 @@ impl PosixThread {
|
|||||||
return_errno_with_message!(Errno::EPERM, "sending signal to the thread is not allowed.");
|
return_errno_with_message!(Errno::EPERM, "sending signal to the thread is not allowed.");
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(in crate::process) fn enqueue_signal(&self, signal: Box<dyn Signal>) {
|
/// Enqueues a thread-directed signal. This method should only be used for enqueue kernel
|
||||||
|
/// signal and fault signal.
|
||||||
|
pub fn enqueue_signal(&self, signal: Box<dyn Signal>) {
|
||||||
self.sig_queues.enqueue(signal);
|
self.sig_queues.enqueue(signal);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns a reference to the profiling clock of the current thread.
|
||||||
|
pub fn prof_clock(&self) -> &Arc<ProfClock> {
|
||||||
|
&self.prof_clock
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a timer based on the profiling CPU clock of the current thread.
|
||||||
|
pub fn create_prof_timer<F>(&self, func: F) -> Arc<Timer>
|
||||||
|
where
|
||||||
|
F: Fn() + Send + Sync + 'static,
|
||||||
|
{
|
||||||
|
self.prof_timer_manager.create_timer(func)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a timer based on the user CPU clock of the current thread.
|
||||||
|
pub fn create_virtual_timer<F>(&self, func: F) -> Arc<Timer>
|
||||||
|
where
|
||||||
|
F: Fn() + Send + Sync + 'static,
|
||||||
|
{
|
||||||
|
self.virtual_timer_manager.create_timer(func)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Checks the `TimerCallback`s that are managed by the `prof_timer_manager`.
|
||||||
|
/// If any have timed out, call the corresponding callback functions.
|
||||||
|
pub fn process_expired_timers(&self) {
|
||||||
|
self.prof_timer_manager.process_expired_timers();
|
||||||
|
}
|
||||||
|
|
||||||
pub fn dequeue_signal(&self, mask: &SigMask) -> Option<Box<dyn Signal>> {
|
pub fn dequeue_signal(&self, mask: &SigMask) -> Option<Box<dyn Signal>> {
|
||||||
self.sig_queues.dequeue(mask)
|
self.sig_queues.dequeue(mask)
|
||||||
}
|
}
|
||||||
|
@ -1,15 +1,13 @@
|
|||||||
// SPDX-License-Identifier: MPL-2.0
|
// SPDX-License-Identifier: MPL-2.0
|
||||||
|
|
||||||
|
use self::timer_manager::PosixTimerManager;
|
||||||
use super::{
|
use super::{
|
||||||
posix_thread::PosixThreadExt,
|
posix_thread::PosixThreadExt,
|
||||||
process_table,
|
process_table,
|
||||||
process_vm::{Heap, InitStackReader, ProcessVm},
|
process_vm::{Heap, InitStackReader, ProcessVm},
|
||||||
rlimit::ResourceLimits,
|
rlimit::ResourceLimits,
|
||||||
signal::{
|
signal::{
|
||||||
constants::{SIGALRM, SIGCHLD},
|
constants::SIGCHLD, sig_disposition::SigDispositions, sig_mask::SigMask, signals::Signal,
|
||||||
sig_disposition::SigDispositions,
|
|
||||||
sig_mask::SigMask,
|
|
||||||
signals::{kernel::KernelSignal, Signal},
|
|
||||||
Pauser,
|
Pauser,
|
||||||
},
|
},
|
||||||
status::ProcessStatus,
|
status::ProcessStatus,
|
||||||
@ -20,12 +18,8 @@ use crate::{
|
|||||||
fs::{file_table::FileTable, fs_resolver::FsResolver, utils::FileCreationMask},
|
fs::{file_table::FileTable, fs_resolver::FsResolver, utils::FileCreationMask},
|
||||||
prelude::*,
|
prelude::*,
|
||||||
sched::nice::Nice,
|
sched::nice::Nice,
|
||||||
thread::{
|
thread::{allocate_tid, Thread},
|
||||||
allocate_tid,
|
time::clocks::ProfClock,
|
||||||
work_queue::{submit_work_item, work_item::WorkItem},
|
|
||||||
Thread,
|
|
||||||
},
|
|
||||||
time::{clocks::RealTimeClock, Timer},
|
|
||||||
vm::vmar::Vmar,
|
vm::vmar::Vmar,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -34,6 +28,7 @@ mod job_control;
|
|||||||
mod process_group;
|
mod process_group;
|
||||||
mod session;
|
mod session;
|
||||||
mod terminal;
|
mod terminal;
|
||||||
|
mod timer_manager;
|
||||||
|
|
||||||
use aster_rights::Full;
|
use aster_rights::Full;
|
||||||
use atomic::Atomic;
|
use atomic::Atomic;
|
||||||
@ -52,6 +47,10 @@ pub type Sid = u32;
|
|||||||
|
|
||||||
pub type ExitCode = i32;
|
pub type ExitCode = i32;
|
||||||
|
|
||||||
|
pub(super) fn init() {
|
||||||
|
timer_manager::init();
|
||||||
|
}
|
||||||
|
|
||||||
/// Process stands for a set of threads that shares the same userspace.
|
/// Process stands for a set of threads that shares the same userspace.
|
||||||
pub struct Process {
|
pub struct Process {
|
||||||
// Immutable Part
|
// Immutable Part
|
||||||
@ -60,8 +59,7 @@ pub struct Process {
|
|||||||
process_vm: ProcessVm,
|
process_vm: ProcessVm,
|
||||||
/// Wait for child status changed
|
/// Wait for child status changed
|
||||||
children_pauser: Arc<Pauser>,
|
children_pauser: Arc<Pauser>,
|
||||||
/// The timer counts down in real (i.e., wall clock) time
|
|
||||||
alarm_timer: Arc<Timer>,
|
|
||||||
// Mutable Part
|
// Mutable Part
|
||||||
/// The executable path.
|
/// The executable path.
|
||||||
executable_path: RwLock<String>,
|
executable_path: RwLock<String>,
|
||||||
@ -91,26 +89,12 @@ pub struct Process {
|
|||||||
// Signal
|
// Signal
|
||||||
/// Sig dispositions
|
/// Sig dispositions
|
||||||
sig_dispositions: Arc<Mutex<SigDispositions>>,
|
sig_dispositions: Arc<Mutex<SigDispositions>>,
|
||||||
}
|
|
||||||
|
|
||||||
fn create_process_timer_callback(process_ref: &Weak<Process>) -> impl Fn() {
|
/// A profiling clock measures the user CPU time and kernel CPU time of the current process.
|
||||||
let current_process = process_ref.clone();
|
prof_clock: Arc<ProfClock>,
|
||||||
let sent_signal = move || {
|
|
||||||
let signal = KernelSignal::new(SIGALRM);
|
|
||||||
if let Some(process) = current_process.upgrade() {
|
|
||||||
process.enqueue_signal(signal);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let work_func = Box::new(sent_signal);
|
/// A manager that manages timer resources and utilities of the process.
|
||||||
let work_item = Arc::new(WorkItem::new(work_func));
|
timer_manager: PosixTimerManager,
|
||||||
|
|
||||||
move || {
|
|
||||||
submit_work_item(
|
|
||||||
work_item.clone(),
|
|
||||||
crate::thread::work_queue::WorkPriority::High,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Process {
|
impl Process {
|
||||||
@ -135,28 +119,26 @@ impl Process {
|
|||||||
Pauser::new_with_mask(sigmask)
|
Pauser::new_with_mask(sigmask)
|
||||||
};
|
};
|
||||||
|
|
||||||
Arc::new_cyclic(|process_ref: &Weak<Process>| {
|
let prof_clock = ProfClock::new();
|
||||||
let callback = create_process_timer_callback(process_ref);
|
|
||||||
let alarm_timer = RealTimeClock::timer_manager().create_timer(callback);
|
|
||||||
|
|
||||||
Self {
|
Arc::new_cyclic(|process_ref: &Weak<Process>| Self {
|
||||||
pid,
|
pid,
|
||||||
threads: Mutex::new(threads),
|
threads: Mutex::new(threads),
|
||||||
executable_path: RwLock::new(executable_path),
|
executable_path: RwLock::new(executable_path),
|
||||||
process_vm,
|
process_vm,
|
||||||
children_pauser,
|
children_pauser,
|
||||||
alarm_timer,
|
status: Mutex::new(ProcessStatus::Uninit),
|
||||||
status: Mutex::new(ProcessStatus::Uninit),
|
parent: Mutex::new(parent),
|
||||||
parent: Mutex::new(parent),
|
children: Mutex::new(BTreeMap::new()),
|
||||||
children: Mutex::new(BTreeMap::new()),
|
process_group: Mutex::new(Weak::new()),
|
||||||
process_group: Mutex::new(Weak::new()),
|
file_table,
|
||||||
file_table,
|
fs,
|
||||||
fs,
|
umask,
|
||||||
umask,
|
sig_dispositions,
|
||||||
sig_dispositions,
|
resource_limits: Mutex::new(resource_limits),
|
||||||
resource_limits: Mutex::new(resource_limits),
|
nice: Atomic::new(nice),
|
||||||
nice: Atomic::new(nice),
|
timer_manager: PosixTimerManager::new(&prof_clock, process_ref),
|
||||||
}
|
prof_clock,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -233,8 +215,14 @@ impl Process {
|
|||||||
self.pid
|
self.pid
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn alarm_timer(&self) -> &Arc<Timer> {
|
/// Gets the profiling clock of the process.
|
||||||
&self.alarm_timer
|
pub fn prof_clock(&self) -> &Arc<ProfClock> {
|
||||||
|
&self.prof_clock
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Gets the timer resources and utilities of the process.
|
||||||
|
pub fn timer_manager(&self) -> &PosixTimerManager {
|
||||||
|
&self.timer_manager
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn threads(&self) -> &Mutex<Vec<Arc<Thread>>> {
|
pub fn threads(&self) -> &Mutex<Vec<Arc<Thread>>> {
|
||||||
@ -288,7 +276,7 @@ impl Process {
|
|||||||
|
|
||||||
// *********** Process group & Session***********
|
// *********** Process group & Session***********
|
||||||
|
|
||||||
/// Returns the process group id of the process.
|
/// Returns the process group ID of the process.
|
||||||
pub fn pgid(&self) -> Pgid {
|
pub fn pgid(&self) -> Pgid {
|
||||||
if let Some(process_group) = self.process_group.lock().upgrade() {
|
if let Some(process_group) = self.process_group.lock().upgrade() {
|
||||||
process_group.pgid()
|
process_group.pgid()
|
||||||
@ -341,7 +329,7 @@ impl Process {
|
|||||||
///
|
///
|
||||||
/// This method may return the following errors:
|
/// This method may return the following errors:
|
||||||
/// * `EPERM`, if the process is a process group leader, or some existing session
|
/// * `EPERM`, if the process is a process group leader, or some existing session
|
||||||
/// or process group has the same id as the process.
|
/// or process group has the same ID as the process.
|
||||||
pub fn to_new_session(self: &Arc<Self>) -> Result<Arc<Session>> {
|
pub fn to_new_session(self: &Arc<Self>) -> Result<Arc<Session>> {
|
||||||
if self.is_session_leader() {
|
if self.is_session_leader() {
|
||||||
return Ok(self.session().unwrap());
|
return Ok(self.session().unwrap());
|
||||||
@ -439,7 +427,7 @@ impl Process {
|
|||||||
if pgid != self.pid() {
|
if pgid != self.pid() {
|
||||||
return_errno_with_message!(
|
return_errno_with_message!(
|
||||||
Errno::EPERM,
|
Errno::EPERM,
|
||||||
"the new process group should have the same id as the process."
|
"the new process group should have the same ID as the process."
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -637,8 +625,6 @@ pub fn current() -> Arc<Process> {
|
|||||||
#[cfg(ktest)]
|
#[cfg(ktest)]
|
||||||
mod test {
|
mod test {
|
||||||
|
|
||||||
use spin::Once;
|
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
fn new_process(parent: Option<Arc<Process>>) -> Arc<Process> {
|
fn new_process(parent: Option<Arc<Process>>) -> Arc<Process> {
|
||||||
|
200
kernel/aster-nix/src/process/process/timer_manager.rs
Normal file
200
kernel/aster-nix/src/process/process/timer_manager.rs
Normal file
@ -0,0 +1,200 @@
|
|||||||
|
// SPDX-License-Identifier: MPL-2.0
|
||||||
|
|
||||||
|
use alloc::{
|
||||||
|
boxed::Box,
|
||||||
|
sync::{Arc, Weak},
|
||||||
|
vec::Vec,
|
||||||
|
};
|
||||||
|
use core::time::Duration;
|
||||||
|
|
||||||
|
use aster_frame::{
|
||||||
|
arch::{
|
||||||
|
timer::{self, TIMER_FREQ},
|
||||||
|
x86::trap::is_kernel_interrupted,
|
||||||
|
},
|
||||||
|
sync::Mutex,
|
||||||
|
};
|
||||||
|
use id_alloc::IdAlloc;
|
||||||
|
|
||||||
|
use super::Process;
|
||||||
|
use crate::{
|
||||||
|
process::{
|
||||||
|
posix_thread::PosixThreadExt,
|
||||||
|
signal::{constants::SIGALRM, signals::kernel::KernelSignal},
|
||||||
|
},
|
||||||
|
thread::{
|
||||||
|
work_queue::{submit_work_item, work_item::WorkItem},
|
||||||
|
Thread,
|
||||||
|
},
|
||||||
|
time::{
|
||||||
|
clocks::{ProfClock, RealTimeClock},
|
||||||
|
Timer, TimerManager,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Updates the CPU time recorded in the CPU clocks of current Process.
|
||||||
|
///
|
||||||
|
/// This function will be invoked at the system timer interrupt, and
|
||||||
|
/// invoke the callbacks of expired timers which are based on the updated
|
||||||
|
/// CPU clock.
|
||||||
|
fn update_cpu_time() {
|
||||||
|
let current_thread = Thread::current();
|
||||||
|
if let Some(posix_thread) = current_thread.as_posix_thread() {
|
||||||
|
let process = posix_thread.process();
|
||||||
|
let timer_manager = process.timer_manager();
|
||||||
|
let jiffies_interval = Duration::from_millis(1000 / TIMER_FREQ);
|
||||||
|
// Based on whether the timer interrupt occurs in kernel mode or user mode,
|
||||||
|
// the function will add the duration of one timer interrupt interval to the
|
||||||
|
// corresponding CPU clocks.
|
||||||
|
if is_kernel_interrupted() {
|
||||||
|
posix_thread
|
||||||
|
.prof_clock()
|
||||||
|
.kernel_clock()
|
||||||
|
.add_time(jiffies_interval);
|
||||||
|
process
|
||||||
|
.prof_clock()
|
||||||
|
.kernel_clock()
|
||||||
|
.add_time(jiffies_interval);
|
||||||
|
} else {
|
||||||
|
posix_thread
|
||||||
|
.prof_clock()
|
||||||
|
.user_clock()
|
||||||
|
.add_time(jiffies_interval);
|
||||||
|
process.prof_clock().user_clock().add_time(jiffies_interval);
|
||||||
|
timer_manager
|
||||||
|
.virtual_timer()
|
||||||
|
.timer_manager()
|
||||||
|
.process_expired_timers();
|
||||||
|
}
|
||||||
|
timer_manager
|
||||||
|
.prof_timer()
|
||||||
|
.timer_manager()
|
||||||
|
.process_expired_timers();
|
||||||
|
posix_thread.process_expired_timers();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Registers a function to update the CPU clock in processes and
|
||||||
|
/// threads during the system timer interrupt.
|
||||||
|
pub(super) fn init() {
|
||||||
|
timer::register_callback(update_cpu_time);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Represents timer resources and utilities for a POSIX process.
|
||||||
|
pub struct PosixTimerManager {
|
||||||
|
/// A real-time countdown timer, measuring in wall clock time.
|
||||||
|
alarm_timer: Arc<Timer>,
|
||||||
|
/// A timer based on user CPU clock.
|
||||||
|
virtual_timer: Arc<Timer>,
|
||||||
|
/// A timer based on the profiling clock.
|
||||||
|
prof_timer: Arc<Timer>,
|
||||||
|
/// An ID allocator to allocate unique timer IDs.
|
||||||
|
id_allocator: Mutex<IdAlloc>,
|
||||||
|
/// A container managing all POSIX timers created by `timer_create()` syscall
|
||||||
|
/// within the process context.
|
||||||
|
posix_timers: Mutex<Vec<Option<Arc<Timer>>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create_process_timer_callback(process_ref: &Weak<Process>) -> impl Fn() + Clone {
|
||||||
|
let current_process = process_ref.clone();
|
||||||
|
let sent_signal = move || {
|
||||||
|
let signal = KernelSignal::new(SIGALRM);
|
||||||
|
if let Some(process) = current_process.upgrade() {
|
||||||
|
process.enqueue_signal(signal);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let work_func = Box::new(sent_signal);
|
||||||
|
let work_item = Arc::new(WorkItem::new(work_func));
|
||||||
|
|
||||||
|
move || {
|
||||||
|
submit_work_item(
|
||||||
|
work_item.clone(),
|
||||||
|
crate::thread::work_queue::WorkPriority::High,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PosixTimerManager {
|
||||||
|
pub(super) fn new(prof_clock: &Arc<ProfClock>, process_ref: &Weak<Process>) -> Self {
|
||||||
|
const MAX_NUM_OF_POSIX_TIMERS: usize = 10000;
|
||||||
|
|
||||||
|
let callback = create_process_timer_callback(process_ref);
|
||||||
|
|
||||||
|
let alarm_timer = RealTimeClock::timer_manager().create_timer(callback.clone());
|
||||||
|
|
||||||
|
let virtual_timer =
|
||||||
|
TimerManager::new(prof_clock.user_clock().clone()).create_timer(callback.clone());
|
||||||
|
let prof_timer = TimerManager::new(prof_clock.clone()).create_timer(callback);
|
||||||
|
|
||||||
|
Self {
|
||||||
|
alarm_timer,
|
||||||
|
virtual_timer,
|
||||||
|
prof_timer,
|
||||||
|
id_allocator: Mutex::new(IdAlloc::with_capacity(MAX_NUM_OF_POSIX_TIMERS)),
|
||||||
|
posix_timers: Mutex::new(Vec::new()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Gets the alarm timer of the corresponding process.
|
||||||
|
pub fn alarm_timer(&self) -> &Arc<Timer> {
|
||||||
|
&self.alarm_timer
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Gets the virtual timer of the corresponding process.
|
||||||
|
pub fn virtual_timer(&self) -> &Arc<Timer> {
|
||||||
|
&self.virtual_timer
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Gets the profiling timer of the corresponding process.
|
||||||
|
pub fn prof_timer(&self) -> &Arc<Timer> {
|
||||||
|
&self.prof_timer
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a timer based on the profiling CPU clock of the current process.
|
||||||
|
pub fn create_prof_timer<F>(&self, func: F) -> Arc<Timer>
|
||||||
|
where
|
||||||
|
F: Fn() + Send + Sync + 'static,
|
||||||
|
{
|
||||||
|
self.prof_timer.timer_manager().create_timer(func)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a timer based on the user CPU clock of the current process.
|
||||||
|
pub fn create_virtual_timer<F>(&self, func: F) -> Arc<Timer>
|
||||||
|
where
|
||||||
|
F: Fn() + Send + Sync + 'static,
|
||||||
|
{
|
||||||
|
self.virtual_timer.timer_manager().create_timer(func)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Adds a POSIX timer to the managed `posix_timers`, and allocate a timer ID for this timer.
|
||||||
|
/// Return the timer ID.
|
||||||
|
pub fn add_posix_timer(&self, posix_timer: Arc<Timer>) -> usize {
|
||||||
|
let mut timers = self.posix_timers.lock();
|
||||||
|
// Holding the lock of `posix_timers` is required to operate the `id_allocator`.
|
||||||
|
let timer_id = self.id_allocator.lock().alloc().unwrap();
|
||||||
|
if timers.len() < timer_id + 1 {
|
||||||
|
timers.resize(timer_id + 1, None);
|
||||||
|
}
|
||||||
|
// The ID allocated is not used by any other timers so this index in `timers`
|
||||||
|
// must be `None`.
|
||||||
|
timers[timer_id] = Some(posix_timer);
|
||||||
|
timer_id
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Finds a POSIX timer by the input `timer_id`.
|
||||||
|
pub fn find_posix_timer(&self, timer_id: usize) -> Option<Arc<Timer>> {
|
||||||
|
self.posix_timers.lock()[timer_id].clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Removes the POSIX timer with the ID `timer_id`.
|
||||||
|
pub fn remove_posix_timer(&self, timer_id: usize) -> Option<Arc<Timer>> {
|
||||||
|
let mut timers = self.posix_timers.lock();
|
||||||
|
let timer = timers[timer_id].take();
|
||||||
|
if timer.is_some() {
|
||||||
|
// Holding the lock of `posix_timers` is required to operate the `id_allocator`.
|
||||||
|
self.id_allocator.lock().free(timer_id);
|
||||||
|
}
|
||||||
|
timer
|
||||||
|
}
|
||||||
|
}
|
67
kernel/aster-nix/src/time/clocks/cpu_clock.rs
Normal file
67
kernel/aster-nix/src/time/clocks/cpu_clock.rs
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
// SPDX-License-Identifier: MPL-2.0
|
||||||
|
|
||||||
|
use alloc::sync::Arc;
|
||||||
|
use core::time::Duration;
|
||||||
|
|
||||||
|
use aster_frame::sync::SpinLock;
|
||||||
|
|
||||||
|
use crate::time::Clock;
|
||||||
|
|
||||||
|
/// A clock used to record the CPU time for processes and threads.
|
||||||
|
pub struct CpuClock {
|
||||||
|
time: SpinLock<Duration>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A profiling clock that contains a user CPU clock and a kernel CPU clock.
|
||||||
|
/// These two clocks record the CPU time in user mode and kernel mode respectively.
|
||||||
|
/// Reading this clock directly returns the sum of both times.
|
||||||
|
pub struct ProfClock {
|
||||||
|
user_clock: Arc<CpuClock>,
|
||||||
|
kernel_clock: Arc<CpuClock>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CpuClock {
|
||||||
|
/// Creates a new `CpuClock`. The recorded time is initialized to 0.
|
||||||
|
pub fn new() -> Arc<Self> {
|
||||||
|
Arc::new(Self {
|
||||||
|
time: SpinLock::new(Duration::ZERO),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Adds `interval` to the original recorded time to update the `CpuClock`.
|
||||||
|
pub fn add_time(&self, interval: Duration) {
|
||||||
|
*self.time.lock_irq_disabled() += interval;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Clock for CpuClock {
|
||||||
|
fn read_time(&self) -> Duration {
|
||||||
|
*self.time.lock_irq_disabled()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ProfClock {
|
||||||
|
/// Creates a new `ProfClock`. The recorded time is initialized to 0.
|
||||||
|
pub fn new() -> Arc<Self> {
|
||||||
|
Arc::new(Self {
|
||||||
|
user_clock: CpuClock::new(),
|
||||||
|
kernel_clock: CpuClock::new(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a reference to the user CPU clock in this profiling clock.
|
||||||
|
pub fn user_clock(&self) -> &Arc<CpuClock> {
|
||||||
|
&self.user_clock
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a reference to the kernel CPU clock in this profiling clock.
|
||||||
|
pub fn kernel_clock(&self) -> &Arc<CpuClock> {
|
||||||
|
&self.kernel_clock
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Clock for ProfClock {
|
||||||
|
fn read_time(&self) -> Duration {
|
||||||
|
self.user_clock.read_time() + self.kernel_clock.read_time()
|
||||||
|
}
|
||||||
|
}
|
@ -1,7 +1,9 @@
|
|||||||
// SPDX-License-Identifier: MPL-2.0
|
// SPDX-License-Identifier: MPL-2.0
|
||||||
|
|
||||||
|
pub use cpu_clock::*;
|
||||||
pub use system_wide::*;
|
pub use system_wide::*;
|
||||||
|
|
||||||
|
mod cpu_clock;
|
||||||
mod system_wide;
|
mod system_wide;
|
||||||
|
|
||||||
pub(super) fn init() {
|
pub(super) fn init() {
|
||||||
|
@ -124,6 +124,11 @@ impl BootTimeClock {
|
|||||||
pub fn get() -> &'static Arc<BootTimeClock> {
|
pub fn get() -> &'static Arc<BootTimeClock> {
|
||||||
CLOCK_BOOTTIME_INSTANCE.get().unwrap()
|
CLOCK_BOOTTIME_INSTANCE.get().unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get the cpu-local system-wide `TimerManager` singleton of this clock.
|
||||||
|
pub fn timer_manager() -> &'static Arc<TimerManager> {
|
||||||
|
CLOCK_BOOTTIME_MANAGER.get().unwrap()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Clock for JiffiesClock {
|
impl Clock for JiffiesClock {
|
||||||
@ -232,7 +237,7 @@ define_system_clocks! {
|
|||||||
CLOCK_BOOTTIME => BootTimeClock,
|
CLOCK_BOOTTIME => BootTimeClock,
|
||||||
}
|
}
|
||||||
|
|
||||||
define_timer_managers![CLOCK_REALTIME, CLOCK_MONOTONIC,];
|
define_timer_managers![CLOCK_REALTIME, CLOCK_MONOTONIC, CLOCK_BOOTTIME,];
|
||||||
|
|
||||||
/// Init the system-wide clocks.
|
/// Init the system-wide clocks.
|
||||||
fn init_system_wide_clocks() {
|
fn init_system_wide_clocks() {
|
||||||
|
Reference in New Issue
Block a user