修复 Linux CI 进程组僵尸成员导致 shutdown 超时
Project CI / AI game creator shell Rust crates (pull_request) Successful in 6m41s
Project CI / AI game creator shell Rust lane 2/2 (pull_request) Successful in 8m12s
Project CI / AI game creator shell Rust lane 1/2 (pull_request) Successful in 8m25s
Project CI / Backend tests (pull_request) Successful in 9m8s
Project CI / Frontend tests (pull_request) Successful in 3m41s
Project CI / AI game creator shell web tests (pull_request) Successful in 3m44s
Project CI / Repository checks (pull_request) Successful in 5m51s
Project CI / Native shell tests (pull_request) Successful in 7m38s
Project CI / AI game creator shell Rust crates (pull_request) Successful in 6m41s
Project CI / AI game creator shell Rust lane 2/2 (pull_request) Successful in 8m12s
Project CI / AI game creator shell Rust lane 1/2 (pull_request) Successful in 8m25s
Project CI / Backend tests (pull_request) Successful in 9m8s
Project CI / Frontend tests (pull_request) Successful in 3m41s
Project CI / AI game creator shell web tests (pull_request) Successful in 3m44s
Project CI / Repository checks (pull_request) Successful in 5m51s
Project CI / Native shell tests (pull_request) Successful in 7m38s
调整 process_tree.rs 的 Unix 进程组空判:Linux 下扫描 /proc 并将僵尸成员视为已退出 避免容器 PID 1 不回收被 reparent 的僵尸孙进程时 kill(-pgid, 0) 误判组仍存活
This commit is contained in:
@@ -135,6 +135,10 @@ impl OwnedProcessTree {
|
||||
.ok()
|
||||
.filter(|pid| *pid > 0)
|
||||
.ok_or("app-server-process-owner-missing")?;
|
||||
#[cfg(target_os = "linux")]
|
||||
if let Some(has_live_member) = linux_process_group_has_live_member(pid) {
|
||||
return Ok(!has_live_member);
|
||||
}
|
||||
if unsafe { libc::kill(-pid, 0) } == 0 {
|
||||
return Ok(false);
|
||||
}
|
||||
@@ -183,6 +187,34 @@ impl OwnedProcessTree {
|
||||
}
|
||||
}
|
||||
|
||||
/// 容器 PID 1 不回收被 reparent 的僵尸孙进程时,kill(-pgid, 0) 仍认为组存活;
|
||||
/// 僵尸对退役语义等价于已退出,扫描 /proc 时只把非僵尸成员视为存活。
|
||||
#[cfg(all(unix, target_os = "linux"))]
|
||||
fn linux_process_group_has_live_member(pgid: i32) -> Option<bool> {
|
||||
for entry in std::fs::read_dir("/proc").ok()? {
|
||||
let entry = entry.ok()?;
|
||||
let Ok(stat) = std::fs::read_to_string(entry.path().join("stat")) else {
|
||||
continue;
|
||||
};
|
||||
let Some(close) = stat.rfind(')') else {
|
||||
continue;
|
||||
};
|
||||
let mut fields = stat[close + 1..].split_whitespace();
|
||||
let (Some(state), Some(_ppid), Some(member_pgid)) =
|
||||
(fields.next(), fields.next(), fields.next())
|
||||
else {
|
||||
continue;
|
||||
};
|
||||
if member_pgid.parse::<i32>().ok() != Some(pgid) {
|
||||
continue;
|
||||
}
|
||||
if state != "Z" {
|
||||
return Some(true);
|
||||
}
|
||||
}
|
||||
Some(false)
|
||||
}
|
||||
|
||||
impl Drop for OwnedProcessTree {
|
||||
fn drop(&mut self) {
|
||||
// Drop 只做应急回收,永远不伪造完成证明;正式终态必须 await shutdown。
|
||||
|
||||
Reference in New Issue
Block a user