//! Functions and structs related to process information //! //! The primary source of data for functions in this module is the files in a `/proc/<pid>/` //! directory.
usesuper::*; usecrate::from_iter;
#[cfg(feature = "serde1")] use serde::{Deserialize, Serialize}; use std::io::Read; use std::path::PathBuf; use std::str::FromStr;
mod limit; pubuse limit::*;
mod stat; pubuse stat::*;
mod mount; pubuse mount::*;
mod namespaces; pubuse namespaces::*;
mod status; pubuse status::*;
mod schedstat; pubuse schedstat::*;
mod smaps_rollup; pubuse smaps_rollup::*;
mod pagemap; pubuse pagemap::*;
mod clear_refs; pubuse clear_refs::*;
bitflags! { /// Kernel flags for a process /// /// See also the [Stat::flags()] method. #[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))] #[derive(Copy, Clone, Debug, Hash, Eq, PartialEq, PartialOrd, Ord)] pubstruct StatFlags: u32 { /// I am an IDLE thread const PF_IDLE = 0x0000_0002; /// Getting shut down const PF_EXITING = 0x0000_0004; /// PI exit done on shut down const PF_EXITPIDONE = 0x0000_0008; /// I'm a virtual CPU const PF_VCPU = 0x0000_0010; /// I'm a workqueue worker const PF_WQ_WORKER = 0x0000_0020; /// Forked but didn't exec const PF_FORKNOEXEC = 0x0000_0040; /// Process policy on mce errors; const PF_MCE_PROCESS = 0x0000_0080; /// Used super-user privileges const PF_SUPERPRIV = 0x0000_0100; /// Dumped core const PF_DUMPCORE = 0x0000_0200; /// Killed by a signal const PF_SIGNALED = 0x0000_0400; ///Allocating memory const PF_MEMALLOC = 0x0000_0800; /// set_user() noticed that RLIMIT_NPROC was exceeded const PF_NPROC_EXCEEDED = 0x0000_1000; /// If unset the fpu must be initialized before use const PF_USED_MATH = 0x0000_2000; /// Used async_schedule*(), used by module init const PF_USED_ASYNC = 0x0000_4000; /// This thread should not be frozen const PF_NOFREEZE = 0x0000_8000; /// Frozen for system suspend const PF_FROZEN = 0x0001_0000; /// I am kswapd const PF_KSWAPD = 0x0002_0000; /// All allocation requests will inherit GFP_NOFS const PF_MEMALLOC_NOFS = 0x0004_0000; /// All allocation requests will inherit GFP_NOIO const PF_MEMALLOC_NOIO = 0x0008_0000; /// Throttle me less: I clean memory const PF_LESS_THROTTLE = 0x0010_0000; /// I am a kernel thread const PF_KTHREAD = 0x0020_0000; /// Randomize virtual address space const PF_RANDOMIZE = 0x0040_0000; /// Allowed to write to swap const PF_SWAPWRITE = 0x0080_0000; /// Stalled due to lack of memory const PF_MEMSTALL = 0x0100_0000; /// I'm an Usermodehelper process const PF_UMH = 0x0200_0000; /// Userland is not allowed to meddle with cpus_allowed const PF_NO_SETAFFINITY = 0x0400_0000; /// Early kill for mce process policy const PF_MCE_EARLY = 0x0800_0000; /// All allocation request will have _GFP_MOVABLE cleared const PF_MEMALLOC_NOCMA = 0x1000_0000; /// Thread belongs to the rt mutex tester const PF_MUTEX_TESTER = 0x2000_0000; /// Freezer should not count it as freezable const PF_FREEZER_SKIP = 0x4000_0000; /// This thread called freeze_processes() and should not be frozen const PF_SUSPEND_TASK = 0x8000_0000;
bitflags! { /// The permissions a process has on memory map entries. /// /// Note that the `SHARED` and `PRIVATE` are mutually exclusive, so while you can /// use `MMPermissions::all()` to construct an instance that has all bits set, /// this particular value would never been seen in procfs. #[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))] #[derive(Copy, Clone, Debug, Hash, Eq, PartialEq, PartialOrd, Ord, Default)] pubstruct MMPermissions: u8 { /// No permissions const NONE = 0; /// Read permission const READ = 1 << 0; /// Write permission const WRITE = 1 << 1; /// Execute permission const EXECUTE = 1 << 2; /// Memory is shared with another process. /// /// Mutually exclusive with PRIVATE. const SHARED = 1 << 3; /// Memory is private (and copy-on-write) /// /// Mutually exclusive with SHARED. const PRIVATE = 1 << 4;
}
}
impl MMPermissions { fn from_ascii_char(b: u8) -> Self { match b {
b'r' => Self::READ,
b'w' => Self::WRITE,
b'x' => Self::EXECUTE,
b's' => Self::SHARED,
b'p' => Self::PRIVATE,
_ => Self::NONE,
}
} /// Returns this permission map as a 4-character string, similar to what you /// might see in `/proc/\<pid\>/maps`. /// /// Note that the SHARED and PRIVATE bits are mutually exclusive, so this /// string is 4 characters long, not 5. pubfn as_str(&self) -> String { letmut s = String::with_capacity(4);
s.push(ifself.contains(Self::READ) { 'r' } else { '-' });
s.push(ifself.contains(Self::WRITE) { 'w' } else { '-' });
s.push(ifself.contains(Self::EXECUTE) { 'x' } else { '-' });
s.push(ifself.contains(Self::SHARED) { 's'
} elseifself.contains(Self::PRIVATE) { 'p'
} else { '-'
});
s
}
}
impl FromStr for MMPermissions { type Err = std::convert::Infallible;
fn from_str(s: &str) -> Result<Self, Self::Err> { // Only operate on ASCII (byte) values
Ok(s.bytes()
.map(Self::from_ascii_char)
.fold(Self::default(), std::ops::BitOr::bitor))
}
}
bitflags! { /// Represents the kernel flags associated with the virtual memory area. /// The names of these flags are just those you'll find in the man page, but in upper case. #[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))] #[derive(Copy, Clone, Debug, Hash, Eq, PartialEq, PartialOrd, Ord, Default)] pubstruct VmFlags: u32 { /// No flags const NONE = 0; /// Readable const RD = 1 << 0; /// Writable const WR = 1 << 1; /// Executable const EX = 1 << 2; /// Shared const SH = 1 << 3; /// May read const MR = 1 << 4; /// May write const MW = 1 << 5; /// May execute const ME = 1 << 6; /// May share const MS = 1 << 7; /// Stack segment grows down const GD = 1 << 8; /// Pure PFN range const PF = 1 << 9; /// Disable write to the mapped file const DW = 1 << 10; /// Pages are locked in memory const LO = 1 << 11; /// Memory mapped I/O area const IO = 1 << 12; /// Sequential read advise provided const SR = 1 << 13; /// Random read provided const RR = 1 << 14; /// Do not copy area on fork const DC = 1 << 15; /// Do not expand area on remapping const DE = 1 << 16; /// Area is accountable const AC = 1 << 17; /// Swap space is not reserved for the area const NR = 1 << 18; /// Area uses huge TLB pages const HT = 1 << 19; /// Perform synchronous page faults (since Linux 4.15) const SF = 1 << 20; /// Non-linear mapping (removed in Linux 4.0) const NL = 1 << 21; /// Architecture specific flag const AR = 1 << 22; /// Wipe on fork (since Linux 4.14) const WF = 1 << 23; /// Do not include area into core dump const DD = 1 << 24; /// Soft-dirty flag (since Linux 3.13) const SD = 1 << 25; /// Mixed map area const MM = 1 << 26; /// Huge page advise flag const HG = 1 << 27; /// No-huge page advise flag const NH = 1 << 28; /// Mergeable advise flag const MG = 1 << 29; /// Userfaultfd missing pages tracking (since Linux 4.3) const UM = 1 << 30; /// Userfaultfd wprotect pages tracking (since Linux 4.3) const UW = 1 << 31;
}
}
impl FromStr for ProcState { type Err = ProcError; fn from_str(s: &str) -> Result<ProcState, ProcError> {
ProcState::from_char(expect!(s.chars().next(), "empty string"))
.ok_or_else(|| build_internal_error!("failed to convert"))
}
}
/// This struct contains I/O statistics for the process, built from `/proc/<pid>/io` /// /// # Note /// /// In the current implementation, things are a bit racy on 32-bit systems: if process A /// reads process B's `/proc/<pid>/io` while process B is updating one of these 64-bit /// counters, process A could see an intermediate result. #[derive(Debug, Copy, Clone)] #[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))] pubstruct Io { /// Characters read /// /// The number of bytes which this task has caused to be read from storage. This is simply the /// sum of bytes which this process passed to read(2) and similar system calls. It includes /// things such as terminal I/O and is unaffected by whether or not actual physical disk I/O /// was required (the read might have been satisfied from pagecache). pub rchar: u64,
/// characters written /// /// The number of bytes which this task has caused, or shall cause to be written to disk. /// Similar caveats apply here as with rchar. pub wchar: u64, /// read syscalls /// /// Attempt to count the number of write I/O operations—that is, system calls such as write(2) /// and pwrite(2). pub syscr: u64, /// write syscalls /// /// Attempt to count the number of write I/O operations—that is, system calls such as write(2) /// and pwrite(2). pub syscw: u64, /// bytes read /// /// Attempt to count the number of bytes which this process really did cause to be fetched from /// the storage layer. This is accurate for block-backed filesystems. pub read_bytes: u64, /// bytes written /// /// Attempt to count the number of bytes which this process caused to be sent to the storage layer. pub write_bytes: u64, /// Cancelled write bytes. /// /// The big inaccuracy here is truncate. If a process writes 1MB to a file and then deletes /// the file, it will in fact perform no write‐ out. But it will have been accounted as having /// caused 1MB of write. In other words: this field represents the number of bytes which this /// process caused to not happen, by truncating pagecache. A task can cause "negative" I/O too. /// If this task truncates some dirty pagecache, some I/O which another task has been accounted /// for (in its write_bytes) will not be happening. pub cancelled_write_bytes: u64,
}
#[derive(Debug, PartialEq, Eq, Clone, Hash)] #[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))] pubenum MMapPath { /// The file that is backing the mapping.
Path(PathBuf), /// The process's heap.
Heap, /// The initial process's (also known as the main thread's) stack.
Stack, /// A thread's stack (where the `<tid>` is a thread ID). It corresponds to the /// `/proc/<pid>/task/<tid>/` path. /// /// (since Linux 3.4)
TStack(u32), /// The virtual dynamically linked shared object.
Vdso, /// Shared kernel variables
Vvar, /// obsolete virtual syscalls, succeeded by vdso
Vsyscall, /// rollup memory mappings, from `/proc/<pid>/smaps_rollup`
Rollup, /// An anonymous mapping as obtained via mmap(2).
Anonymous, /// Shared memory segment. The i32 value corresponds to [Shm.key](Shm::key), while [MemoryMap.inode](MemoryMap::inode) corresponds to [Shm.shmid](Shm::shmid)
Vsys(i32), /// Some other pseudo-path
Other(String),
}
impl MMapPath { pubfn from(path: &str) -> ProcResult<MMapPath> {
Ok(match path.trim() { "" => MMapPath::Anonymous, "[heap]" => MMapPath::Heap, "[stack]" => MMapPath::Stack, "[vdso]" => MMapPath::Vdso, "[vvar]" => MMapPath::Vvar, "[vsyscall]" => MMapPath::Vsyscall, "[rollup]" => MMapPath::Rollup,
x if x.starts_with("[stack:") => { letmut s = x[1..x.len() - 1].split(':'); let tid = from_str!(u32, expect!(s.nth(1)));
MMapPath::TStack(tid)
}
x if x.starts_with('[') && x.ends_with(']') => MMapPath::Other(x[1..x.len() - 1].to_string()),
x if x.starts_with("/SYSV") => MMapPath::Vsys(u32::from_str_radix(&x[5..13], 16)? as i32), // 32bits signed hex. /SYSVaabbccdd (deleted)
x => MMapPath::Path(PathBuf::from(x)),
})
}
}
/// Represents all entries in a `/proc/<pid>/maps` or `/proc/<pid>/smaps` file. #[derive(Debug, PartialEq, Eq, Clone)] #[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))] #[non_exhaustive] pubstruct MemoryMaps(pub Vec<MemoryMap>);
implcrate::FromBufRead for MemoryMaps { /// The data should be formatted according to procfs /proc/pid/{maps,smaps,smaps_rollup}. fn from_buf_read<R: BufRead>(reader: R) -> ProcResult<Self> { letmut memory_maps = Vec::new();
letmut line_iter = reader.lines().map(|r| r.map_err(|_| ProcError::Incomplete(None))); letmut current_memory_map: Option<MemoryMap> = None; whilelet Some(line) = line_iter.next().transpose()? { // Assumes all extension fields (in `/proc/<pid>/smaps`) start with a capital letter, // which seems to be the case. if line.starts_with(|c: char| c.is_ascii_uppercase()) { match current_memory_map.as_mut() {
None => return Err(ProcError::Incomplete(None)),
Some(mm) => { // This is probably an attribute if line.starts_with("VmFlags") { let flags = line.split_ascii_whitespace(); let flags = flags.skip(1); // Skips the `VmFlags:` part since we don't need it.
let flags = flags
.map(VmFlags::from_str) // FUTURE: use `Iterator::reduce`
.fold(VmFlags::NONE, std::ops::BitOr::bitor);
mm.extension.vm_flags = flags;
} else { letmut parts = line.split_ascii_whitespace();
let key = parts.next(); let value = parts.next();
iflet (Some(k), Some(v)) = (key, value) { // While most entries do have one, not all of them do. let size_suffix = parts.next();
// Limited poking at /proc/<pid>/smaps and then checking if "MB", "GB", and "TB" appear in the C file that is // supposedly responsible for creating smaps, has lead me to believe that the only size suffixes we'll ever encounter // "kB", which is most likely kibibytes. Actually checking if the size suffix is any of the above is a way to // future-proof the code, but I am not sure it is worth doing so. let size_multiplier = if size_suffix.is_some() { 1024 } else { 1 };
let v = v.parse::<u64>().map_err(|_| {
ProcError::Other("Value in `Key: Value` pair was not actually a number".into())
})?;
// This ignores the case when our Key: Value pairs are really Key Value pairs. Is this a good idea? let k = k.trim_end_matches(':');
/// Represents an entry in a `/proc/<pid>/maps` or `/proc/<pid>/smaps` file. #[derive(Debug, PartialEq, Eq, Clone)] #[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))] pubstruct MemoryMap { /// The address space in the process that the mapping occupies. pub address: (u64, u64), pub perms: MMPermissions, /// The offset into the file/whatever pub offset: u64, /// The device (major, minor) pub dev: (i32, i32), /// The inode on that device /// /// 0 indicates that no inode is associated with the memory region, as would be the case with /// BSS (uninitialized data). pub inode: u64, pub pathname: MMapPath, /// Memory mapping extension information, populated when parsing `/proc/<pid>/smaps`. /// /// The members will be `Default::default()` (empty/none) when the information isn't available. pub extension: MMapExtension,
}
impl MemoryMap { fn from_line(line: &str) -> ProcResult<MemoryMap> { letmut s = line.splitn(6, ' '); let address = expect!(s.next()); let perms = expect!(s.next()); let offset = expect!(s.next()); let dev = expect!(s.next()); let inode = expect!(s.next()); let path = expect!(s.next());
/// Represents the information about a specific mapping as presented in /proc/\<pid\>/smaps #[derive(Default, Debug, PartialEq, Eq, Clone)] #[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))] pubstruct MMapExtension { /// Key-value pairs that may represent statistics about memory usage, or other interesting things, /// such a "ProtectionKey" (if you're on X86 and that kernel config option was specified). /// /// Note that should a key-value pair represent a memory usage statistic, it will be in bytes. /// /// Check your manpage for more information pub map: HashMap<String, u64>, /// Kernel flags associated with the virtual memory area /// /// (since Linux 3.8) pub vm_flags: VmFlags,
}
impl MMapExtension { /// Return whether the extension information is empty. pubfn is_empty(&self) -> bool { self.map.is_empty() && self.vm_flags == VmFlags::NONE
}
}
for line in reader.lines() { let line = line?; if line.is_empty() || !line.contains(' ') { continue;
} letmut s = line.split_whitespace(); let field = expect!(s.next()); let value = expect!(s.next());
assert!(!cfg!(test) || map.is_empty(), "io map is not empty: {:#?}", map);
Ok(io)
}
}
/// Describes a file descriptor opened by a process. #[derive(Clone, Debug)] #[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))] pubenum FDTarget { /// A file or device
Path(PathBuf), /// A socket type, with an inode
Socket(u64),
Net(u64),
Pipe(u64), /// A file descriptor that have no corresponding inode.
AnonInode(String), /// A memfd file descriptor with a name.
MemFD(String), /// Some other file descriptor type, with an inode.
Other(String, u64),
}
impl FromStr for FDTarget { type Err = ProcError; fn from_str(s: &str) -> Result<FDTarget, ProcError> { // helper function that removes the first and last character fn strip_first_last(s: &str) -> ProcResult<&str> { if s.len() > 2 { letmut c = s.chars(); // remove the first and last characters let _ = c.next(); let _ = c.next_back();
Ok(c.as_str())
} else {
Err(ProcError::Incomplete(None))
}
}
if !s.starts_with('/') && s.contains(':') { letmut s = s.split(':'); let fd_type = expect!(s.next()); match fd_type { "socket" => { let inode = expect!(s.next(), "socket inode"); let inode = expect!(u64::from_str_radix(strip_first_last(inode)?, 10));
Ok(FDTarget::Socket(inode))
} "net" => { let inode = expect!(s.next(), "net inode"); let inode = expect!(u64::from_str_radix(strip_first_last(inode)?, 10));
Ok(FDTarget::Net(inode))
} "pipe" => { let inode = expect!(s.next(), "pipe inode"); let inode = expect!(u64::from_str_radix(strip_first_last(inode)?, 10));
Ok(FDTarget::Pipe(inode))
} "anon_inode" => Ok(FDTarget::AnonInode(expect!(s.next(), "anon inode").to_string())), "" => Err(ProcError::Incomplete(None)),
x => { let inode = expect!(s.next(), "other inode"); let inode = expect!(u64::from_str_radix(strip_first_last(inode)?, 10));
Ok(FDTarget::Other(x.to_string(), inode))
}
}
} elseiflet Some(s) = s.strip_prefix("/memfd:") {
Ok(FDTarget::MemFD(s.to_string()))
} else {
Ok(FDTarget::Path(PathBuf::from(s)))
}
}
}
/// Provides information about memory usage, measured in pages. #[derive(Debug, Clone, Copy)] #[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))] pubstruct StatM { /// Total program size, measured in pages /// /// (same as VmSize in /proc/\<pid\>/status) pub size: u64, /// Resident set size, measured in pages /// /// (same as VmRSS in /proc/\<pid\>/status) pub resident: u64, /// number of resident shared pages (i.e., backed by a file) /// /// (same as RssFile+RssShmem in /proc/\<pid\>/status) pub shared: u64, /// Text (code) pub text: u64, /// library (unused since Linux 2.6; always 0) pub lib: u64, /// data + stack pub data: u64, /// dirty pages (unused since Linux 2.6; always 0) pub dt: u64,
}
implcrate::FromRead for StatM { fn from_read<R: Read>(mut r: R) -> ProcResult<Self> { letmut line = String::new();
r.read_to_string(&mut line)?; letmut s = line.split_whitespace();
let size = expect!(from_iter(&mut s)); let resident = expect!(from_iter(&mut s)); let shared = expect!(from_iter(&mut s)); let text = expect!(from_iter(&mut s)); let lib = expect!(from_iter(&mut s)); let data = expect!(from_iter(&mut s)); let dt = expect!(from_iter(&mut s));
¤ Diese beiden folgenden Angebotsgruppen bietet das Unternehmen0.17Angebot
(Wie Sie bei der Firma Beratungs- und Dienstleistungen beauftragen können 2026-06-18)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.