Skip to content

Commit 6ec1dd5

Browse files
authored
feat(procfs): 新增/proc/<pid>/stat和/proc/<pid>/task支持 (#1490)
- 添加/proc/<pid>/stat文件生成,支持BusyBox ps/pstree/top等工具 - 实现/proc/<pid>/task目录结构,支持主线程tid=pid的展示 - 新增proc_pid_stat和proc_pid_task模块处理相关逻辑 - 扩展ProcFileType枚举,添加ProcPidStat、ProcPidTaskDir等类型 - 在InodeInfo中新增tid字段以支持线程信息 Signed-off-by: longjin <[email protected]>
1 parent 839a0d5 commit 6ec1dd5

File tree

3 files changed

+352
-0
lines changed

3 files changed

+352
-0
lines changed

kernel/src/filesystem/procfs/mod.rs

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,8 @@ mod proc_cpuinfo;
5353
mod proc_maps;
5454
mod proc_mounts;
5555
mod proc_pid_cmdline;
56+
mod proc_pid_stat;
57+
mod proc_pid_task;
5658
mod proc_thread_self_ns;
5759
mod proc_version;
5860
mod procfs_setup;
@@ -92,6 +94,14 @@ pub enum ProcFileType {
9294
ProcCmdline,
9395
/// /proc/<pid>/cmdline(也覆盖 /proc/self/cmdline)
9496
ProcPidCmdline,
97+
/// /proc/<pid>/stat
98+
ProcPidStat,
99+
/// /proc/<pid>/task 目录
100+
ProcPidTaskDir,
101+
/// /proc/<pid>/task/<tid> 目录
102+
ProcPidTaskTidDir,
103+
/// /proc/<pid>/task/<tid>/stat 文件
104+
ProcPidTaskTidStat,
95105
/// /proc/thread-self/ns itself
96106
ProcThreadSelfNsRoot,
97107
/// /proc/thread-self/ns/* namespace files
@@ -190,6 +200,8 @@ impl<'a> ProcFileCreationParamsBuilder<'a> {
190200
pub struct InodeInfo {
191201
///进程的pid
192202
pid: Option<RawPid>,
203+
/// 线程的 tid(用于 /proc/<pid>/task/<tid>)
204+
tid: Option<RawPid>,
193205
///文件类型
194206
ftype: ProcFileType,
195207
/// 文件描述符
@@ -203,6 +215,7 @@ impl core::fmt::Debug for InodeInfo {
203215
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
204216
f.debug_struct("InodeInfo")
205217
.field("pid", &self.pid)
218+
.field("tid", &self.tid)
206219
.field("ftype", &self.ftype)
207220
.field("fd", &self.fd)
208221
.field("target_inode", &self.target_inode.is_some())
@@ -660,6 +673,7 @@ impl ProcFS {
660673
fs: Weak::default(),
661674
fdata: InodeInfo {
662675
pid: None,
676+
tid: None,
663677
ftype: ProcFileType::Default,
664678
fd: -1,
665679
target_inode: None,
@@ -729,6 +743,15 @@ impl ProcFS {
729743
statm_file.0.lock().fdata.pid = Some(pid);
730744
statm_file.0.lock().fdata.ftype = ProcFileType::ProcStatm;
731745

746+
// stat 文件: /proc/<pid>/stat
747+
let stat_binding = pid_dir.create("stat", FileType::File, InodeMode::S_IRUGO)?;
748+
let stat_file = stat_binding
749+
.as_any_ref()
750+
.downcast_ref::<LockedProcFSInode>()
751+
.unwrap();
752+
stat_file.0.lock().fdata.pid = Some(pid);
753+
stat_file.0.lock().fdata.ftype = ProcFileType::ProcPidStat;
754+
732755
// maps 文件
733756
let maps_binding = pid_dir.create("maps", FileType::File, InodeMode::S_IRUGO)?;
734757
let maps_file = maps_binding
@@ -747,6 +770,16 @@ impl ProcFS {
747770
cmdline_file.0.lock().fdata.pid = Some(pid);
748771
cmdline_file.0.lock().fdata.ftype = ProcFileType::ProcPidCmdline;
749772

773+
// task dir: /proc/<pid>/task
774+
let task_dir =
775+
pid_dir.create("task", FileType::Dir, InodeMode::from_bits_truncate(0o555))?;
776+
let task_dir = task_dir
777+
.as_any_ref()
778+
.downcast_ref::<LockedProcFSInode>()
779+
.unwrap();
780+
task_dir.0.lock().fdata.pid = Some(pid);
781+
task_dir.0.lock().fdata.ftype = ProcFileType::ProcPidTaskDir;
782+
750783
// fd dir
751784
let fd = pid_dir.create("fd", FileType::Dir, InodeMode::from_bits_truncate(0o555))?;
752785
let fd = fd.as_any_ref().downcast_ref::<LockedProcFSInode>().unwrap();
@@ -797,6 +830,8 @@ impl ProcFS {
797830
// 删除进程文件夹下文件
798831
pid_dir.unlink("status")?;
799832
pid_dir.unlink("exe")?;
833+
let _ = pid_dir.unlink("stat");
834+
let _ = pid_dir.rmdir("task");
800835
pid_dir.rmdir("fd")?;
801836
pid_dir.rmdir("fdinfo")?;
802837
let _ = pid_dir.unlink("mounts");
@@ -985,6 +1020,8 @@ impl IndexNode for LockedProcFSInode {
9851020
ProcFileType::ProcMaps => inode.open_maps(&mut proc_private)?,
9861021
ProcFileType::ProcCmdline => inode.open_cmdline(&mut proc_private)?,
9871022
ProcFileType::ProcPidCmdline => inode.open_pid_cmdline(&mut proc_private)?,
1023+
ProcFileType::ProcPidStat => inode.open_pid_stat(&mut proc_private)?,
1024+
ProcFileType::ProcPidTaskTidStat => inode.open_pid_task_tid_stat(&mut proc_private)?,
9881025
ProcFileType::ProcExe => inode.open_exe(&mut proc_private)?,
9891026
ProcFileType::ProcMounts => inode.open_mounts(&mut proc_private)?,
9901027
ProcFileType::ProcMountInfo => {
@@ -1002,6 +1039,8 @@ impl IndexNode for LockedProcFSInode {
10021039
}
10031040
ProcFileType::Default => inode.data.len() as i64,
10041041
ProcFileType::ProcKmsg
1042+
| ProcFileType::ProcPidTaskDir
1043+
| ProcFileType::ProcPidTaskTidDir
10051044
| ProcFileType::ProcFdDir
10061045
| ProcFileType::ProcFdFile
10071046
| ProcFileType::ProcFdInfoDir
@@ -1076,6 +1115,8 @@ impl IndexNode for LockedProcFSInode {
10761115
| ProcFileType::ProcMaps
10771116
| ProcFileType::ProcCmdline
10781117
| ProcFileType::ProcPidCmdline
1118+
| ProcFileType::ProcPidStat
1119+
| ProcFileType::ProcPidTaskTidStat
10791120
| ProcFileType::ProcMounts
10801121
| ProcFileType::ProcMountInfo
10811122
| ProcFileType::ProcVersion
@@ -1243,6 +1284,7 @@ impl IndexNode for LockedProcFSInode {
12431284
fs: inode.fs.clone(),
12441285
fdata: InodeInfo {
12451286
pid: None,
1287+
tid: None,
12461288
ftype: ProcFileType::Default,
12471289
fd: -1,
12481290
target_inode: None,
@@ -1369,6 +1411,12 @@ impl IndexNode for LockedProcFSInode {
13691411
ProcFileType::ProcThreadSelfNsRoot => {
13701412
return self.dynamical_find_thread_self_ns(name);
13711413
}
1414+
ProcFileType::ProcPidTaskDir => {
1415+
return self.dynamical_find_task_tid(name);
1416+
}
1417+
ProcFileType::ProcPidTaskTidDir => {
1418+
return self.dynamical_find_task_tid_child(name);
1419+
}
13721420
_ => {}
13731421
}
13741422

@@ -1457,6 +1505,11 @@ impl IndexNode for LockedProcFSInode {
14571505

14581506
return Ok(keys);
14591507
}
1508+
ProcFileType::ProcPidTaskDir => {
1509+
let mut tids = self.dynamical_list_task_tids()?;
1510+
keys.append(&mut tids);
1511+
return Ok(keys);
1512+
}
14601513
_ => {}
14611514
}
14621515

Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
use alloc::{
2+
format,
3+
string::{String, ToString},
4+
sync::Arc,
5+
};
6+
use system_error::SystemError;
7+
8+
use crate::{
9+
arch::MMArch,
10+
mm::MemoryManagementArch,
11+
process::{pid::PidType, ProcessControlBlock, ProcessManager, ProcessState, RawPid},
12+
};
13+
14+
use super::{ProcFSInode, ProcfsFilePrivateData};
15+
16+
fn state_to_linux_char(state: ProcessState) -> char {
17+
match state {
18+
ProcessState::Runnable => 'R',
19+
ProcessState::Blocked(interruptable) => {
20+
if interruptable {
21+
'S'
22+
} else {
23+
'D'
24+
}
25+
}
26+
ProcessState::Stopped => 'T',
27+
ProcessState::Exited(_) => 'Z',
28+
}
29+
}
30+
31+
fn sanitize_comm_for_proc_stat(comm: &str) -> String {
32+
// BusyBox/procps 使用 strrchr(')') 定位 comm 结束位置。
33+
// 若 comm 中包含 ')' 会破坏解析,先做最小替换。
34+
comm.chars()
35+
.map(|c| if c == ')' { '_' } else { c })
36+
.collect()
37+
}
38+
39+
fn generate_linux_proc_stat_line(
40+
pid: RawPid,
41+
comm: &str,
42+
state: ProcessState,
43+
ppid: RawPid,
44+
pcb: &Arc<ProcessControlBlock>,
45+
) -> String {
46+
// Linux /proc/[pid]/stat 字段:
47+
// pid (comm) state ppid pgrp session tty_nr tpgid flags minflt cminflt majflt cmajflt
48+
// utime stime cutime cstime priority nice num_threads itrealvalue starttime vsize rss ...
49+
//
50+
// BusyBox 1.35.0 只强依赖到 rss 字段位置,且字段顺序必须对齐。
51+
let comm = sanitize_comm_for_proc_stat(comm);
52+
let state_ch = state_to_linux_char(state);
53+
54+
// 尽量填真实值;拿不到的先填 0,不影响 BusyBox 解析,只影响输出质量。
55+
let pgrp: usize = 0;
56+
let session: usize = 0;
57+
let tty_nr: i32 = 0;
58+
let tpgid: i32 = 0;
59+
let flags: u64 = 0;
60+
let minflt: u64 = 0;
61+
let cminflt: u64 = 0;
62+
let majflt: u64 = 0;
63+
let cmajflt: u64 = 0;
64+
let utime: u64 = 0;
65+
let stime: u64 = 0;
66+
let cutime: i64 = 0;
67+
let cstime: i64 = 0;
68+
let priority: i64 = 0;
69+
let nice: i64 = 0;
70+
// Linux 语义:进程线程组中的线程数量(包含主线程)。
71+
// 线程组成员由 Pid(TGID)->tasks[PidType::TGID] 维护;用它计数比本地缓存更可靠。
72+
let num_threads: i64 = pcb
73+
.task_pid_ptr(PidType::TGID)
74+
.map(|tgid_pid| tgid_pid.tasks_iter(PidType::TGID).count() as i64)
75+
.unwrap_or(1);
76+
let itrealvalue: i64 = 0;
77+
let starttime: u64 = 0;
78+
79+
// vsize: bytes, rss: pages
80+
// 使用传入的 pcb,避免重复查找
81+
let (vsize_bytes, rss_pages) = pcb
82+
.basic()
83+
.user_vm()
84+
.map(|vm| {
85+
let guard = vm.read();
86+
let bytes = guard.vma_usage_bytes();
87+
let pages = (bytes.saturating_add(MMArch::PAGE_SIZE - 1)) >> MMArch::PAGE_SHIFT;
88+
(bytes as u64, pages as u64)
89+
})
90+
.unwrap_or((0, 0));
91+
92+
// rsslim 及后续字段给足占位,避免对方 parser 依赖更多字段时出问题
93+
format!(
94+
"{pid} ({comm}) {state_ch} {ppid} {pgrp} {session} {tty_nr} {tpgid} {flags} \
95+
{minflt} {cminflt} {majflt} {cmajflt} {utime} {stime} {cutime} {cstime} {priority} {nice} \
96+
{num_threads} {itrealvalue} {starttime} {vsize_bytes} {rss_pages} 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0\n",
97+
pid = pid.data(),
98+
ppid = ppid.data(),
99+
)
100+
}
101+
102+
impl ProcFSInode {
103+
/// 生成进程 stat 信息的公共逻辑
104+
fn generate_pid_stat_data(pid: RawPid) -> Result<String, SystemError> {
105+
let pcb = ProcessManager::find_task_by_vpid(pid).ok_or(SystemError::ESRCH)?;
106+
107+
let comm = pcb.basic().name().to_string();
108+
let sched = pcb.sched_info();
109+
let state = sched.inner_lock_read_irqsave().state();
110+
111+
let ppid = pcb
112+
.parent_pcb()
113+
.map(|p| p.raw_pid())
114+
.unwrap_or(RawPid::new(0));
115+
116+
Ok(generate_linux_proc_stat_line(pid, &comm, state, ppid, &pcb))
117+
}
118+
119+
/// /proc/<pid>/stat
120+
#[inline(never)]
121+
pub(super) fn open_pid_stat(
122+
&self,
123+
pdata: &mut ProcfsFilePrivateData,
124+
) -> Result<i64, SystemError> {
125+
let pid = self.fdata.pid.ok_or(SystemError::EINVAL)?;
126+
127+
let s = Self::generate_pid_stat_data(pid)?;
128+
pdata.data = s.into_bytes();
129+
Ok(pdata.data.len() as i64)
130+
}
131+
132+
/// /proc/<pid>/task/<tid>/stat(最小实现:先按进程视图输出)
133+
#[inline(never)]
134+
pub(super) fn open_pid_task_tid_stat(
135+
&self,
136+
pdata: &mut ProcfsFilePrivateData,
137+
) -> Result<i64, SystemError> {
138+
let pid = self.fdata.pid.ok_or(SystemError::EINVAL)?;
139+
// 目前内核线程/用户线程还没有独立的 tid 视图,这里先占位:tid 仅用于路径匹配。
140+
let _tid = self.fdata.tid.ok_or(SystemError::EINVAL)?;
141+
142+
let s = Self::generate_pid_stat_data(pid)?;
143+
pdata.data = s.into_bytes();
144+
Ok(pdata.data.len() as i64)
145+
}
146+
}

0 commit comments

Comments
 (0)