Add fields in `/proc/*/stat` and `/proc/*/status`
This commit is contained in:
parent
513532fe95
commit
a13297ae4c
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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())
|
||||
|
|
|
|||
|
|
@ -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())
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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()),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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())
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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()),
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
Loading…
Reference in New Issue