Support stopping states in `proc/pid/stat`
This commit is contained in:
parent
7a7e62b318
commit
35ab40057a
|
|
@ -111,6 +111,8 @@ impl FileOps for StatFileOps {
|
|||
SleepingState::Running => 'R',
|
||||
SleepingState::Interruptible => 'S',
|
||||
SleepingState::Uninterruptible => 'D',
|
||||
SleepingState::StopBySignal => 'T',
|
||||
SleepingState::StopByPtrace => 't',
|
||||
}
|
||||
};
|
||||
let ppid = process.parent().pid();
|
||||
|
|
|
|||
|
|
@ -100,6 +100,8 @@ impl FileOps for StatusFileOps {
|
|||
SleepingState::Running => "R (running)",
|
||||
SleepingState::Interruptible => "S (sleeping)",
|
||||
SleepingState::Uninterruptible => "D (disk sleep)",
|
||||
SleepingState::StopBySignal => "T (stopped)",
|
||||
SleepingState::StopByPtrace => "t (tracing stop)",
|
||||
}
|
||||
};
|
||||
writeln!(status_output, "State:\t{}", state).unwrap();
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ use crate::{
|
|||
signal::{
|
||||
constants::{SIGCHLD, SIGKILL},
|
||||
signals::kernel::KernelSignal,
|
||||
HandlePendingSignal, SigStack,
|
||||
HandlePendingSignal, PauseReason, SigStack,
|
||||
},
|
||||
ContextUnshareAdminApi, Credentials, Process, ProgramToLoad,
|
||||
},
|
||||
|
|
@ -191,7 +191,8 @@ fn wait_other_threads_exit(ctx: &Context) -> Result<()> {
|
|||
// Wait until any signal comes or any other thread exits.
|
||||
let (waiter, waker) = Waiter::new_pair();
|
||||
|
||||
ctx.posix_thread.set_signalled_waker(waker.clone());
|
||||
ctx.posix_thread
|
||||
.set_signalled_waker(waker.clone(), PauseReason::Sleep);
|
||||
if ctx.has_pending_sigkill() {
|
||||
ctx.posix_thread.clear_signalled_waker();
|
||||
return_errno_with_message!(Errno::EAGAIN, "the current thread has received SIGKILL");
|
||||
|
|
|
|||
|
|
@ -16,7 +16,11 @@ use crate::{
|
|||
events::IoEvents,
|
||||
fs::{file_table::FileTable, thread_info::ThreadFsInfo},
|
||||
prelude::*,
|
||||
process::{namespace::nsproxy::NsProxy, signal::PollHandle, Pid},
|
||||
process::{
|
||||
namespace::nsproxy::NsProxy,
|
||||
signal::{PauseReason, PollHandle},
|
||||
Pid,
|
||||
},
|
||||
thread::{Thread, Tid},
|
||||
time::{clocks::ProfClock, Timer, TimerManager},
|
||||
};
|
||||
|
|
@ -64,8 +68,8 @@ pub struct PosixThread {
|
|||
/// Thread-directed sigqueue
|
||||
sig_queues: SigQueues,
|
||||
/// The per-thread signal [`Waker`], which will be used to wake up the thread
|
||||
/// when enqueuing a signal.
|
||||
signalled_waker: SpinLock<Option<Arc<Waker>>>,
|
||||
/// when enqueuing a signal, along with the reason why the thread is paused.
|
||||
signalled_waker: SpinLock<Option<(Arc<Waker>, PauseReason)>>,
|
||||
|
||||
/// A profiling clock measures the user CPU time and kernel CPU time in the thread.
|
||||
prof_clock: Arc<ProfClock>,
|
||||
|
|
@ -143,7 +147,8 @@ impl PosixThread {
|
|||
self.sig_mask.contains(signum, Ordering::Relaxed)
|
||||
}
|
||||
|
||||
/// Sets the input [`Waker`] as the signalled waker of this thread.
|
||||
/// Sets the input [`Waker`] as the signalled waker of this thread,
|
||||
/// along with the reason why the thread is paused.
|
||||
///
|
||||
/// This approach can collaborate with signal-aware wait methods.
|
||||
/// Once a signalled waker is set for a thread, it cannot be reset until it is cleared.
|
||||
|
|
@ -152,10 +157,10 @@ impl PosixThread {
|
|||
///
|
||||
/// If setting a new waker before clearing the current thread's signalled waker
|
||||
/// this method will panic.
|
||||
pub fn set_signalled_waker(&self, waker: Arc<Waker>) {
|
||||
pub fn set_signalled_waker(&self, waker: Arc<Waker>, reason: PauseReason) {
|
||||
let mut signalled_waker = self.signalled_waker.lock();
|
||||
assert!(signalled_waker.is_none());
|
||||
*signalled_waker = Some(waker);
|
||||
*signalled_waker = Some((waker, reason));
|
||||
}
|
||||
|
||||
/// Clears the signalled waker of this thread.
|
||||
|
|
@ -199,21 +204,34 @@ impl PosixThread {
|
|||
// - 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.
|
||||
//
|
||||
// Similarly, this implementation prevents a process that has been stopped by
|
||||
// a signal or ptrace from being incorrectly reported as sleeping in an
|
||||
// (un)interruptible wait.
|
||||
//
|
||||
// FIXME: This implementation cannot prevent a stopped process from being
|
||||
// reported as running when `crate::process::signal::handle_pending_signal`
|
||||
// is called, but the pending signal is not a `SIGCONT`. However, is this
|
||||
// actually a problem? We considered an approach to fix this issue, but it
|
||||
// does not fully resolve it and has some drawbacks. For more details, see
|
||||
// <https://github.com/asterinas/asterinas/pull/2491#issuecomment-3527958970>.
|
||||
let signalled_waker = self.signalled_waker.lock();
|
||||
let task = self.task.upgrade().unwrap();
|
||||
match (
|
||||
signalled_waker.is_some(),
|
||||
signalled_waker.as_ref(),
|
||||
task.schedule_info().cpu.get().is_none(),
|
||||
) {
|
||||
(true, true) => SleepingState::Interruptible,
|
||||
(false, true) => SleepingState::Uninterruptible,
|
||||
(Some((_, PauseReason::Sleep)), true) => SleepingState::Interruptible,
|
||||
(Some((_, PauseReason::StopBySignal)), true) => SleepingState::StopBySignal,
|
||||
(Some((_, PauseReason::StopByPtrace)), true) => SleepingState::StopByPtrace,
|
||||
(None, 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() {
|
||||
if let Some((waker, _)) = &*self.signalled_waker.lock() {
|
||||
waker.wake_up();
|
||||
}
|
||||
}
|
||||
|
|
@ -334,4 +352,8 @@ pub enum SleepingState {
|
|||
Interruptible,
|
||||
/// The thread is sleeping in an uninterruptible wait.
|
||||
Uninterruptible,
|
||||
/// The thread is stopped by a signal.
|
||||
StopBySignal,
|
||||
/// The thread is stopped by ptrace.
|
||||
StopByPtrace,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ use ostd::{
|
|||
arch::cpu::context::{FpuContext, UserContext},
|
||||
user::UserContextApi,
|
||||
};
|
||||
pub use pause::{with_sigmask_changed, Pause};
|
||||
pub use pause::{with_sigmask_changed, Pause, PauseReason};
|
||||
pub use pending::HandlePendingSignal;
|
||||
pub use poll::{PollAdaptor, PollHandle, Pollable, Pollee, Poller};
|
||||
use sig_action::{SigAction, SigActionFlags, SigDefaultAction};
|
||||
|
|
|
|||
|
|
@ -37,7 +37,26 @@ pub trait Pause: WaitTimeout {
|
|||
where
|
||||
F: FnMut() -> Option<R>,
|
||||
{
|
||||
self.pause_until_or_timeout_impl(cond, None)
|
||||
self.pause_until_or_timeout_impl(cond, None, PauseReason::Sleep)
|
||||
}
|
||||
|
||||
/// Pauses until the condition is met or a signal interrupts.
|
||||
///
|
||||
/// This pause happens due to the reason specified. If the reason is `PauseReason::Sleep`,
|
||||
/// the caller should use `pause_until` straightforwardly.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// This method will return an error with [`EINTR`] if a signal is received before the
|
||||
/// condition is met.
|
||||
///
|
||||
/// [`EINTR`]: crate::error::Errno::EINTR
|
||||
#[track_caller]
|
||||
fn pause_until_by<F, R>(&self, cond: F, reason: PauseReason) -> Result<R>
|
||||
where
|
||||
F: FnMut() -> Option<R>,
|
||||
{
|
||||
self.pause_until_or_timeout_impl(cond, None, reason)
|
||||
}
|
||||
|
||||
/// Pauses until the condition is met, the timeout is reached, or a signal interrupts.
|
||||
|
|
@ -62,7 +81,7 @@ pub trait Pause: WaitTimeout {
|
|||
Err(err) => return cond().ok_or(err),
|
||||
};
|
||||
|
||||
self.pause_until_or_timeout_impl(cond, timeout_inner)
|
||||
self.pause_until_or_timeout_impl(cond, timeout_inner, PauseReason::Sleep)
|
||||
}
|
||||
|
||||
/// Pauses until the condition is met, the timeout is reached, or a signal interrupts.
|
||||
|
|
@ -81,6 +100,7 @@ pub trait Pause: WaitTimeout {
|
|||
&self,
|
||||
cond: F,
|
||||
timeout: Option<&ManagedTimeout>,
|
||||
reason: PauseReason,
|
||||
) -> Result<R>
|
||||
where
|
||||
F: FnMut() -> Option<R>;
|
||||
|
|
@ -104,6 +124,7 @@ impl Pause for Waiter {
|
|||
&self,
|
||||
cond: F,
|
||||
timeout: Option<&ManagedTimeout>,
|
||||
reason: PauseReason,
|
||||
) -> Result<R>
|
||||
where
|
||||
F: FnMut() -> Option<R>,
|
||||
|
|
@ -129,7 +150,7 @@ impl Pause for Waiter {
|
|||
Ok(())
|
||||
};
|
||||
|
||||
posix_thread.set_signalled_waker(self.waker());
|
||||
posix_thread.set_signalled_waker(self.waker(), reason);
|
||||
let res = self.wait_until_or_timeout_cancelled(cond, cancel_cond, timeout);
|
||||
posix_thread.clear_signalled_waker();
|
||||
|
||||
|
|
@ -150,7 +171,7 @@ impl Pause for Waiter {
|
|||
.and_then(|thread| thread.as_posix_thread());
|
||||
|
||||
if let Some(posix_thread) = posix_thread_opt {
|
||||
posix_thread.set_signalled_waker(self.waker());
|
||||
posix_thread.set_signalled_waker(self.waker(), PauseReason::Sleep);
|
||||
// Check `has_pending` after `set_signalled_waker` to avoid race conditions.
|
||||
if posix_thread.has_pending() {
|
||||
posix_thread.clear_signalled_waker();
|
||||
|
|
@ -194,6 +215,7 @@ impl Pause for WaitQueue {
|
|||
&self,
|
||||
mut cond: F,
|
||||
timeout: Option<&ManagedTimeout>,
|
||||
reason: PauseReason,
|
||||
) -> Result<R>
|
||||
where
|
||||
F: FnMut() -> Option<R>,
|
||||
|
|
@ -208,7 +230,7 @@ impl Pause for WaitQueue {
|
|||
self.enqueue(waiter.waker());
|
||||
cond()
|
||||
};
|
||||
waiter.pause_until_or_timeout_impl(cond, timeout)
|
||||
waiter.pause_until_or_timeout_impl(cond, timeout, reason)
|
||||
}
|
||||
|
||||
fn pause_timeout(&self, _timeout: &TimeoutExt<'_>) -> Result<()> {
|
||||
|
|
@ -216,6 +238,14 @@ impl Pause for WaitQueue {
|
|||
}
|
||||
}
|
||||
|
||||
/// The reason why a process is paused by a `pause`-family method.
|
||||
pub enum PauseReason {
|
||||
Sleep,
|
||||
StopBySignal,
|
||||
#[expect(dead_code)]
|
||||
StopByPtrace,
|
||||
}
|
||||
|
||||
/// Executes a closure after temporarily adjusting the signal mask of the current POSIX thread.
|
||||
pub fn with_sigmask_changed<R>(
|
||||
ctx: &Context,
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ use crate::{
|
|||
prelude::*,
|
||||
process::{
|
||||
posix_thread::{AsPosixThread, AsThreadLocal, ThreadLocal},
|
||||
signal::{handle_pending_signal, HandlePendingSignal},
|
||||
signal::{handle_pending_signal, HandlePendingSignal, PauseReason},
|
||||
},
|
||||
syscall::handle_syscall,
|
||||
thread::{exception::handle_exception, AsThread},
|
||||
|
|
@ -112,7 +112,11 @@ pub fn create_new_user_task(
|
|||
// We need to further investigate Linux behavior regarding which signals should be handled
|
||||
// when the thread is stopped.
|
||||
while !current_thread.is_exited() && ctx.process.is_stopped() {
|
||||
let _ = stop_waiter.pause_until(|| (!ctx.process.is_stopped()).then_some(()));
|
||||
let _ = stop_waiter.pause_until_by(
|
||||
|| (!ctx.process.is_stopped()).then_some(()),
|
||||
// We currently do not support ptrace.
|
||||
PauseReason::StopBySignal,
|
||||
);
|
||||
handle_pending_signal(user_ctx, &ctx, None);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue