Add fields in `/proc/*/stat` and `/proc/*/status`

This commit is contained in:
Wang Siyuan 2025-07-02 02:54:59 +00:00 committed by Ruihan Li
parent 513532fe95
commit a13297ae4c
15 changed files with 360 additions and 132 deletions

View File

@ -41,49 +41,64 @@ pub enum DeviceType {
MiscDevice,
}
/// Device Id
#[derive(Clone, Copy)]
pub struct DeviceId(u64);
impl DeviceId {
pub fn new(major: u32, minor: u32) -> Self {
let major = major as u64;
let minor = minor as u64;
Self(
((major & 0xffff_f000) << 32)
| ((major & 0x0000_0fff) << 8)
| ((minor & 0xffff_ff00) << 12)
| (minor & 0x0000_00ff),
)
}
pub fn major(&self) -> u32 {
((self.0 >> 32) & 0xffff_f000 | (self.0 >> 8) & 0x0000_0fff) as u32
}
pub fn minor(&self) -> u32 {
((self.0 >> 12) & 0xffff_ff00 | self.0 & 0x0000_00ff) as u32
}
/// A device ID, containing a major device number and a minor device number.
#[derive(Clone, Copy, Debug)]
pub struct DeviceId {
major: u32,
minor: u32,
}
impl Debug for DeviceId {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
f.debug_struct("DeviceId")
.field("major", &self.major())
.field("minor", &self.minor())
.finish()
impl DeviceId {
/// Creates a device ID from the major device number and the minor device number.
pub fn new(major: u32, minor: u32) -> Self {
Self { major, minor }
}
/// Returns the major device number.
pub fn major(&self) -> u32 {
self.major
}
/// Returns the minor device number.
pub fn minor(&self) -> u32 {
self.minor
}
/// Encodes the device ID as a `u32` value.
///
/// The encoding strategy here is the same as in Linux. See the Linux implementation at:
/// <https://github.com/torvalds/linux/blob/0ff41df1cb268fc69e703a08a57ee14ae967d0ca/include/linux/kdev_t.h#L39-L44>
pub fn as_encoded_u32(&self) -> u32 {
self.as_encoded_u64() as u32
}
/// Encodes the device ID as a `u64` value.
fn as_encoded_u64(&self) -> u64 {
let major = self.major() as u64;
let minor = self.minor() as u64;
((major & 0xffff_f000) << 32)
| ((major & 0x0000_0fff) << 8)
| ((minor & 0xffff_ff00) << 12)
| (minor & 0x0000_00ff)
}
/// Decodes the device ID from a `u64` value.
fn decode_from_u64(raw: u64) -> Self {
let major = ((raw >> 32) & 0xffff_f000 | (raw >> 8) & 0x0000_0fff) as u32;
let minor = ((raw >> 12) & 0xffff_ff00 | raw & 0x0000_00ff) as u32;
Self::new(major, minor)
}
}
impl From<DeviceId> for u64 {
fn from(value: DeviceId) -> Self {
value.0
value.as_encoded_u64()
}
}
impl From<u64> for DeviceId {
fn from(raw: u64) -> Self {
Self(raw)
Self::decode_from_u64(raw)
}
}

View File

@ -1,7 +1,8 @@
// SPDX-License-Identifier: MPL-2.0
use self::{
cmdline::CmdlineFileOps, comm::CommFileOps, exe::ExeSymOps, fd::FdDirOps, task::TaskDirOps,
cmdline::CmdlineFileOps, comm::CommFileOps, exe::ExeSymOps, fd::FdDirOps, stat::StatFileOps,
status::StatusFileOps, task::TaskDirOps,
};
use super::template::{DirOps, ProcDir, ProcDirBuilder};
use crate::{
@ -64,8 +65,12 @@ impl DirOps for PidDirOps {
"comm" => CommFileOps::new_inode(self.0.clone(), this_ptr.clone()),
"fd" => FdDirOps::new_inode(self.0.clone(), this_ptr.clone()),
"cmdline" => CmdlineFileOps::new_inode(self.0.clone(), this_ptr.clone()),
"status" => status::StatusFileOps::new_inode(self.0.clone(), this_ptr.clone()),
"stat" => stat::StatFileOps::new_inode(self.0.clone(), this_ptr.clone()),
"status" => {
StatusFileOps::new_inode(self.0.clone(), self.0.main_thread(), this_ptr.clone())
}
"stat" => {
StatFileOps::new_inode(self.0.clone(), self.0.main_thread(), true, this_ptr.clone())
}
"task" => TaskDirOps::new_inode(self.0.clone(), this_ptr.clone()),
_ => return_errno!(Errno::ENOENT),
};
@ -91,10 +96,10 @@ impl DirOps for PidDirOps {
CmdlineFileOps::new_inode(self.0.clone(), this_ptr.clone())
});
cached_children.put_entry_if_not_found("status", || {
status::StatusFileOps::new_inode(self.0.clone(), this_ptr.clone())
StatusFileOps::new_inode(self.0.clone(), self.0.main_thread(), this_ptr.clone())
});
cached_children.put_entry_if_not_found("stat", || {
stat::StatFileOps::new_inode(self.0.clone(), this_ptr.clone())
StatFileOps::new_inode(self.0.clone(), self.0.main_thread(), true, this_ptr.clone())
});
cached_children.put_entry_if_not_found("task", || {
TaskDirOps::new_inode(self.0.clone(), this_ptr.clone())

View File

@ -1,6 +1,6 @@
// SPDX-License-Identifier: MPL-2.0
use core::fmt::Write;
use core::{fmt::Write, sync::atomic::Ordering};
use crate::{
fs::{
@ -8,13 +8,18 @@ use crate::{
utils::Inode,
},
prelude::*,
process::posix_thread::AsPosixThread,
thread::Thread,
vm::vmar::RssType,
Process,
};
/// Represents the inode at `/proc/[pid]/stat`.
/// The fields are the same as the ones in `/proc/[pid]/status`. But the format is different.
/// See https://github.com/torvalds/linux/blob/ce1c54fdff7c4556b08f5b875a331d8952e8b6b7/fs/proc/array.c#L467
/// FIXME: Some fields are not implemented yet.
/// Represents the inode at either `/proc/[pid]/stat` or `/proc/[pid]/task/[tid]/stat`.
///
/// The fields are the same as the ones in `/proc/[pid]/status`, but the format is different.
/// See <https://github.com/torvalds/linux/blob/ce1c54fdff7c4556b08f5b875a331d8952e8b6b7/fs/proc/array.c#L467>.
///
/// FIXME: Some fields are not implemented or contain placeholders yet.
///
/// Fields:
/// - pid : Process ID.
@ -68,36 +73,133 @@ use crate::{
/// - env_start : Start address of environment variables.
/// - env_end : End address of environment variables.
/// - exit_code : Process exit code as returned by waitpid(2).
pub struct StatFileOps(Arc<Process>);
pub struct StatFileOps {
process_ref: Arc<Process>,
thread_ref: Arc<Thread>,
/// If `is_pid_stat` is true, this file corresponds to a process-level `/proc/[pid]/stat`.
/// Otherwise, this file corresponds to the thread-level `/proc/[pid]/task/[tid]/stat`.
is_pid_stat: bool,
}
impl StatFileOps {
pub fn new_inode(process_ref: Arc<Process>, parent: Weak<dyn Inode>) -> Arc<dyn Inode> {
ProcFileBuilder::new(Self(process_ref))
.parent(parent)
.build()
.unwrap()
pub fn new_inode(
process_ref: Arc<Process>,
thread_ref: Arc<Thread>,
is_pid_stat: bool,
parent: Weak<dyn Inode>,
) -> Arc<dyn Inode> {
ProcFileBuilder::new(Self {
process_ref,
thread_ref,
is_pid_stat,
})
.parent(parent)
.build()
.unwrap()
}
}
impl FileOps for StatFileOps {
fn data(&self) -> Result<Vec<u8>> {
let process = &self.0;
let process = &self.process_ref;
let thread = &self.thread_ref;
let posix_thread = thread.as_posix_thread().unwrap();
let pid = process.pid();
let comm = process.executable_path();
let state = if process.status().is_zombie() {
'Z'
} else {
'R'
};
// According to the Linux implementation, a process's `/proc/<pid>/stat` should be
// almost identical to its main thread's `/proc/<pid>/task/<pid>/stat`, except for
// fields `exit_code`, `wchan`, `min_flt`, `maj_flt`, `gtime`, `utime`, and `stime`.
//
// Reference: <https://github.com/torvalds/linux/blob/0ff41df1cb268fc69e703a08a57ee14ae967d0ca/fs/proc/array.c#L467-L681>
let pid = posix_thread.tid();
let comm = posix_thread
.thread_name()
.lock()
.as_ref()
.and_then(|name| name.as_string())
.unwrap_or_else(|| process.executable_path());
let state = if thread.is_exited() { 'Z' } else { 'R' };
let ppid = process.parent().pid();
let pgrp = process.pgid();
let session = process.sid();
let (tty_nr, tpgid) = if let Some(terminal) = process.terminal() {
(
terminal.id().as_encoded_u32(),
terminal
.job_control()
.foreground()
.map(|pgrp| pgrp.pgid() as i64)
.unwrap_or(-1),
)
} else {
(0, -1)
};
let flags = 0;
let min_flt = 0;
let cmin_flt = 0;
let maj_flt = 0;
let cmaj_flt = 0;
let (utime, stime) = {
let prof_clock = if self.is_pid_stat {
process.prof_clock()
} else {
posix_thread.prof_clock()
};
(
prof_clock.user_clock().read_jiffies().as_u64(),
prof_clock.kernel_clock().read_jiffies().as_u64(),
)
};
let cutime = 0;
let cstime = 0;
let priority = 0;
let nice = process.nice().load(Ordering::Relaxed).value().get();
let num_threads = process.tasks().lock().as_slice().len();
let itrealvalue = 0;
let starttime = 0;
let (vsize, rss) = if let Some(vmar_ref) = process.lock_root_vmar().as_ref() {
let vsize = vmar_ref.get_mappings_total_size();
let anon = vmar_ref.get_rss_counter(RssType::RSS_ANONPAGES);
let file = vmar_ref.get_rss_counter(RssType::RSS_FILEPAGES);
let rss = anon + file;
(vsize, rss)
} else {
(0, 0)
};
let mut stat_output = String::new();
writeln!(
stat_output,
"{} ({}) {} {} {}",
pid, comm, state, ppid, pgrp
"{} ({}) {} {} {} {} {} {} {} {} {} {} {} {} {} {} {} {} {} {} {} {} {} {}",
pid,
comm,
state,
ppid,
pgrp,
session,
tty_nr,
tpgid,
flags,
min_flt,
cmin_flt,
maj_flt,
cmaj_flt,
utime,
stime,
cutime,
cstime,
priority,
nice,
num_threads,
itrealvalue,
starttime,
vsize,
rss
)
.unwrap();
Ok(stat_output.into_bytes())

View File

@ -9,11 +9,12 @@ use crate::{
},
prelude::*,
process::posix_thread::AsPosixThread,
thread::Thread,
vm::vmar::RssType,
Process,
};
/// Represents the inode at `/proc/[pid]/status`.
/// Represents the inode at either `/proc/[pid]/status` or `/proc/[pid]/task/[tid]/status`.
/// See https://github.com/torvalds/linux/blob/ce1c54fdff7c4556b08f5b875a331d8952e8b6b7/fs/proc/array.c#L148
/// FIXME: Some fields are not implemented yet.
///
@ -59,54 +60,95 @@ use crate::{
/// - Mems_allowed_list: List of memory nodes allowed for this process.
/// - voluntary_ctxt_switches: Number of voluntary context switches.
/// - nonvoluntary_ctxt_switches: Number of nonvoluntary context switches.
pub struct StatusFileOps(Arc<Process>);
pub struct StatusFileOps {
process_ref: Arc<Process>,
thread_ref: Arc<Thread>,
}
impl StatusFileOps {
pub fn new_inode(process_ref: Arc<Process>, parent: Weak<dyn Inode>) -> Arc<dyn Inode> {
ProcFileBuilder::new(Self(process_ref))
.parent(parent)
.build()
.unwrap()
pub fn new_inode(
process_ref: Arc<Process>,
thread_ref: Arc<Thread>,
parent: Weak<dyn Inode>,
) -> Arc<dyn Inode> {
ProcFileBuilder::new(Self {
process_ref,
thread_ref,
})
.parent(parent)
.build()
.unwrap()
}
}
impl FileOps for StatusFileOps {
fn data(&self) -> Result<Vec<u8>> {
let process = &self.0;
let main_thread = process.main_thread();
let file_table = main_thread.as_posix_thread().unwrap().file_table();
let process = &self.process_ref;
let thread = &self.thread_ref;
let posix_thread = thread.as_posix_thread().unwrap();
// According to the Linux implementation, a process's `/proc/<pid>/status`
// is exactly the same as its main thread's `/proc/<pid>/task/<pid>/status`.
//
// Reference:
// <https://github.com/torvalds/linux/blob/0ff41df1cb268fc69e703a08a57ee14ae967d0ca/fs/proc/base.c#L3320>
// <https://github.com/torvalds/linux/blob/0ff41df1cb268fc69e703a08a57ee14ae967d0ca/fs/proc/base.c#L3669>
let mut status_output = String::new();
writeln!(status_output, "Name:\t{}", process.executable_path()).unwrap();
writeln!(
status_output,
"Name:\t{}",
posix_thread
.thread_name()
.lock()
.as_ref()
.and_then(|name| name.as_string())
.unwrap_or_else(|| process.executable_path())
)
.unwrap();
let state = if thread.is_exited() {
"Z (zombie)"
} else {
"R (running)"
};
writeln!(status_output, "State:\t{}", state).unwrap();
writeln!(status_output, "Tgid:\t{}", process.pid()).unwrap();
writeln!(status_output, "Pid:\t{}", process.pid()).unwrap();
writeln!(status_output, "Pid:\t{}", posix_thread.tid()).unwrap();
writeln!(status_output, "PPid:\t{}", process.parent().pid()).unwrap();
writeln!(status_output, "TracerPid:\t{}", process.parent().pid()).unwrap(); // Assuming TracerPid is the same as PPid
writeln!(status_output, "TracerPid:\t{}", 0).unwrap();
writeln!(
status_output,
"FDSize:\t{}",
file_table
posix_thread
.file_table()
.lock()
.as_ref()
.map(|file_table| file_table.read().len())
.unwrap_or(0)
)
.unwrap();
writeln!(
status_output,
"Threads:\t{}",
process.tasks().lock().as_slice().len()
)
.unwrap();
if let Some(vmar_ref) = process.lock_root_vmar().as_ref() {
let vsize = vmar_ref.get_mappings_total_size();
let anon = vmar_ref.get_rss_counter(RssType::RSS_ANONPAGES) * (PAGE_SIZE / 1024);
let file = vmar_ref.get_rss_counter(RssType::RSS_FILEPAGES) * (PAGE_SIZE / 1024);
let rss = anon + file;
writeln!(
status_output,
"VmRSS:\t{} kB\nRssAnon:\t{} kB\nRssFile:\t{} kB",
rss, anon, file
"VmSize:\t{} kB\nVmRSS:\t{} kB\nRssAnon:\t{} kB\nRssFile:\t{} kB",
vsize, rss, anon, file
)
.unwrap();
}
if Arc::ptr_eq(thread, &process.main_thread()) {
writeln!(
status_output,
"Threads:\t{}",
process.tasks().lock().as_slice().len()
)
.unwrap();
}

View File

@ -9,6 +9,7 @@ use crate::{
utils::{DirEntryVecExt, Inode},
},
process::posix_thread::AsPosixThread,
thread::{AsThread, Thread},
Process,
};
@ -25,22 +26,43 @@ impl TaskDirOps {
}
/// Represents the inode at `/proc/[pid]/task/[tid]`.
struct ThreadDirOps(Arc<Process>);
struct TidDirOps {
process_ref: Arc<Process>,
thread_ref: Arc<Thread>,
}
impl ThreadDirOps {
pub fn new_inode(process_ref: Arc<Process>, parent: Weak<dyn Inode>) -> Arc<dyn Inode> {
ProcDirBuilder::new(Self(process_ref))
.parent(parent)
.build()
.unwrap()
impl TidDirOps {
pub fn new_inode(
process_ref: Arc<Process>,
thread_ref: Arc<Thread>,
parent: Weak<dyn Inode>,
) -> Arc<dyn Inode> {
ProcDirBuilder::new(Self {
process_ref,
thread_ref,
})
.parent(parent)
.build()
.unwrap()
}
}
impl DirOps for ThreadDirOps {
impl DirOps for TidDirOps {
fn lookup_child(&self, this_ptr: Weak<dyn Inode>, name: &str) -> Result<Arc<dyn Inode>> {
let inode = match name {
"fd" => FdDirOps::new_inode(self.0.clone(), this_ptr.clone()),
"exe" => ExeSymOps::new_inode(self.0.clone(), this_ptr.clone()),
"fd" => FdDirOps::new_inode(self.process_ref.clone(), this_ptr.clone()),
"exe" => ExeSymOps::new_inode(self.process_ref.clone(), this_ptr.clone()),
"stat" => StatFileOps::new_inode(
self.process_ref.clone(),
self.thread_ref.clone(),
false,
this_ptr.clone(),
),
"status" => StatusFileOps::new_inode(
self.process_ref.clone(),
self.thread_ref.clone(),
this_ptr.clone(),
),
_ => return_errno!(Errno::ENOENT),
};
Ok(inode)
@ -49,14 +71,29 @@ impl DirOps for ThreadDirOps {
fn populate_children(&self, this_ptr: Weak<dyn Inode>) {
let this = {
let this = this_ptr.upgrade().unwrap();
this.downcast_ref::<ProcDir<ThreadDirOps>>().unwrap().this()
this.downcast_ref::<ProcDir<TidDirOps>>().unwrap().this()
};
let mut cached_children = this.cached_children().write();
cached_children.put_entry_if_not_found("fd", || {
FdDirOps::new_inode(self.0.clone(), this_ptr.clone())
FdDirOps::new_inode(self.process_ref.clone(), this_ptr.clone())
});
cached_children.put_entry_if_not_found("exe", || {
ExeSymOps::new_inode(self.0.clone(), this_ptr.clone())
ExeSymOps::new_inode(self.process_ref.clone(), this_ptr.clone())
});
cached_children.put_entry_if_not_found("stat", || {
StatFileOps::new_inode(
self.process_ref.clone(),
self.thread_ref.clone(),
false,
this_ptr.clone(),
)
});
cached_children.put_entry_if_not_found("status", || {
StatusFileOps::new_inode(
self.process_ref.clone(),
self.thread_ref.clone(),
this_ptr.clone(),
)
});
}
}
@ -68,10 +105,15 @@ impl DirOps for TaskDirOps {
};
for task in self.0.tasks().lock().as_slice() {
if task.as_posix_thread().unwrap().tid() != tid {
let thread = task.as_thread().unwrap();
if thread.as_posix_thread().unwrap().tid() != tid {
continue;
}
return Ok(ThreadDirOps::new_inode(self.0.clone(), this_ptr));
return Ok(TidDirOps::new_inode(
self.0.clone(),
thread.clone(),
this_ptr,
));
}
return_errno_with_message!(Errno::ENOENT, "No such thread")
}
@ -83,9 +125,10 @@ impl DirOps for TaskDirOps {
};
let mut cached_children = this.cached_children().write();
for task in self.0.tasks().lock().as_slice() {
let thread = task.as_thread().unwrap();
cached_children.put_entry_if_not_found(
&format!("{}", task.as_posix_thread().unwrap().tid()),
|| ThreadDirOps::new_inode(self.0.clone(), this_ptr.clone()),
|| TidDirOps::new_inode(self.0.clone(), thread.clone(), this_ptr.clone()),
);
}
}

View File

@ -257,6 +257,9 @@ fn clone_child_task(
// Inherit sigmask from current thread
let sig_mask = posix_thread.sig_mask().load(Ordering::Relaxed).into();
// Inherit the thread name.
let thread_name = posix_thread.thread_name().lock().as_ref().cloned();
let child_tid = allocate_posix_tid();
let child_task = {
let credentials = {
@ -266,6 +269,7 @@ fn clone_child_task(
let mut thread_builder = PosixThreadBuilder::new(child_tid, child_user_ctx, credentials)
.process(posix_thread.weak_process())
.thread_name(thread_name)
.sig_mask(sig_mask)
.file_table(child_file_table)
.fs(child_fs);

View File

@ -1,10 +1,12 @@
// SPDX-License-Identifier: MPL-2.0
use alloc::borrow::ToOwned;
use crate::prelude::*;
pub const MAX_THREAD_NAME_LEN: usize = 16;
#[derive(Debug)]
#[derive(Debug, Clone)]
pub struct ThreadName {
inner: [u8; MAX_THREAD_NAME_LEN],
count: usize,
@ -50,7 +52,12 @@ impl ThreadName {
Ok(())
}
pub fn name(&self) -> Result<Option<&CStr>> {
Ok(Some(CStr::from_bytes_until_nul(&self.inner)?))
pub fn name(&self) -> Option<&CStr> {
CStr::from_bytes_until_nul(&self.inner).ok()
}
pub fn as_string(&self) -> Option<String> {
let name = self.name()?;
name.to_str().ok().map(|name| name.to_owned())
}
}

View File

@ -95,7 +95,7 @@ pub struct Process {
/// Whether the process has a subreaper that will reap it when the
/// process becomes orphaned.
///
///
/// If `has_child_subreaper` is true in a `Process`, this attribute should
/// also be true for all of its descendants.
pub(super) has_child_subreaper: AtomicBool,

View File

@ -5,7 +5,7 @@ use alloc::sync::Arc;
use super::{session::SessionGuard, JobControl, Pgid, Process, Session, Sid};
use crate::{
current_userspace,
fs::{inode_handle::FileIo, utils::IoctlCmd},
fs::{device::Device, inode_handle::FileIo, utils::IoctlCmd},
prelude::{current, return_errno_with_message, warn, Errno, Error, Result},
process::process_table,
};
@ -14,7 +14,7 @@ use crate::{
///
/// We currently support two kinds of terminal, the TTY and PTY. They're associated with a
/// `JobControl` to track the session and the foreground process group.
pub trait Terminal: FileIo {
pub trait Terminal: FileIo + Device {
/// Returns the job control of the terminal.
fn job_control(&self) -> &JobControl;
}

View File

@ -5,14 +5,9 @@ use alloc::{
sync::{Arc, Weak},
vec::Vec,
};
use core::time::Duration;
use id_alloc::IdAlloc;
use ostd::{
arch::{timer::TIMER_FREQ, trap::is_kernel_interrupted},
sync::Mutex,
timer,
};
use ostd::{arch::trap::is_kernel_interrupted, sync::Mutex, timer};
use super::Process;
use crate::{
@ -48,25 +43,15 @@ fn update_cpu_time() {
return;
};
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);
posix_thread.prof_clock().kernel_clock().add_jiffies(1);
process.prof_clock().kernel_clock().add_jiffies(1);
} else {
posix_thread
.prof_clock()
.user_clock()
.add_time(jiffies_interval);
process.prof_clock().user_clock().add_time(jiffies_interval);
posix_thread.prof_clock().user_clock().add_jiffies(1);
process.prof_clock().user_clock().add_jiffies(1);
timer_manager
.virtual_timer()
.timer_manager()

View File

@ -63,7 +63,7 @@ pub fn sys_prctl(
PrctlCmd::PR_GET_NAME(write_to_addr) => {
let thread_name = ctx.posix_thread.thread_name().lock();
if let Some(thread_name) = &*thread_name {
if let Some(thread_name) = thread_name.name()? {
if let Some(thread_name) = thread_name.name() {
ctx.user_space().write_bytes(
write_to_addr,
&mut VmReader::from(thread_name.to_bytes_with_nul()),

View File

@ -3,13 +3,16 @@
use alloc::sync::Arc;
use core::time::Duration;
use ostd::sync::SpinLock;
use ostd::{
sync::{LocalIrqDisabled, SpinLock},
timer::Jiffies,
};
use crate::time::Clock;
/// A clock used to record the CPU time for processes and threads.
pub struct CpuClock {
time: SpinLock<Duration>,
time: SpinLock<Jiffies, LocalIrqDisabled>,
}
/// A profiling clock that contains a user CPU clock and a kernel CPU clock.
@ -25,19 +28,24 @@ 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),
time: SpinLock::new(Jiffies::new(0)),
})
}
/// Adds `interval` to the original recorded time to update the `CpuClock`.
pub fn add_time(&self, interval: Duration) {
*self.time.disable_irq().lock() += interval;
/// Adds `jiffies` to the original recorded time to update the `CpuClock`.
pub fn add_jiffies(&self, jiffies: u64) {
self.time.lock().add(jiffies);
}
/// Reads the current time of this clock in [`Jiffies`].
pub fn read_jiffies(&self) -> Jiffies {
*self.time.lock()
}
}
impl Clock for CpuClock {
fn read_time(&self) -> Duration {
*self.time.disable_irq().lock()
self.read_jiffies().as_duration()
}
}

View File

@ -813,6 +813,11 @@ impl<R> Vmar<R> {
pub fn get_rss_counter(&self, rss_type: RssType) -> usize {
self.0.get_rss_counter(rss_type)
}
/// Returns the total size of the mappings in bytes.
pub fn get_mappings_total_size(&self) -> usize {
self.0.inner.read().total_vm
}
}
/// Options for creating a new mapping. The mapping is not allowed to overlap

View File

@ -17,6 +17,9 @@ pub struct Jiffies(u64);
pub(crate) static ELAPSED: AtomicU64 = AtomicU64::new(0);
impl Jiffies {
/// The maximum value of [`Jiffies`].
pub const MAX: Self = Self(u64::MAX);
/// Creates a new instance.
pub fn new(value: u64) -> Self {
Self(value)
@ -32,9 +35,16 @@ impl Jiffies {
self.0
}
/// Adds the given number of jiffies, saturating at [`Jiffies::MAX`] on overflow.
pub fn add(&mut self, jiffies: u64) {
self.0 = self.0.saturating_add(jiffies);
}
/// Gets the [`Duration`] calculated from the jiffies counts.
pub fn as_duration(self) -> Duration {
Duration::from_millis(self.0 * 1000 / TIMER_FREQ)
let secs = self.0 / TIMER_FREQ;
let nanos = ((self.0 % TIMER_FREQ) * 1_000_000_000) / TIMER_FREQ;
Duration::new(secs, nanos as u32)
}
}

View File

@ -28,12 +28,14 @@ unlink tesk_cmd_hard_link
sed 3q shell_cmd.sh
find . -name "*shell_cmd*"
find . -name "*shell_cmd*"
mkdir foo
rmdir foo
rmdir foo
echo "Hello world from asterinas" > hello.txt
rm hello.txt
cd ..
ps -T | grep ps