Support sleeping states in `proc/pid/stat`

This commit is contained in:
Wang Siyuan 2025-11-12 12:07:57 +00:00 committed by Ruihan Li
parent 3ffa253947
commit 7a7e62b318
4 changed files with 75 additions and 5 deletions

View File

@ -9,7 +9,7 @@ use crate::{
utils::{mkmod, Inode},
},
prelude::*,
process::posix_thread::AsPosixThread,
process::posix_thread::{AsPosixThread, SleepingState},
vm::vmar::RssType,
};
@ -104,7 +104,15 @@ impl FileOps for StatFileOps {
.name()
.to_string_lossy()
.into_owned();
let state = if thread.is_exited() { 'Z' } else { 'R' };
let state = if thread.is_exited() {
'Z'
} else {
match posix_thread.sleeping_state() {
SleepingState::Running => 'R',
SleepingState::Interruptible => 'S',
SleepingState::Uninterruptible => 'D',
}
};
let ppid = process.parent().pid();
let pgrp = process.pgid();
let session = process.sid();

View File

@ -9,7 +9,7 @@ use crate::{
utils::{mkmod, Inode},
},
prelude::*,
process::posix_thread::AsPosixThread,
process::posix_thread::{AsPosixThread, SleepingState},
vm::vmar::RssType,
};
@ -96,7 +96,11 @@ impl FileOps for StatusFileOps {
let state = if thread.is_exited() {
"Z (zombie)"
} else {
"R (running)"
match posix_thread.sleeping_state() {
SleepingState::Running => "R (running)",
SleepingState::Interruptible => "S (sleeping)",
SleepingState::Uninterruptible => "D (disk sleep)",
}
};
writeln!(status_output, "State:\t{}", state).unwrap();

View File

@ -163,6 +163,54 @@ impl PosixThread {
*self.signalled_waker.lock() = None;
}
/// Returns the sleeping state of this thread.
pub fn sleeping_state(&self) -> SleepingState {
// This implementation prevents a thread (let's call it `threadA`) that is
// sleeping in an interruptible wait from being mistakenly reported as
// sleeping in an uninterruptible wait due to a race condition, where another
// thread (`threadB`) may observe that its `task.schedule_info().cpu` is
// `AtomicCpuId::NONE` and its `signalled_waker` is `None` (not set yet or
// already cleared).
//
// When `threadA` enters an interruptible wait, it executes the following steps:
// ```
// A1: Acquire signalled_waker.lock |
// A2: set signalled_waker to Some |-- critical section #1
// A3: Release signalled_waker.lock |
// A4: cpu.set_to_none(Relaxed)
// A5: cpu.set_if_is_none(cpuid, Relaxed)
// A6: Acquire signalled_waker.lock |
// A7: set signalled_waker to None |-- critical section #2
// A8: Release signalled_waker.lock |
// ```
//
// When `threadB` calls `threadA.sleeping_state()`, it executes the following steps:
// ```
// B1: Acquire threadA.signalled_waker.lock |
// B2: check threadA.signalled_waker |-- critical section #3
// B3: check threadA.cpu.get(Relaxed) |
// B4: Release threadA.signalled_waker.lock |
// ```
//
// We can see that:
// - If #3 happens before #1, B3 can not observe the effect of A4 due to the
// release-acquire pair B4-A1.
// - If #3 happens between #1 and #2, B2 will always see a `Some`.
// - If #3 happens after #2, B3 can observe the effect of A5 due to the
// release-acquire pair A8-B1.
// Therefore, the condition where both B2 and B3 see `None` will never happen.
let signalled_waker = self.signalled_waker.lock();
let task = self.task.upgrade().unwrap();
match (
signalled_waker.is_some(),
task.schedule_info().cpu.get().is_none(),
) {
(true, true) => SleepingState::Interruptible,
(false, true) => SleepingState::Uninterruptible,
(_, false) => SleepingState::Running,
}
}
/// Wakes up the signalled waker.
pub fn wake_signalled_waker(&self) {
if let Some(waker) = &*self.signalled_waker.lock() {
@ -276,3 +324,14 @@ pub fn last_tid() -> Tid {
// FIXME: The current value is chosen arbitrarily.
// This value can be modified by the user by writing to `/proc/sys/kernel/pid_max`.
pub const PID_MAX: u32 = u32::MAX / 2;
/// The sleeping state of a thread.
#[derive(Debug, Clone, Copy)]
pub enum SleepingState {
/// The thread is running.
Running,
/// The thread is sleeping in an interruptible wait.
Interruptible,
/// The thread is sleeping in an uninterruptible wait.
Uninterruptible,
}

View File

@ -13,7 +13,6 @@ ProcCpuinfo.RequiredFieldsArePresent
ProcCpuinfo.DeniesWriteNonRoot
ProcSelfStat.PopulateWriteRSS
ProcPidStatusTest.HasBasicFields
ProcPidStatusTest.StateSleeping_NoRandomSave
ProcPidStatTest.VmStats
ProcPidEnviron.MatchesEnviron
ProcPidSymlink.SubprocessRunning