/proc 文件系统¶
/proc/sys |
Terrehon Bowden <terrehon@pacbell.net>, Bodo Bauer <bb@ricochet.net> |
1999 年 10 月 7 日 |
2.4.x 更新 |
Jorge Nerin <comandante@zaralinux.com> |
2000 年 11 月 14 日 |
移动 /proc/sys |
Shen Feng <shen@cn.fujitsu.com> |
2009 年 4 月 1 日 |
修复/更新 第 1.1 部分 |
Stefani Seibold <stefani@seibold.net> |
2009 年 6 月 9 日 |
前言¶
0.1 引言/鸣谢¶
本文档是即将发布(或者我们希望如此)的关于 SuSE Linux 发行版的书籍的一部分。由于 /proc 文件系统没有完整的文档,并且我们使用了许多免费提供的资源来编写这些章节,因此将这项工作回馈给 Linux 社区似乎是公平的。这项工作基于 2.2.* 内核版本和即将到来的 2.4.*。恐怕它还远未完成,但我们希望它会有用。据我们所知,这是第一份关于 /proc 文件系统的“all-in-one”文档。它专注于 Intel x86 硬件,因此如果您正在寻找 PPC、ARM、SPARC、AXP 等功能,您可能找不到您要找的东西。它也只涵盖 IPv4 网络,不包括 IPv6 或其他协议 - 抱歉。但是欢迎添加和补丁,如果您将它们邮寄给 Bodo,它们将被添加到本文档中。
我们要感谢 Alan Cox、Rik van Riel 和 Alexey Kuznetsov 以及许多其他人帮助编写本文档。我们还要特别感谢 Andi Kleen 的文档,我们在创建本文档时严重依赖这些文档,以及他提供的其他信息。感谢所有为 Linux 内核贡献源代码或文档并帮助创建出色软件的人…… :)
如果您有任何意见、更正或补充,请随时通过 bb@ricochet.net 联系 Bodo Bauer。我们很乐意将它们添加到本文档中。
本文档的最新版本可在网上找到:https://linuxkernel.org.cn/doc/html/latest/filesystems/proc.html
如果上述方向对您不起作用,您可以尝试内核邮件列表 linux-kernel@vger.kernel.org 和/或尝试通过 comandante@zaralinux.com 与我联系。
0.2 法律声明¶
我们不保证本文档的正确性,如果您因为不正确的文档而搞砸了您的系统而向我们抱怨,我们不会承担责任……
第 1 章:收集系统信息¶
本章内容¶
调查伪文件系统 /proc 的属性及其提供有关运行中的 Linux 系统的信息的能力
检查 /proc 的结构
揭示有关内核和系统上运行的进程的各种信息
proc 文件系统充当内核中内部数据结构的接口。它可用于获取有关系统的信息并在运行时更改某些内核参数 (sysctl)。
首先,我们将看看 /proc 的只读部分。在第 2 章中,我们将向您展示如何使用 /proc/sys 更改设置。
1.1 进程特定的子目录¶
/proc 目录(除其他外)包含系统上运行的每个进程的一个子目录,该子目录以进程 ID (PID) 命名。
链接“self”指向读取文件系统的进程。每个进程子目录都有表 1-1 中列出的条目。
进程可以从 /proc/PID/* 读取自己的信息,而无需额外的权限。读取其他进程的 /proc/PID/* 信息时,读取进程需要具有 CAP_SYS_PTRACE 能力和 PTRACE_MODE_READ 访问权限,或者具有 CAP_PERFMON 能力。这适用于所有只读信息,如 maps、environ、pagemap 等。唯一的例外是 mem 文件,由于其读写性质,需要 CAP_SYS_PTRACE 能力和更高的 PTRACE_MODE_ATTACH 权限;CAP_PERFMON 能力不授予对其他进程的 /proc/PID/mem 的访问权限。
请注意,打开 /proc/<pid> 或其包含的任何文件或子目录的文件描述符不会阻止 <pid> 在 <pid> 退出的情况下被其他进程重用。对与已死进程对应的打开 /proc/<pid> 文件描述符的操作永远不会对内核可能(通过偶然)也分配了进程 ID <pid> 的任何新进程起作用。相反,对这些 FD 的操作通常会失败,并出现 ESRCH。
文件 |
内容 |
---|---|
clear_refs |
清除 smaps 输出中显示 的页面引用位 |
cmdline |
命令行参数 |
cpu |
当前和上次执行的 cpu (2.4)(smp) |
cwd |
链接到当前工作目录 |
environ |
环境变量的值 |
exe |
链接到此进程的可执行文件 |
fd |
目录,其中包含所有文件描述符 |
maps |
到可执行文件和库文件的内存映射 (2.4) |
mem |
此进程持有的内存 |
root |
链接到此进程的根目录 |
stat |
进程状态 |
statm |
进程内存状态信息 |
status |
以人类可读形式显示的进程状态 |
wchan |
存在 CONFIG_KALLSYMS=y 时:它显示任务阻塞的内核函数符号 - 如果未阻塞,则显示“0”。 |
pagemap |
页表 |
stack |
报告完整堆栈跟踪,通过 CONFIG_STACKTRACE 启用 |
smaps |
基于 maps 的扩展,显示每个映射的内存消耗以及与之关联的标志 |
smaps_rollup |
进程所有映射的累积 smaps 统计信息。这可以从 smaps 派生,但速度更快、更方便 |
numa_maps |
基于 maps 的扩展,显示内存局部性和绑定策略以及每个映射的内存使用情况(以页为单位)。 |
例如,要获取进程的状态信息,您所要做的就是读取文件 /proc/PID/status
>cat /proc/self/status
Name: cat
State: R (running)
Tgid: 5452
Pid: 5452
PPid: 743
TracerPid: 0 (2.4)
Uid: 501 501 501 501
Gid: 100 100 100 100
FDSize: 256
Groups: 100 14 16
Kthread: 0
VmPeak: 5004 kB
VmSize: 5004 kB
VmLck: 0 kB
VmHWM: 476 kB
VmRSS: 476 kB
RssAnon: 352 kB
RssFile: 120 kB
RssShmem: 4 kB
VmData: 156 kB
VmStk: 88 kB
VmExe: 68 kB
VmLib: 1412 kB
VmPTE: 20 kb
VmSwap: 0 kB
HugetlbPages: 0 kB
CoreDumping: 0
THP_enabled: 1
Threads: 1
SigQ: 0/28578
SigPnd: 0000000000000000
ShdPnd: 0000000000000000
SigBlk: 0000000000000000
SigIgn: 0000000000000000
SigCgt: 0000000000000000
CapInh: 00000000fffffeff
CapPrm: 0000000000000000
CapEff: 0000000000000000
CapBnd: ffffffffffffffff
CapAmb: 0000000000000000
NoNewPrivs: 0
Seccomp: 0
Speculation_Store_Bypass: thread vulnerable
SpeculationIndirectBranch: conditional enabled
voluntary_ctxt_switches: 0
nonvoluntary_ctxt_switches: 1
这会向您显示几乎与您使用 ps 命令查看时相同的信息。实际上,ps 使用 proc 文件系统来获取其信息。但是,通过读取文件 /proc/PID/status,您可以获得进程的更详细视图。它的字段在表 1-2 中描述。
statm 文件包含有关进程内存使用情况的更多详细信息。它的七个字段在表 1-3 中解释。stat 文件包含有关进程本身的详细信息。它的字段在表 1-4 中解释。
(对于 SMP CONFIG 用户)
为了使记帐可扩展,RSS 相关信息以异步方式处理,并且该值可能不是很精确。要查看某个时刻的精确快照,您可以查看 /proc/<pid>/smaps 文件并扫描页表。它很慢,但非常精确。
字段 |
内容 |
---|---|
名称 |
可执行文件的文件名 |
Umask |
文件模式创建掩码 |
State |
状态(R 是运行,S 是睡眠,D 是在不可中断的等待中睡眠,Z 是僵尸,T 是被跟踪或停止) |
Tgid |
线程组 ID |
Ngid |
NUMA 组 ID(如果没有,则为 0) |
Pid |
进程 ID |
PPid |
父进程的进程 ID |
TracerPid |
跟踪此进程的进程的 PID(如果不是,或者跟踪器在当前 pid 命名空间之外,则为 0) |
Uid |
实际、有效、保存的集合和文件系统 UID |
Gid |
实际、有效、保存的集合和文件系统 GID |
FDSize |
当前分配的文件描述符槽数 |
Groups |
补充组列表 |
NStgid |
后代命名空间线程组 ID 层次结构 |
NSpid |
后代命名空间进程 ID 层次结构 |
NSpgid |
后代命名空间进程组 ID 层次结构 |
NSsid |
后代命名空间会话 ID 层次结构 |
Kthread |
内核线程标志,1 表示是,0 表示否 |
VmPeak |
峰值虚拟内存大小 |
VmSize |
总程序大小 |
VmLck |
锁定的内存大小 |
VmPin |
固定的内存大小 |
VmHWM |
峰值常驻集大小(“最高水位线”) |
VmRSS |
内存部分的大小。它包含以下三个部分(VmRSS = RssAnon + RssFile + RssShmem) |
RssAnon |
常驻匿名内存的大小 |
RssFile |
常驻文件映射的大小 |
RssShmem |
常驻 shmem 内存的大小(包括 SysV shm、tmpfs 映射和共享匿名映射) |
VmData |
私有数据段的大小 |
VmStk |
堆栈段的大小 |
VmExe |
文本段的大小 |
VmLib |
共享库代码的大小 |
VmPTE |
页表条目的大小 |
VmSwap |
匿名私有数据使用的交换空间量(不包括 shmem 交换空间使用量) |
HugetlbPages |
hugetlb 内存部分的大小 |
CoreDumping |
进程的内存当前正在转储(终止进程可能会导致核心损坏) |
THP_enabled |
进程允许使用 THP(当在进程上设置 PR_SET_THP_DISABLE 时返回 0 |
Threads |
线程数 |
SigQ |
排队的信号数/队列的最大数 |
SigPnd |
线程的挂起信号位图 |
ShdPnd |
进程的共享挂起信号位图 |
SigBlk |
阻止的信号位图 |
SigIgn |
忽略的信号位图 |
SigCgt |
捕获的信号位图 |
CapInh |
可继承能力的位图 |
CapPrm |
允许的能力的位图 |
CapEff |
有效能力的位图 |
CapBnd |
能力边界集位图 |
CapAmb |
环境能力位图 |
NoNewPrivs |
no_new_privs,如 prctl(PR_GET_NO_NEW_PRIV, ...) |
Seccomp |
seccomp 模式,如 prctl(PR_GET_SECCOMP, ...) |
Speculation_Store_Bypass |
推测存储旁路缓解状态 |
SpeculationIndirectBranch |
间接分支推测模式 |
Cpus_allowed |
此进程可以在其上运行的 CPU 掩码 |
Cpus_allowed_list |
与上一个相同,但以“列表格式”显示 |
Mems_allowed |
允许此进程使用的内存节点掩码 |
Mems_allowed_list |
与上一个相同,但以“列表格式”显示 |
voluntary_ctxt_switches |
自愿上下文切换的次数 |
nonvoluntary_ctxt_switches |
非自愿上下文切换的次数 |
字段 |
内容 |
|
---|---|---|
size |
总程序大小(页) |
(与 status 中的 VmSize 相同) |
resident |
内存部分的大小(页) |
(与 status 中的 VmRSS 相同) |
shared |
共享的页数 |
(即由文件支持,与 status 中的 RssFile+RssShmem 相同) |
trs |
“代码”的页数 |
(不包括 libs;已损坏,包括数据段) |
lrs |
库页数 |
(在 2.6 上始终为 0) |
drs |
数据/堆栈的页数 |
(包括 libs;已损坏,包括库文本) |
dt |
脏页数 |
(在 2.6 上始终为 0) |
字段 |
内容 |
---|---|
pid |
进程 ID |
tcomm |
可执行文件的文件名 |
state |
状态(R 是运行,S 是睡眠,D 是在不可中断的等待中睡眠,Z 是僵尸,T 是被跟踪或停止) |
ppid |
父进程的进程 ID |
pgrp |
进程的 pgrp |
sid |
会话 ID |
tty_nr |
进程使用的 tty |
tty_pgrp |
tty 的 pgrp |
flags |
任务标志 |
min_flt |
次要错误的数量 |
cmin_flt |
带有子进程的次要错误的数量 |
maj_flt |
主要错误的数量 |
cmaj_flt |
带有子进程的主要错误的数量 |
utime |
用户模式节拍数 |
stime |
内核模式节拍数 |
cutime |
带有子进程的用户模式节拍数 |
cstime |
带有子进程的内核模式节拍数 |
priority |
优先级 |
nice |
nice 级别 |
num_threads |
线程数 |
it_real_value |
(已过时,始终为 0) |
start_time |
系统启动后进程启动的时间 |
vsize |
虚拟内存大小 |
rss |
常驻集内存大小 |
rsslim |
rss 的当前字节限制 |
start_code |
程序文本可以在其上方运行的地址 |
end_code |
程序文本可以在其下方运行的地址 |
start_stack |
主进程堆栈的起始地址 |
esp |
ESP 的当前值 |
eip |
EIP 的当前值 |
pending |
挂起信号的位图 |
blocked |
阻止的信号位图 |
sigign |
忽略的信号位图 |
sigcatch |
捕获的信号位图 |
0 |
(占位符,曾经是 wchan 地址,请改用 /proc/PID/wchan) |
0 |
(占位符) |
0 |
(占位符) |
exit_signal |
退出时要发送到父线程的信号 |
task_cpu |
任务计划在其上运行的 CPU |
rt_priority |
实时优先级 |
policy |
调度策略 (man sched_setscheduler) |
blkio_ticks |
等待块 IO 的时间 |
gtime |
任务的客户时间(以节拍为单位) |
cgtime |
任务子进程的客户时间(以节拍为单位) |
start_data |
程序 data+bss 放置在其上方的地址 |
end_data |
程序 data+bss 放置在其下方的地址 |
start_brk |
程序堆可以通过 brk() 扩展的地址 |
arg_start |
程序命令行放置在其上方的地址 |
arg_end |
程序命令行放置在其下方的地址 |
env_start |
程序环境放置在其上方的地址 |
env_end |
程序环境放置在其下方的地址 |
exit_code |
waitpid 系统调用报告的线程的 exit_code 形式 |
/proc/PID/maps 文件包含当前映射的内存区域及其访问权限。
格式为
address perms offset dev inode pathname
08048000-08049000 r-xp 00000000 03:00 8312 /opt/test
08049000-0804a000 rw-p 00001000 03:00 8312 /opt/test
0804a000-0806b000 rw-p 00000000 00:00 0 [heap]
a7cb1000-a7cb2000 ---p 00000000 00:00 0
a7cb2000-a7eb2000 rw-p 00000000 00:00 0
a7eb2000-a7eb3000 ---p 00000000 00:00 0
a7eb3000-a7ed5000 rw-p 00000000 00:00 0
a7ed5000-a8008000 r-xp 00000000 03:00 4222 /lib/libc.so.6
a8008000-a800a000 r--p 00133000 03:00 4222 /lib/libc.so.6
a800a000-a800b000 rw-p 00135000 03:00 4222 /lib/libc.so.6
a800b000-a800e000 rw-p 00000000 00:00 0
a800e000-a8022000 r-xp 00000000 03:00 14462 /lib/libpthread.so.0
a8022000-a8023000 r--p 00013000 03:00 14462 /lib/libpthread.so.0
a8023000-a8024000 rw-p 00014000 03:00 14462 /lib/libpthread.so.0
a8024000-a8027000 rw-p 00000000 00:00 0
a8027000-a8043000 r-xp 00000000 03:00 8317 /lib/ld-linux.so.2
a8043000-a8044000 r--p 0001b000 03:00 8317 /lib/ld-linux.so.2
a8044000-a8045000 rw-p 0001c000 03:00 8317 /lib/ld-linux.so.2
aff35000-aff4a000 rw-p 00000000 00:00 0 [stack]
ffffe000-fffff000 r-xp 00000000 00:00 0 [vdso]
其中“address”是它占用的进程中的地址空间,“perms”是一组权限
r = read
w = write
x = execute
s = shared
p = private (copy on write)
“offset”是映射中的偏移量,“dev”是设备(major:minor),“inode”是该设备上的 inode。0 表示没有 inode 与内存区域关联,BSS(未初始化的数据)就是这种情况。“pathname”显示与此映射关联的文件的名称。如果映射未与文件关联
[heap]
程序的堆
[stack]
主进程的堆栈
[vdso]
“虚拟动态共享对象”,内核系统调用处理程序
[anon:<name>]
已被用户空间命名的私有匿名映射
[anon_shmem:<name>]
已被用户空间命名的匿名共享内存映射
或者,如果为空,则映射是匿名的。
从 6.11 内核开始,/proc/PID/maps 提供了一个替代的基于 ioctl() 的 API,该 API 能够灵活高效地查询和过滤单个 VMA。struct procmap_query,在 linux/fs.h UAPI 标头中定义,用作 PROCMAP_QUERY ioctl() 命令的输入/输出参数。有关查询语义、支持的标志、返回的数据以及常规 API 用法信息的详细信息,请参见 linus/fs.h UAPI 标头中的注释。
/proc/PID/smaps 是基于 maps 的扩展,显示了进程的每个映射的内存消耗。对于每个映射(也称为虚拟内存区域或 VMA),都有一系列行,例如以下内容
08048000-080bc000 r-xp 00000000 03:02 13130 /bin/bash
Size: 1084 kB
KernelPageSize: 4 kB
MMUPageSize: 4 kB
Rss: 892 kB
Pss: 374 kB
Pss_Dirty: 0 kB
Shared_Clean: 892 kB
Shared_Dirty: 0 kB
Private_Clean: 0 kB
Private_Dirty: 0 kB
Referenced: 892 kB
Anonymous: 0 kB
KSM: 0 kB
LazyFree: 0 kB
AnonHugePages: 0 kB
ShmemPmdMapped: 0 kB
Shared_Hugetlb: 0 kB
Private_Hugetlb: 0 kB
Swap: 0 kB
SwapPss: 0 kB
KernelPageSize: 4 kB
MMUPageSize: 4 kB
Locked: 0 kB
THPeligible: 0
VmFlags: rd ex mr mw me dw
这些行的第一行显示与 /proc/PID/maps 中显示的映射相同的信息。以下行显示映射的大小(大小);在支持 VMA 时分配的每个页面的大小(KernelPageSize),通常与页表条目中的大小相同;MMU 在支持 VMA 时使用的页面大小(在大多数情况下,与 KernelPageSize 相同);当前驻留在 RAM 中的映射量 (RSS);进程在此映射中的比例份额 (PSS);以及映射中干净和脏的共享页面和私有页面的数量。
进程的“比例集大小”(PSS) 是其在内存中拥有的页面计数,其中每个页面都除以共享它的进程数。因此,如果一个进程拥有 1000 个页面,并且与另一个进程共享 1000 个页面,则其 PSS 将为 1500。“Pss_Dirty”是 PSS 中由脏页面组成的部分。(不包括“Pss_Clean”,但可以通过从“Pss”中减去“Pss_Dirty”来计算。)
传统上,如果一个页面被映射一次,则将其计为“私有”页面;当页面被多次映射时,则将其计为“共享”页面,即使在同一进程中多次映射也是如此。请注意,此记帐独立于 MAP_SHARED。
在某些内核配置中,较大分配(例如,THP)的一部分的页面的语义可能有所不同:如果属于相应较大分配的所有页面确定在同一进程中映射(即使该页面在该进程中映射多次),则该页面将计为“私有”页面。如果较大分配的任何页面可能在不同的进程中映射,则该页面将计为“共享”页面。在某些情况下,即使较大分配不再是这种情况,也可能将其视为“可能被多个进程映射”。
某些内核配置不跟踪较大分配的一部分的页面映射的精确次数。在这种情况下,在计算 PSS 时,此较大分配中每个页面的平均映射数可以用作页面映射数的近似值。在这种情况下,PSS 计算将是不精确的。
“Referenced”指示当前标记为引用或访问的内存量。
“Anonymous”显示不属于任何文件的内存量。即使与文件关联的映射也可能包含匿名页面:当 MAP_PRIVATE 和一个页面被修改时,文件页面将被私有匿名副本替换。
“KSM”报告有多少页面是 KSM 页面。请注意,不包括 KSM 放置的零页面,仅包括实际 KSM 页面。
“LazyFree”显示 madvise(MADV_FREE) 标记的内存量。该内存不会立即通过 madvise() 释放。如果在内存压力下内存是干净的,则会释放该内存。请注意,由于当前实现中使用的优化,打印的值可能低于实际值。如果这是不可取的,请提交错误报告。
“AnonHugePages”显示由透明巨页支持的内存量。
“ShmemPmdMapped”显示由巨页支持的共享 (shmem/tmpfs) 内存量。
“Shared_Hugetlb”和“Private_Hugetlb”显示由 hugetlbfs 页面支持的内存量,由于历史原因,该内存未计入“RSS”或“PSS”字段中。并且这些不包括在 {Shared,Private}_{Clean,Dirty} 字段中。
“Swap”显示了多少可以成为匿名内存也被使用,但已在交换空间中。
对于 shmem 映射,“Swap”还包括 shmem 对象底层交换空间上映射(并且未被写时复制替换)部分的大小。“SwapPss”显示此映射的比例交换空间份额。与“Swap”不同,这不考虑底层 shmem 对象的交换出页面。“Locked”指示映射是否锁定在内存中。
“THPeligible”指示映射是否符合分配任何当前启用的自然对齐 THP 页面的大小。如果为真,则为 1;否则为 0。
“VmFlags”字段值得单独描述。此成员以双字母编码方式表示与特定虚拟内存区域关联的内核标志。代码如下
rd
可读
wr
可写
ex
可执行
sh
shared
mr
可能读取
mw
可能写入
me
可能执行
ms
可能共享
gd
堆栈段向下增长
pf
纯 PFN 范围
lo
页面锁定在内存中
io
内存映射 I/O 区域
sr
提供顺序读取建议
rr
提供随机读取建议
dc
不在 fork 时复制区域
de
不在重新映射时扩展区域
ac
该区域是可记帐的
nr
未为此区域保留交换空间
ht
该区域使用巨型 tlb 页面
sf
同步页面错误
ar
特定于架构的标志
wf
在 fork 时擦除
dd
不将该区域包含到核心转储中
sd
软脏标志
mm
混合映射区域
hg
巨页建议标志
nh
无巨页建议标志
mg
可合并建议标志
bt
arm64 BTI 受保护页面
mt
已启用 arm64 MTE 分配标记
um
userfaultfd 缺少跟踪
uw
userfaultfd wr-protect 跟踪
ui
userfaultfd 次要错误
ss
阴影/受保护的控制堆栈页面
sl
密封
lf
在错误页面上锁定
dp
始终可延迟释放的映射
请注意,不能保证每个标志和关联的助记符都会出现在所有以后的内核版本中。事情会发生变化,标志可能会消失,或者相反——会添加新的标志。将来对其含义的解释也可能会发生变化。因此,这些标志的每个使用者都必须遵循每个特定的内核版本,以了解其确切的语义。
仅当启用 CONFIG_MMU 内核配置选项时,此文件才存在。
注意:读取 /proc/PID/maps 或 /proc/PID/smaps 本质上是竞争的(只有在单个读取调用中才能实现一致的输出)。
当在修改内存映射时对这些文件进行部分读取时,通常会表现出这种情况。尽管存在竞争,但我们确实提供了以下保证
映射的地址永远不会向后移动,这意味着没有两个区域会重叠。
如果在 smaps/maps 遍历的整个生命周期中,给定的 vaddr 上存在某些内容,则会为其提供一些输出。
/proc/PID/smaps_rollup 文件包含与 /proc/PID/smaps 相同的字段,但其值是进程所有映射的相应值的总和。此外,它还包含以下字段
Pss_Anon
Pss_File
Pss_Shmem
它们表示匿名页面、文件页面和 shmem 页面的比例份额,如上文针对 smaps 所述。这些字段在 smaps 中被省略,因为每个映射都标识了它包含的所有页面的类型(匿名页面、文件页面或 shmem 页面)。因此,smaps_rollup 中的所有信息都可以从 smaps 派生,但成本要高得多。
/proc/PID/clear_refs 用于重置与进程关联的物理页面和虚拟页面上的 PG_Referenced 和 ACCESSED/YOUNG 位,以及 pte 上的软脏位(有关详细信息,请参见 Soft-Dirty PTEs)。要清除与进程关联的所有页面的位
> echo 1 > /proc/PID/clear_refs
要清除与进程关联的匿名页面的位
> echo 2 > /proc/PID/clear_refs
要清除与进程关联的文件映射页面的位
> echo 3 > /proc/PID/clear_refs
要清除软脏位
> echo 4 > /proc/PID/clear_refs
要将峰值常驻集大小(“最高水位线”)重置为进程的当前值
> echo 5 > /proc/PID/clear_refs
写入 /proc/PID/clear_refs 的任何其他值都不会产生任何影响。
/proc/pid/pagemap 提供 PFN,可用于使用 /proc/kpageflags 查找页面标志,并使用 /proc/kpagecount 查找页面映射的次数。有关详细说明,请参见 Examining Process Page Tables。
/proc/pid/numa_maps 是基于 maps 的扩展,显示了内存局部性和绑定策略,以及每个映射的内存使用情况(以页为单位)。输出遵循一种通用格式,其中映射详细信息被空格分隔地汇总,每个映射一行
address policy mapping details
00400000 default file=/usr/local/bin/app mapped=1 active=0 N3=1 kernelpagesize_kB=4
00600000 default file=/usr/local/bin/app anon=1 dirty=1 N3=1 kernelpagesize_kB=4
3206000000 default file=/lib64/ld-2.12.so mapped=26 mapmax=6 N0=24 N3=2 kernelpagesize_kB=4
320621f000 default file=/lib64/ld-2.12.so anon=1 dirty=1 N3=1 kernelpagesize_kB=4
3206220000 default file=/lib64/ld-2.12.so anon=1 dirty=1 N3=1 kernelpagesize_kB=4
3206221000 default anon=1 dirty=1 N3=1 kernelpagesize_kB=4
3206800000 default file=/lib64/libc-2.12.so mapped=59 mapmax=21 active=55 N0=41 N3=18 kernelpagesize_kB=4
320698b000 default file=/lib64/libc-2.12.so
3206b8a000 default file=/lib64/libc-2.12.so anon=2 dirty=2 N3=2 kernelpagesize_kB=4
3206b8e000 default file=/lib64/libc-2.12.so anon=1 dirty=1 N3=1 kernelpagesize_kB=4
3206b8f000 default anon=3 dirty=3 active=1 N3=3 kernelpagesize_kB=4
7f4dc10a2000 default anon=3 dirty=3 N3=3 kernelpagesize_kB=4
7f4dc10b4000 default anon=2 dirty=2 active=1 N3=2 kernelpagesize_kB=4
7f4dc1200000 default file=/anon_hugepage\040(deleted) huge anon=1 dirty=1 N3=1 kernelpagesize_kB=2048
7fff335f0000 default stack anon=3 dirty=3 N3=3 kernelpagesize_kB=4
7fff3369d000 default mapped=1 mapmax=35 active=0 N3=1 kernelpagesize_kB=4
其中
“address”是映射的起始地址;
“policy”报告为映射设置的 NUMA 内存策略(请参见 NUMA 内存策略);
“mapping details”汇总了映射数据,例如映射类型、页面使用计数器、节点局部性页面计数器(N0 == node0,N1 == node1,...)以及支持映射的内核页面大小(以 KB 为单位)。
请注意,某些内核配置不跟踪较大分配(例如,THP)的一部分页面映射的精确次数。在这些配置中,“mapmax”可能对应于此类较大分配中每个页面的平均映射数。
1.2 内核数据¶
与进程条目类似,内核数据文件提供有关运行内核的信息。用于获取此信息的文件包含在 /proc 中,并在表 1-5 中列出。并非所有文件都将存在于您的系统中。这取决于内核配置和已加载的模块,哪些文件存在,哪些文件缺失。
文件 |
内容 |
---|---|
allocinfo |
内存分配分析信息 |
apm |
高级电源管理信息 |
bootconfig |
从引导配置获得的内核命令行,以及,如果有来自引导加载程序的内核参数,则有一行“# 来自引导加载程序的参数:”,后跟一行包含以“# ”为前缀的那些参数。(5.5) |
buddyinfo |
内核内存分配器信息(请参见文本)(2.5) |
bus |
包含总线特定信息的目录 |
cmdline |
内核命令行,包括来自引导加载程序和嵌入在内核镜像中的 |
cpuinfo |
CPU 相关信息 |
devices |
可用设备(块设备和字符设备) |
dma |
已使用的 DMA 通道 |
filesystems |
支持的文件系统 |
driver |
各种驱动程序在此分组,目前是 rtc (2.4) |
execdomains |
执行域,与安全性相关 (2.4) |
fb |
帧缓冲区设备 (2.4) |
fs |
文件系统参数,目前是 nfs/exports (2.4) |
ide |
包含 IDE 子系统信息的目录 |
interrupts |
中断使用情况 |
iomem |
内存映射 (2.4) |
ioports |
I/O 端口使用情况 |
irq |
IRQ 到 CPU 亲和性的掩码 (2.4)(smp?) |
isapnp |
ISA PnP (即插即用) 信息 (2.4) |
kcore |
内核核心镜像(可以是 ELF 或 A.OUT(在 2.4 中已弃用)) |
kmsg |
内核消息 |
ksyms |
内核符号表 |
loadavg |
|
locks |
内核锁 |
meminfo |
内存信息 |
misc |
杂项 |
modules |
已加载模块列表 |
mounts |
已挂载的文件系统 |
net |
网络信息(见正文) |
pagetypeinfo |
额外的页面分配器信息(见正文)(2.5) |
partitions |
系统已知的分区表 |
pci |
PCI 总线的已弃用信息(新方法 -> /proc/bus/pci/,由 lspci 解耦 (2.4)) |
rtc |
实时时钟 |
scsi |
SCSI 信息(见正文) |
slabinfo |
Slab 池信息 |
softirqs |
软中断使用情况 |
stat |
总体统计 |
swaps |
交换空间利用率 |
sys |
参见第 2 章 |
sysvipc |
SysVIPC 资源信息(msg、sem、shm)(2.4) |
tty |
tty 驱动程序信息 |
uptime |
自启动以来的挂钟时间,所有 CPU 的总空闲时间 |
version |
内核版本 |
video |
视频资源的 bttv 信息 (2.4) |
vmallocinfo |
显示 vmalloced 区域 |
例如,您可以通过查看 /proc/interrupts 文件来检查当前正在使用的中断以及它们的用途
> cat /proc/interrupts
CPU0
0: 8728810 XT-PIC timer
1: 895 XT-PIC keyboard
2: 0 XT-PIC cascade
3: 531695 XT-PIC aha152x
4: 2014133 XT-PIC serial
5: 44401 XT-PIC pcnet_cs
8: 2 XT-PIC rtc
11: 8 XT-PIC i82365
12: 182918 XT-PIC PS/2 Mouse
13: 1 XT-PIC fpu
14: 1232265 XT-PIC ide0
15: 7 XT-PIC ide1
NMI: 0
在 2.4.* 中,该文件中添加了几行 LOC & ERR(这次是 SMP 机器的输出)
> cat /proc/interrupts
CPU0 CPU1
0: 1243498 1214548 IO-APIC-edge timer
1: 8949 8958 IO-APIC-edge keyboard
2: 0 0 XT-PIC cascade
5: 11286 10161 IO-APIC-edge soundblaster
8: 1 0 IO-APIC-edge rtc
9: 27422 27407 IO-APIC-edge 3c503
12: 113645 113873 IO-APIC-edge PS/2 Mouse
13: 0 0 XT-PIC fpu
14: 22491 24012 IO-APIC-edge ide0
15: 2183 2415 IO-APIC-edge ide1
17: 30564 30414 IO-APIC-level eth0
18: 177 164 IO-APIC-level bttv
NMI: 2457961 2457959
LOC: 2457882 2457881
ERR: 2155
在这种情况下,NMI 递增,因为每个定时器中断都会生成一个 NMI(不可屏蔽中断),NMI Watchdog 使用它来检测死锁。
LOC 是每个 CPU 内部 APIC 的本地中断计数器。
如果在 IO-APIC 总线(连接 SMP 系统中 CPU 的总线)中出现错误,ERR 将递增。这意味着检测到了错误,IO-APIC 会自动重试传输,因此这应该不是大问题,但您应该阅读 SMP-FAQ。
在 2.6.2* 中,/proc/interrupts 再次扩展。这次的目标是使 /proc/interrupts 显示系统使用的每个 IRQ 向量,而不仅仅是那些被认为是“最重要”的向量。新的向量是
- THR
当机器检查阈值计数器(通常计算内存或缓存的 ECC 校正错误)超过可配置阈值时引发的中断。仅在某些系统上可用。
- TRM
当 CPU 的温度阈值被超过时,会发生热事件中断。当温度恢复正常时,也可能会生成此中断。
- SPU
伪中断是某些 IO 设备在 APIC 完全处理之前引发然后降低的中断。因此,APIC 会看到中断,但不知道它来自哪个设备。对于这种情况,APIC 将生成 IRQ 向量为 0xff 的中断。这也可能是由芯片组错误引起的。
- RES、CAL、TLB
重新调度、调用和 TLB 刷新中断会根据操作系统的需要从一个 CPU 发送到另一个 CPU。通常,内核开发人员和感兴趣的用户使用它们的统计信息来确定给定类型的中断的发生。
上述 IRQ 向量仅在相关时显示。例如,阈值向量在 x86_64 平台上不存在。当系统是单处理器时,其他向量会被抑制。截至撰写本文时,只有 i386 和 x86_64 平台支持新的 IRQ 向量显示。
在 2.4 中,/proc/irq 目录的引入具有一定的意义。它可以用于设置 IRQ 到 CPU 的亲和性。这意味着您可以将 IRQ“挂钩”到只有一个 CPU,或者排除一个 CPU 处理 IRQ。irq 子目录的内容是每个 IRQ 的一个子目录和两个文件:default_smp_affinity 和 prof_cpu_mask。
例如
> ls /proc/irq/
0 10 12 14 16 18 2 4 6 8 prof_cpu_mask
1 11 13 15 17 19 3 5 7 9 default_smp_affinity
> ls /proc/irq/0/
smp_affinity
smp_affinity 是一个位掩码,您可以在其中指定哪些 CPU 可以处理 IRQ。您可以通过执行以下操作来设置它
> echo 1 > /proc/irq/10/smp_affinity
这意味着只有第一个 CPU 会处理 IRQ,但您也可以 echo 5,这意味着只有第一个和第三个 CPU 可以处理 IRQ。
默认情况下,每个 smp_affinity 文件的内容都相同
> cat /proc/irq/0/smp_affinity
ffffffff
还有另一个接口 smp_affinity_list,它允许指定 CPU 范围而不是位掩码
> cat /proc/irq/0/smp_affinity_list
1024-1031
default_smp_affinity 掩码适用于所有非活动 IRQ,这些 IRQ 是尚未分配/激活的 IRQ,因此缺少 /proc/irq/[0-9]* 目录。
SMP 系统上的节点文件显示使用 IRQ 的设备报告自身所连接到的节点。此硬件局部性信息不包括有关任何可能的驱动程序局部性首选项的信息。
prof_cpu_mask 指定系统范围的分析器要分析哪些 CPU。默认值为 ffffffff(如果只有 32 个 CPU,则为所有 CPU)。
IRQ 的路由方式由 IO-APIC 处理,并且在所有允许处理它的 CPU 之间进行轮询。与往常一样,内核比您拥有更多信息,并且比您做得更好,因此默认值几乎是每个人的最佳选择。[请注意,这仅适用于那些支持“轮询”中断分配的 IO-APIC。]
/proc 中还有三个更重要的子目录:net、scsi 和 sys。一般规则是,这些目录的内容,甚至存在,取决于您的内核配置。如果未启用 SCSI,则 scsi 目录可能不存在。net 也是如此,只有在正在运行的内核中存在网络支持时才存在。
slabinfo 文件提供有关 slab 级别的内存使用情况的信息。在 2.2 版本中,Linux 使用 slab 池进行页面级别以上的内存管理。常用对象有它们自己的 slab 池(例如,网络缓冲区、目录缓存等)。
> cat /proc/buddyinfo
Node 0, zone DMA 0 4 5 4 4 3 ...
Node 0, zone Normal 1 0 0 1 101 8 ...
Node 0, zone HighMem 2 0 0 1 1 0 ...
外部碎片在某些工作负载下是一个问题,buddyinfo 是一个有用的工具,可以帮助诊断这些问题。Buddyinfo 将为您提供关于您可以安全分配多大的区域的线索,或者为什么先前的分配失败。
每一列代表可用特定顺序的页面数。在这种情况下,ZONE_DMA 中有 0 个 2^0*PAGE_SIZE 的块可用,ZONE_DMA 中有 4 个 2^1*PAGE_SIZE 的块,ZONE_NORMAL 中有 101 个 2^4*PAGE_SIZE 的块可用,等等...
有关外部碎片化的更多信息可以在 pagetypeinfo 中找到
> cat /proc/pagetypeinfo
Page block order: 9
Pages per block: 512
Free pages count per migrate type at order 0 1 2 3 4 5 6 7 8 9 10
Node 0, zone DMA, type Unmovable 0 0 0 1 1 1 1 1 1 1 0
Node 0, zone DMA, type Reclaimable 0 0 0 0 0 0 0 0 0 0 0
Node 0, zone DMA, type Movable 1 1 2 1 2 1 1 0 1 0 2
Node 0, zone DMA, type Reserve 0 0 0 0 0 0 0 0 0 1 0
Node 0, zone DMA, type Isolate 0 0 0 0 0 0 0 0 0 0 0
Node 0, zone DMA32, type Unmovable 103 54 77 1 1 1 11 8 7 1 9
Node 0, zone DMA32, type Reclaimable 0 0 2 1 0 0 0 0 1 0 0
Node 0, zone DMA32, type Movable 169 152 113 91 77 54 39 13 6 1 452
Node 0, zone DMA32, type Reserve 1 2 2 2 2 0 1 1 1 1 0
Node 0, zone DMA32, type Isolate 0 0 0 0 0 0 0 0 0 0 0
Number of blocks type Unmovable Reclaimable Movable Reserve Isolate
Node 0, zone DMA 2 0 5 1 0
Node 0, zone DMA32 41 6 967 2 0
内核中避免碎片化的方法是将不同迁移类型的页面分组到称为页面块的相同连续内存区域中。页面块通常是默认大页面的大小,例如 X86-64 上为 2MB。通过基于页面移动的能力对页面进行分组,内核可以在页面块中回收页面以满足高阶分配。
pagetypinfo 以页面块大小的信息开始。然后,它提供与 buddyinfo 相同类型的信息,但按迁移类型细分,并以每种类型的页面块存在多少个的详细信息结束。
如果 min_free_kbytes 已正确调整(来自 libhugetlbfs 的 hugeadm 所做的建议 https://github.com/libhugetlbfs/libhugetlbfs/),则可以估计在给定时间点可以分配的大页面数量。除非内存已被 mlock(),否则所有“可移动”块都应该是可分配的。某些“可回收”块也应该是可分配的,尽管可能需要回收大量文件系统元数据才能实现这一点。
allocinfo¶
提供有关代码库中所有位置的内存分配的信息。代码中的每个分配都由其源文件、行号、模块(如果来自可加载模块)和调用分配的函数标识。报告每个位置分配的字节数和调用次数。第一行指示文件版本,第二行是文件中的标题列表字段。
示例输出。
> tail -n +3 /proc/allocinfo | sort -rn
127664128 31168 mm/page_ext.c:270 func:alloc_page_ext
56373248 4737 mm/slub.c:2259 func:alloc_slab_page
14880768 3633 mm/readahead.c:247 func:page_cache_ra_unbounded
14417920 3520 mm/mm_init.c:2530 func:alloc_large_system_hash
13377536 234 block/blk-mq.c:3421 func:blk_mq_alloc_rqs
11718656 2861 mm/filemap.c:1919 func:__filemap_get_folio
9192960 2800 kernel/fork.c:307 func:alloc_thread_stack_node
4206592 4 net/netfilter/nf_conntrack_core.c:2567 func:nf_ct_alloc_hashtable
4136960 1010 drivers/staging/ctagmod/ctagmod.c:20 [ctagmod] func:ctagmod_start
3940352 962 mm/memory.c:4214 func:alloc_anon_folio
2894464 22613 fs/kernfs/dir.c:615 func:__kernfs_new_node
...
meminfo¶
提供有关内存的分配和利用率的信息。这因体系结构和编译选项而异。此处报告的一些计数器重叠。非重叠计数器报告的内存可能不会加到总内存使用量,并且某些工作负载的差异可能很大。在许多情况下,有其他方法可以使用子系统特定接口找到额外的内存,例如 /proc/net/sockstat 用于 TCP 内存分配。
示例输出。您可能没有所有这些字段。
> cat /proc/meminfo
MemTotal: 32858820 kB
MemFree: 21001236 kB
MemAvailable: 27214312 kB
Buffers: 581092 kB
Cached: 5587612 kB
SwapCached: 0 kB
Active: 3237152 kB
Inactive: 7586256 kB
Active(anon): 94064 kB
Inactive(anon): 4570616 kB
Active(file): 3143088 kB
Inactive(file): 3015640 kB
Unevictable: 0 kB
Mlocked: 0 kB
SwapTotal: 0 kB
SwapFree: 0 kB
Zswap: 1904 kB
Zswapped: 7792 kB
Dirty: 12 kB
Writeback: 0 kB
AnonPages: 4654780 kB
Mapped: 266244 kB
Shmem: 9976 kB
KReclaimable: 517708 kB
Slab: 660044 kB
SReclaimable: 517708 kB
SUnreclaim: 142336 kB
KernelStack: 11168 kB
PageTables: 20540 kB
SecPageTables: 0 kB
NFS_Unstable: 0 kB
Bounce: 0 kB
WritebackTmp: 0 kB
CommitLimit: 16429408 kB
Committed_AS: 7715148 kB
VmallocTotal: 34359738367 kB
VmallocUsed: 40444 kB
VmallocChunk: 0 kB
Percpu: 29312 kB
EarlyMemtestBad: 0 kB
HardwareCorrupted: 0 kB
AnonHugePages: 4149248 kB
ShmemHugePages: 0 kB
ShmemPmdMapped: 0 kB
FileHugePages: 0 kB
FilePmdMapped: 0 kB
CmaTotal: 0 kB
CmaFree: 0 kB
Unaccepted: 0 kB
Balloon: 0 kB
HugePages_Total: 0
HugePages_Free: 0
HugePages_Rsvd: 0
HugePages_Surp: 0
Hugepagesize: 2048 kB
Hugetlb: 0 kB
DirectMap4k: 401152 kB
DirectMap2M: 10008576 kB
DirectMap1G: 24117248 kB
- MemTotal
总可用 RAM(即物理 RAM 减去一些保留位和内核二进制代码)
- MemFree
总空闲 RAM。在高内存系统中,是 LowFree+HighFree 的总和
- MemAvailable
无需交换即可启动新应用程序的可用内存估计值。从 MemFree、SReclaimable、文件 LRU 列表的大小以及每个区域的低水位线计算得出。该估计考虑了系统需要一些页面缓存才能良好运行,并且由于某些项目正在使用中,并非所有可回收的 slab 都将可回收。这些因素的影响因系统而异。
- Buffers
用于原始磁盘块的相对临时存储不应变得非常大(大约 20MB 左右)
- Cached
从磁盘读取的文件(页面缓存)以及 tmpfs 和 shmem 的内存缓存。不包括 SwapCached。
- SwapCached
曾经被交换出去的内存,又被交换回来,但仍然在交换文件中(如果需要内存,则不需要再次交换出去,因为它已经在交换文件中。这节省了 I/O)
- Active
最近使用的内存,通常除非绝对必要,否则不会回收。
- Inactive
最近较少使用的内存。它更有资格被回收用于其他目的
- Unevictable
为用户空间分配的无法回收的内存,例如 mlocked 页面、ramfs 后备页面、秘密 memfd 页面等。
- Mlocked
用 mlock() 锁定的内存。
- HighTotal, HighFree
高内存是物理内存中 ~860MB 以上的所有内存。高内存区域供用户空间程序或页面缓存使用。内核必须使用技巧来访问此内存,使其访问速度低于低内存。
- LowTotal, LowFree
低内存是可以用于高内存可以使用的所有东西的内存,但它也可以供内核用于其自己的数据结构。在许多其他方面,Slab 中的所有东西都是从这里分配的。当您耗尽低内存时,会发生糟糕的事情。
- SwapTotal
可用的交换空间总量
- SwapFree
已从 RAM 中驱逐的内存,暂时位于磁盘上
- Zswap
zswap 后端消耗的内存(压缩大小)
- Zswapped
存储在 zswap 中的匿名内存量(原始大小)
- Dirty
等待写回磁盘的内存
- Writeback
正在积极写回磁盘的内存
- AnonPages
映射到用户空间页表中的非文件后备页面。请注意,某些内核配置可能会将较大分配中的所有页面(例如,THP)视为“已映射”,只要映射了单个页面即可。
- Mapped
已进行 mmap 的文件,例如库。请注意,某些内核配置可能会将较大分配中的所有页面(例如,THP)视为“已映射”,只要映射了单个页面即可。
- Shmem
共享内存 (shmem) 和 tmpfs 使用的总内存
- KReclaimable
内核将在内存压力下尝试回收的内核分配。包括 SReclaimable(如下)和其他具有收缩器的直接分配。
- Slab
内核数据结构缓存
- SReclaimable
Slab 的一部分,可以回收,例如缓存
- SUnreclaim
Slab 的一部分,无法在内存压力下回收
- KernelStack
所有任务的内核堆栈消耗的内存
- PageTables
用户空间页表消耗的内存
- SecPageTables
二级页表消耗的内存,目前包括 x86 和 arm64 上的 KVM mmu 和 IOMMU 分配。
- NFS_Unstable
始终为零。以前计算的已写入服务器但尚未提交到稳定存储的页面。
- Bounce
用于块设备“反弹缓冲区”的内存
- WritebackTmp
FUSE 用于临时写回缓冲区的内存
- CommitLimit
基于过度提交率 ('vm.overcommit_ratio'),这是当前可用于在系统上分配的内存总量。只有启用严格的过度提交记帐('vm.overcommit_memory' 中的模式 2)时,才遵守此限制。
CommitLimit 使用以下公式计算
CommitLimit = ([total RAM pages] - [total huge TLB pages]) * overcommit_ratio / 100 + [total swap pages]
例如,在物理 RAM 为 1G、交换空间为 7G 且 vm.overcommit_ratio 为 30 的系统上,将产生 7.3G 的 CommitLimit。
有关更多详细信息,请参阅 mm/overcommit-accounting 中的内存过度提交文档。
- Committed_AS
当前在系统上分配的内存量。提交的内存是进程分配的所有内存的总和,即使它们尚未“使用”它。malloc() 1G 内存但仅触摸其中 300M 的进程将显示为使用 1G。此 1G 内存已由 VM“提交”并可随时被分配应用程序使用。如果在系统上启用了严格的过度提交('vm.overcommit_memory' 中的模式 2),则不允许超过 CommitLimit(上面详述)的分配。如果需要保证进程在成功分配内存后不会因缺少内存而失败,这将非常有用。
- VmallocTotal
vmalloc 虚拟地址空间的总大小
- VmallocUsed
已使用的 vmalloc 区域量
- VmallocChunk
空闲的 vmalloc 区域的最大连续块
- Percpu
分配给用于支持 percpu 分配的 percpu 分配器的内存。此统计信息不包括元数据的成本。
- EarlyMemtestBad
在早期内存测试中识别为已损坏的 RAM/内存量(以 kB 为单位)。如果未运行内存测试,则根本不会显示此字段。大小永远不会向下舍入到 0 kB。这意味着如果报告了 0 kB,您可以安全地假设至少有一个内存测试通过,并且没有通过找到 RAM 的单个错误字节。
- HardwareCorrupted
内核识别为已损坏的 RAM/内存量(以 KB 为单位)。
- AnonHugePages
映射到用户空间页表中的非文件后备大页面
- ShmemHugePages
使用大页面分配的共享内存 (shmem) 和 tmpfs 使用的内存
- ShmemPmdMapped
使用大页面映射到用户空间的共享内存
- FileHugePages
使用大页面分配的文件系统数据(页面缓存)使用的内存
- FilePmdMapped
使用大页面映射到用户空间的页面缓存
- CmaTotal
为连续内存分配器 (CMA) 保留的内存
- CmaFree
CMA 保留中的剩余空闲内存
- Unaccepted
访客尚未接受的内存
- Balloon
VM Balloon Drivers 返回给主机的内存
- HugePages_Total, HugePages_Free, HugePages_Rsvd, HugePages_Surp, Hugepagesize, Hugetlb
请参阅 HugeTLB 页面。
- DirectMap4k, DirectMap2M, DirectMap1G
内核 RAM 身份映射中使用的页表大小细分
vmallocinfo¶
提供有关 vmalloced/vmaped 区域的信息。每个区域一行,包含区域的虚拟地址范围、大小(以字节为单位)、创建者的调用者信息以及可选信息(具体取决于区域的类型)
> cat /proc/vmallocinfo
0xffffc20000000000-0xffffc20000201000 2101248 alloc_large_system_hash+0x204 ...
/0x2c0 pages=512 vmalloc N0=128 N1=128 N2=128 N3=128
0xffffc20000201000-0xffffc20000302000 1052672 alloc_large_system_hash+0x204 ...
/0x2c0 pages=256 vmalloc N0=64 N1=64 N2=64 N3=64
0xffffc20000302000-0xffffc20000304000 8192 acpi_tb_verify_table+0x21/0x4f...
phys=7fee8000 ioremap
0xffffc20000304000-0xffffc20000307000 12288 acpi_tb_verify_table+0x21/0x4f...
phys=7fee7000 ioremap
0xffffc2000031d000-0xffffc2000031f000 8192 init_vdso_vars+0x112/0x210
0xffffc2000031f000-0xffffc2000032b000 49152 cramfs_uncompress_init+0x2e ...
/0x80 pages=11 vmalloc N0=3 N1=3 N2=2 N3=3
0xffffc2000033a000-0xffffc2000033d000 12288 sys_swapon+0x640/0xac0 ...
pages=2 vmalloc N1=2
0xffffc20000347000-0xffffc2000034c000 20480 xt_alloc_table_info+0xfe ...
/0x130 [x_tables] pages=4 vmalloc N0=4
0xffffffffa0000000-0xffffffffa000f000 61440 sys_init_module+0xc27/0x1d00 ...
pages=14 vmalloc N2=14
0xffffffffa000f000-0xffffffffa0014000 20480 sys_init_module+0xc27/0x1d00 ...
pages=4 vmalloc N1=4
0xffffffffa0014000-0xffffffffa0017000 12288 sys_init_module+0xc27/0x1d00 ...
pages=2 vmalloc N1=2
0xffffffffa0017000-0xffffffffa0022000 45056 sys_init_module+0xc27/0x1d00 ...
pages=10 vmalloc N0=10
softirqs¶
提供自启动时间以来,每个 CPU 服务软中断处理程序的计数。
> cat /proc/softirqs
CPU0 CPU1 CPU2 CPU3
HI: 0 0 0 0
TIMER: 27166 27120 27097 27034
NET_TX: 0 0 0 17
NET_RX: 42 0 0 39
BLOCK: 0 0 107 1121
TASKLET: 0 0 0 290
SCHED: 27035 26983 26971 26746
HRTIMER: 0 0 0 0
RCU: 1678 1769 2178 2250
1.3 /proc/net 中的网络信息¶
子目录 /proc/net 遵循通常的模式。表 1-8 显示了如果您将内核配置为支持 IPv6,您将获得的额外值。表 1-9 列出了文件及其含义。
文件 |
内容 |
---|---|
udp6 |
UDP 套接字 (IPv6) |
tcp6 |
TCP 套接字 (IPv6) |
raw6 |
原始设备统计信息 (IPv6) |
igmp6 |
此主机加入的 IP 组播地址 (IPv6) |
if_inet6 |
IPv6 接口地址列表 |
ipv6_route |
IPv6 的内核路由表 |
rt6_stats |
全局 IPv6 路由表统计信息 |
sockstat6 |
套接字统计信息 (IPv6) |
snmp6 |
Snmp 数据 (IPv6) |
文件 |
内容 |
---|---|
arp |
内核 ARP 表 |
dev |
具有统计信息的网络设备 |
dev_mcast |
设备正在侦听的 Layer2 组播组(接口索引、标签、引用数、绑定地址数)。 |
dev_stat |
网络设备状态 |
ip_fwchains |
防火墙链链路 |
ip_fwnames |
防火墙链名称 |
ip_masq |
包含伪装表的目录 |
ip_masquerade |
主要伪装表 |
netstat |
网络统计信息 |
raw |
原始设备统计信息 |
route |
内核路由表 |
rpc |
包含 rpc 信息的目录 |
rt_cache |
路由缓存 |
snmp |
SNMP 数据 |
sockstat |
套接字统计信息 |
softnet_stat |
联机 CPU 的每个 CPU 的传入数据包队列统计信息 |
tcp |
TCP 套接字 |
udp |
UDP 套接字 |
unix |
UNIX 域套接字 |
wireless |
无线接口数据 (Wavelan 等) |
igmp |
此主机加入的 IP 组播地址 |
psched |
全局数据包调度程序参数。 |
netlink |
PF_NETLINK 套接字列表 |
ip_mr_vifs |
组播虚拟接口列表 |
ip_mr_cache |
组播路由缓存列表 |
您可以使用此信息来查看系统中可用的网络设备以及通过这些设备路由的流量
> cat /proc/net/dev
Inter-|Receive |[...
face |bytes packets errs drop fifo frame compressed multicast|[...
lo: 908188 5596 0 0 0 0 0 0 [...
ppp0:15475140 20721 410 0 0 410 0 0 [...
eth0: 614530 7085 0 0 0 0 0 1 [...
...] Transmit
...] bytes packets errs drop fifo colls carrier compressed
...] 908188 5596 0 0 0 0 0 0
...] 1375103 17405 0 0 0 0 0 0
...] 1703981 5535 0 0 0 3 0 0
此外,每个通道绑定接口都有自己的目录。例如,bond0 设备将有一个名为 /proc/net/bond0/ 的目录。它将包含特定于该绑定的信息,例如绑定的当前从属设备、从属设备的链接状态以及从属设备链接失败的次数。
1.4 SCSI 信息¶
如果您的系统中有一个 SCSI 或 ATA 主机适配器,您将在 /proc/scsi 中找到一个以该适配器的驱动程序命名的子目录。您还将在 /proc/scsi 中看到所有已识别的 SCSI 设备列表
>cat /proc/scsi/scsi
Attached devices:
Host: scsi0 Channel: 00 Id: 00 Lun: 00
Vendor: IBM Model: DGHS09U Rev: 03E0
Type: Direct-Access ANSI SCSI revision: 03
Host: scsi0 Channel: 00 Id: 06 Lun: 00
Vendor: PIONEER Model: CD-ROM DR-U06S Rev: 1.04
Type: CD-ROM ANSI SCSI revision: 02
以驱动程序命名的目录为系统中找到的每个适配器都有一个文件。这些文件包含有关控制器的信息,包括使用的 IRQ 和 IO 地址范围。显示的信息量取决于您使用的适配器。该示例显示了 Adaptec AHA-2940 SCSI 适配器的输出
> cat /proc/scsi/aic7xxx/0
Adaptec AIC7xxx driver version: 5.1.19/3.2.4
Compile Options:
TCQ Enabled By Default : Disabled
AIC7XXX_PROC_STATS : Disabled
AIC7XXX_RESET_DELAY : 5
Adapter Configuration:
SCSI Adapter: Adaptec AHA-294X Ultra SCSI host adapter
Ultra Wide Controller
PCI MMAPed I/O Base: 0xeb001000
Adapter SEEPROM Config: SEEPROM found and used.
Adaptec SCSI BIOS: Enabled
IRQ: 10
SCBs: Active 0, Max Active 2,
Allocated 15, HW 16, Page 255
Interrupts: 160328
BIOS Control Word: 0x18b6
Adapter Control Word: 0x005b
Extended Translation: Enabled
Disconnect Enable Flags: 0xffff
Ultra Enable Flags: 0x0001
Tag Queue Enable Flags: 0x0000
Ordered Queue Tag Flags: 0x0000
Default Tag Queue Depth: 8
Tagged Queue By Device array for aic7xxx host instance 0:
{255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255}
Actual queue depth per device for aic7xxx host instance 0:
{1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1}
Statistics:
(scsi0:0:0:0)
Device using Wide/Sync transfers at 40.0 MByte/sec, offset 8
Transinfo settings: current(12/8/1/0), goal(12/8/1/0), user(12/15/1/0)
Total transfers 160151 (74577 reads and 85574 writes)
(scsi0:0:6:0)
Device using Narrow/Sync transfers at 5.0 MByte/sec, offset 15
Transinfo settings: current(50/15/0/0), goal(50/15/0/0), user(50/15/0/0)
Total transfers 0 (0 reads and 0 writes)
1.5 /proc/parport 中的并行端口信息¶
目录 /proc/parport 包含有关系统并行端口的信息。它为每个端口都有一个子目录,以端口号 (0,1,2,...) 命名。
这些目录包含表 1-10 中显示的四个文件。
文件 |
内容 |
---|---|
autoprobe |
已获得的任何 IEEE-1284 设备 ID 信息。 |
devices |
使用该端口的设备驱动程序列表。设备当前正在使用该端口的名称旁边会出现一个 +(可能不会出现在任何设备旁边)。 |
hardware |
并行端口的基本地址、IRQ 线和 DMA 通道。 |
irq |
parport 正在用于该端口的 IRQ。这是一个单独的文件,允许您通过写入一个新值(IRQ 号或 none)来更改它。 |
1.6 /proc/tty 中的 TTY 信息¶
有关可用和实际使用的 tty 的信息可以在目录 /proc/tty 中找到。您将在此目录中找到驱动程序和线路规程的条目,如表 1-11 所示。
文件 |
内容 |
---|---|
drivers |
驱动程序列表及其使用情况 |
ldiscs |
注册的线路规程 |
driver/serial |
单个 tty 线路的使用统计信息和状态 |
要查看当前正在使用的 tty,您可以简单地查看文件 /proc/tty/drivers
> cat /proc/tty/drivers
pty_slave /dev/pts 136 0-255 pty:slave
pty_master /dev/ptm 128 0-255 pty:master
pty_slave /dev/ttyp 3 0-255 pty:slave
pty_master /dev/pty 2 0-255 pty:master
serial /dev/cua 5 64-67 serial:callout
serial /dev/ttyS 4 64-67 serial
/dev/tty0 /dev/tty0 4 0 system:vtmaster
/dev/ptmx /dev/ptmx 5 2 system
/dev/console /dev/console 5 1 system:console
/dev/tty /dev/tty 5 0 system:/dev/tty
unknown /dev/tty 4 1-63 console
1.7 /proc/stat 中的其他内核统计信息¶
有关内核活动的各种信息可在 /proc/stat 文件中找到。此文件中报告的所有数字都是自系统首次启动以来的聚合值。要快速查看,只需 cat 该文件
> cat /proc/stat
cpu 237902850 368826709 106375398 1873517540 1135548 0 14507935 0 0 0
cpu0 60045249 91891769 26331539 468411416 495718 0 5739640 0 0 0
cpu1 59746288 91759249 26609887 468860630 312281 0 4384817 0 0 0
cpu2 59489247 92985423 26904446 467808813 171668 0 2268998 0 0 0
cpu3 58622065 92190267 26529524 468436680 155879 0 2114478 0 0 0
intr 8688370575 8 3373 0 0 0 0 0 0 1 40791 0 0 353317 0 0 0 0 224789828 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 190974333 41958554 123983334 43 0 224593 0 0 0 <more 0's deleted>
ctxt 22848221062
btime 1605316999
processes 746787147
procs_running 2
procs_blocked 0
softirq 12121874454 100099120 3938138295 127375644 2795979 187870761 0 173808342 3072582055 52608 224184354
第一个“cpu”行聚合了所有其他“cpuN”行中的数字。这些数字标识了 CPU 花费在执行不同类型工作上的时间量。时间单位为 USER_HZ(通常为百分之一秒)。从左到右,各列的含义如下
user:在用户模式下执行的普通进程
nice:在用户模式下执行的 niced 进程
system:在内核模式下执行的进程
idle:无所事事
iowait:总之,iowait 代表等待 I/O 完成。但存在几个问题
CPU 将不会等待 I/O 完成,iowait 是任务等待 I/O 完成的时间。当 CPU 由于未完成的任务 I/O 而进入空闲状态时,将在该 CPU 上调度另一个任务。
在多核 CPU 中,等待 I/O 完成的任务不在任何 CPU 上运行,因此每个 CPU 的 iowait 难以计算。
/proc/stat 中 iowait 字段的值将在某些条件下减少。
因此,通过从 /proc/stat 读取 iowait 是不可靠的。
irq:服务中断
softirq:服务软中断
steal:非自愿等待
guest:运行普通访客
guest_nice:运行 niced 访客
“intr”行提供自启动时间以来服务的中断计数,用于每个可能的系统中断。第一列是服务的所有中断的总数,包括未编号的特定于体系结构的中断;每个后续列是该特定编号中断的总数。未编号的中断不会显示,只会汇总到总数中。
“ctxt”行给出所有 CPU 上的上下文切换总数。
“btime”行给出系统启动的时间,以自 Unix 纪元以来的秒数表示。
“processes”行给出创建的进程和线程数,其中包括(但不限于)通过调用 fork() 和 clone() 系统调用创建的进程和线程。
“procs_running”行给出正在运行或准备运行的线程总数(即,可运行线程的总数)。
“procs_blocked”行给出当前被阻止的进程数,等待 I/O 完成。
“softirq”行提供自启动时间以来服务的软中断计数,用于每个可能的系统软中断。第一列是服务的所有软中断的总数;每个后续列是该特定软中断的总数。
1.8 Ext4 文件系统参数¶
有关已挂载的 ext4 文件系统的信息可以在 /proc/fs/ext4 中找到。每个已挂载的文件系统将在 /proc/fs/ext4 中基于其设备名称(即,/proc/fs/ext4/hdc 或 /proc/fs/ext4/sda9 或 /proc/fs/ext4/dm-0)都有一个目录。表 1-12 显示了每个按设备目录中的文件。
文件 |
内容 |
mb_groups |
多块分配器空闲块伙伴缓存的详细信息 |
1.9 /proc/consoles¶
显示已注册的系统控制台线路。
要查看当前用于系统控制台 /dev/console 的字符设备线路,您可以简单地查看文件 /proc/consoles
> cat /proc/consoles
tty0 -WU (ECp) 4:7
ttyS0 -W- (Ep) 4:64
各列如下
device |
设备名称 |
---|---|
operations |
|
flags |
|
major:minor |
设备的主设备号和次设备号,用冒号分隔 |
总结¶
/proc 文件系统提供有关运行系统的信息。它不仅允许访问进程数据,还允许您通过读取层次结构中的文件来请求内核状态。
/proc 的目录结构反映了信息的类型,并使查找特定数据变得容易(如果不是显而易见)。
第 2 章:修改系统参数¶
在本章中¶
通过写入 /proc/sys 中找到的文件来修改内核参数
探索修改某些参数的文件
审查 /proc/sys 文件树
/proc 中非常有趣的一部分是目录 /proc/sys。这不仅是信息的来源,还允许您更改内核中的参数。尝试这样做时要非常小心。您可以优化系统,但也可能导致系统崩溃。永远不要在生产系统上更改内核参数。设置开发机器并进行测试,以确保一切按您希望的方式工作。一旦出现错误,您可能别无选择,只能重新启动机器。
要更改值,只需将新值 echo 到文件中。您需要 root 权限才能执行此操作。您可以创建自己的启动脚本来在每次系统启动时执行此操作。
/proc/sys 中的文件可用于微调和监视 Linux 内核操作中的杂项和常规事项。由于某些文件可能会无意中扰乱您的系统,因此建议在实际进行调整之前阅读文档和源代码。在任何情况下,写入这些文件时都要非常小心。/proc 中的条目在 2.1.* 和 2.2 内核之间可能会略有变化,因此如果有任何疑问,请查看目录 linux/Documentation 中的内核文档。本章主要基于 pre 2.2 内核中包含的文档,并在 Linux 内核的 2.2.1 版本中成为其中的一部分。
请参阅:Documentation/admin-guide/sysctl/ 目录以获取这些条目的描述。
总结¶
内核行为的某些方面可以在运行时修改,而无需重新编译内核,甚至无需重新启动系统。/proc/sys 树中的文件不仅可以读取,还可以修改。您可以使用 echo 命令将值写入这些文件,从而更改内核的默认设置。
第 3 章:按进程参数¶
3.1 /proc/<pid>/oom_adj & /proc/<pid>/oom_score_adj- 调整 oom-killer 分数¶
这些文件可用于调整 badness 启发式算法,该算法用于选择在内存不足 (oom) 条件下要杀死的进程。
badness 启发式算法为每个候选任务分配一个值,范围从 0(从不杀死)到 1000(始终杀死),以确定哪个进程是目标。这些单位大致是在该范围内的一部分,进程可以根据对其当前内存和交换使用情况的估计来分配允许的内存。例如,如果一个任务正在使用所有允许的内存,它的 badness 分数将为 1000。如果它正在使用其允许内存的一半,它的分数将为 500。
“允许”内存量取决于调用 oom killer 的上下文。如果是由于分配任务的 cpuset 分配的内存已耗尽,则允许的内存表示分配给该 cpuset 的内存集。如果是由于 mempolicy 的节点已耗尽,则允许的内存表示 mempolicy 节点的集合。如果是由于达到了内存限制(或交换限制),则允许的内存是该配置的限制。最后,如果是由于整个系统内存不足,则允许的内存表示所有可分配的资源。
/proc/<pid>/oom_score_adj 的值添加到 badness 分数中,然后再用于确定要杀死的任务。可接受的值范围从 -1000 (OOM_SCORE_ADJ_MIN) 到 +1000 (OOM_SCORE_ADJ_MAX)。这允许用户空间通过始终首选某个任务或完全禁用它来极化 oom 杀死首选项。可能的最低值 -1000 等效于完全禁用该任务的 oom 杀死,因为它将始终报告 badness 分数为 0。
因此,用户空间可以非常简单地定义每个任务要考虑的内存量。例如,将 /proc/<pid>/oom_score_adj 值设置为 +500,大致相当于允许共享相同系统、cpuset、内存策略或内存控制器资源的其余任务使用至少多 50% 的内存。另一方面,将值设置为 -500,大致相当于从任务允许的内存中扣除 50% 的内存,使其不被视为针对该任务的评分。
为了与以前的内核向后兼容,/proc/<pid>/oom_adj 也可以用于调整 badness 分数。其可接受的值范围为 -16 (OOM_ADJUST_MIN) 到 +15 (OOM_ADJUST_MAX),特殊值 -17 (OOM_DISABLE) 用于完全禁用该任务的 OOM killing。其值与 /proc/<pid>/oom_score_adj 线性缩放。
/proc/<pid>/oom_score_adj 的值可能不会降低到 CAP_SYS_RESOURCE 进程设置的最后一个值以下。要将值降低到更低的值,需要 CAP_SYS_RESOURCE 权限。
3.2 /proc/<pid>/oom_score - 显示当前 OOM killer 分数¶
该文件可用于检查 OOM killer 为任何给定的 <pid> 使用的当前分数。将其与 /proc/<pid>/oom_score_adj 一起使用,以调整在内存不足的情况下应杀死哪个进程。
请注意,导出的值包括 oom_score_adj,因此实际上范围为 [0,2000]。
3.3 /proc/<pid>/io - 显示 IO 统计字段¶
此文件包含每个正在运行的进程的 IO 统计信息。
示例¶
test:/tmp # dd if=/dev/zero of=/tmp/test.dat &
[1] 3828
test:/tmp # cat /proc/3828/io
rchar: 323934931
wchar: 323929600
syscr: 632687
syscw: 632675
read_bytes: 0
write_bytes: 323932160
cancelled_write_bytes: 0
描述¶
rchar¶
I/O 计数器:读取的字符数。此任务导致从存储读取的字节数。这只是此进程传递给 read() 和 pread() 的字节数之和。它包括诸如 tty IO 之类的东西,并且不受是否需要实际物理磁盘 IO 的影响(读取可能已从 pagecache 满足)。
wchar¶
I/O 计数器:写入的字符数。此任务已导致或将导致写入磁盘的字节数。与 rchar 一样,这里也适用类似的注意事项。
syscr¶
I/O 计数器:读取 syscalls。尝试计算读取 I/O 操作的数量,即诸如 read() 和 pread() 之类的 syscalls。
syscw¶
I/O 计数器:写入 syscalls。尝试计算写入 I/O 操作的数量,即诸如 write() 和 pwrite() 之类的 syscalls。
read_bytes¶
I/O 计数器:读取的字节数。尝试计算此进程真正导致从存储层获取的字节数。在 submit_bio()
级别完成,因此对于块支持的文件系统来说是准确的。<请稍后添加关于 NFS 和 CIFS 的状态>
write_bytes¶
I/O 计数器:写入的字节数。尝试计算此进程导致发送到存储层的字节数。这在页面变脏时完成。
cancelled_write_bytes¶
这里最大的不准确之处是 truncate。如果一个进程向一个文件写入 1MB,然后删除该文件,实际上不会执行任何写出操作。但它将被计为导致 1MB 的写入。换句话说:此进程通过截断 pagecache 导致未发生的字节数。一个任务也可以导致“负”IO。如果此任务截断了一些脏 pagecache,那么另一个任务已计入的(在其 write_bytes 中)一些 IO 将不会发生。我们可以直接从截断任务的 write_bytes 中减去它,但这样做会丢失信息。
注意
在其当前的实现状态下,这在 32 位机器上有点竞争:如果进程 A 读取进程 B 的 /proc/pid/io,而进程 B 正在更新这些 64 位计数器之一,则进程 A 可能会看到中间结果。
有关此的更多信息可以在 Documentation/accounting 中的 taskstats 文档中找到。
3.4 /proc/<pid>/coredump_filter - Core dump 过滤设置¶
当转储进程时,所有匿名内存都会写入 core 文件,只要 core 文件的大小没有限制。但是有时我们不想转储某些内存段,例如,巨大的共享内存或 DAX。相反,有时我们希望将文件支持的内存段保存到 core 文件中,而不仅仅是个别文件。
/proc/<pid>/coredump_filter 允许您自定义在转储 <pid> 进程时将转储哪些内存段。 coredump_filter 是内存类型的位掩码。如果设置了位掩码的一位,则会转储相应内存类型的内存段,否则不会转储。
支持以下 9 种内存类型
(bit 0) 匿名私有内存
(bit 1) 匿名共享内存
(bit 2) 文件支持的私有内存
(bit 3) 文件支持的共享内存
(bit 4) 文件支持的私有内存区域中的 ELF 标头页面(仅当位 2 被清除时才有效)
(bit 5) hugetlb 私有内存
(bit 6) hugetlb 共享内存
(bit 7) DAX 私有内存
(bit 8) DAX 共享内存
请注意,MMIO 页面(例如帧缓冲区)永远不会转储,并且 vDSO 页面始终会被转储,而与位掩码状态无关。
请注意,位 0-4 不影响 hugetlb 或 DAX 内存。 hugetlb 内存仅受位 5-6 影响,DAX 仅受位 7-8 影响。
coredump_filter 的默认值为 0x33;这意味着所有匿名内存段、ELF 标头页面和 hugetlb 私有内存都会被转储。
如果您不想转储附加到 pid 1234 的所有共享内存段,请将 0x31 写入进程的 proc 文件
$ echo 0x31 > /proc/1234/coredump_filter
创建新进程时,进程会从其父进程继承位掩码状态。在程序运行之前设置 coredump_filter 很有用。例如
$ echo 0x7 > /proc/self/coredump_filter
$ ./some_program
3.5 /proc/<pid>/mountinfo - 关于挂载的信息¶
此文件包含以下形式的行
36 35 98:0 /mnt1 /mnt2 rw,noatime master:1 - ext3 /dev/root rw,errors=continue
(1)(2)(3) (4) (5) (6) (n…m) (m+1)(m+2) (m+3) (m+4)
(1) mount ID: unique identifier of the mount (may be reused after umount)
(2) parent ID: ID of parent (or of self for the top of the mount tree)
(3) major:minor: value of st_dev for files on filesystem
(4) root: root of the mount within the filesystem
(5) mount point: mount point relative to the process's root
(6) mount options: per mount options
(n…m) optional fields: zero or more fields of the form "tag[:value]"
(m+1) separator: marks the end of the optional fields
(m+2) filesystem type: name of filesystem of the form "type[.subtype]"
(m+3) mount source: filesystem specific information or "none"
(m+4) super options: per super block options
解析器应忽略所有无法识别的可选字段。当前可能的可选字段有
shared:X |
挂载在对等组 X 中共享 |
master:X |
挂载是从对等组 X 的从属 |
propagate_from:X |
挂载是从属,并从对等组 X 接收传播 [1] |
unbindable |
挂载是不可绑定的 |
有关挂载传播的更多信息,请参见
3.6 /proc/<pid>/comm & /proc/<pid>/task/<tid>/comm¶
这些文件提供了一种访问任务 comm 值的方法。它还允许任务设置其自身或其线程兄弟的 comm 值。与 cmdline 值相比,comm 值的大小有限,因此写入任何长于内核 TASK_COMM_LEN(当前为 16 个字符,包括 NUL 终止符)的内容将导致 comm 值被截断。
3.7 /proc/<pid>/task/<tid>/children - 关于任务子进程的信息¶
此文件提供了一种快速检索由 <pid>/<tid> 对指向的任务的第一级子进程 pid 的方法。格式是以空格分隔的 pid 流。
请注意这里的“第一级”——如果一个子进程有它自己的子进程,它们将不会在这里列出;需要读取 /proc/<children-pid>/task/<tid>/children 以获得后代。
由于此接口旨在快速且廉价,因此它不保证提供精确的结果,并且某些子进程可能会被跳过,特别是如果它们在我们打印其 pid 后立即退出,因此如果需要精确的结果,则需要停止或冻结正在检查的进程。
3.8 /proc/<pid>/fdinfo/<fd> - 关于打开的文件的信息¶
此文件提供与打开的文件关联的信息。常规文件至少有四个字段——“pos”、“flags”、“mnt_id” 和 “ino”。“pos” 表示打开的文件中当前的偏移量(十进制形式)[有关详细信息,请参见 lseek(2)],“flags” 表示创建文件时使用的八进制 O_xxx 掩码 [有关详细信息,请参见 open(2)],“mnt_id” 表示包含打开的文件的文件系统的挂载 ID [有关详细信息,请参见 3.5 /proc/<pid>/mountinfo]。 “ino” 表示文件的 inode 编号。
典型的输出是
pos: 0
flags: 0100002
mnt_id: 19
ino: 63107
与文件描述符关联的所有锁也会显示在其 fdinfo 中
lock: 1: FLOCK ADVISORY WRITE 359 00:13:11691 0 EOF
诸如 eventfd、fsnotify、signalfd、epoll 之类的文件除了常规的 pos/flags 对之外,还提供特定于它们所代表的对象的其他信息。
Eventfd 文件¶
pos: 0
flags: 04002
mnt_id: 9
ino: 63107
eventfd-count: 5a
其中 “eventfd-count” 是计数器的十六进制值。
Signalfd 文件¶
pos: 0
flags: 04002
mnt_id: 9
ino: 63107
sigmask: 0000000000000200
其中 “sigmask” 是与文件关联的信号掩码的十六进制值。
Epoll 文件¶
pos: 0
flags: 02
mnt_id: 9
ino: 63107
tfd: 5 events: 1d data: ffffffffffffffff pos:0 ino:61af sdev:7
其中 “tfd” 是目标文件描述符编号(十进制形式),“events” 是被监视的事件掩码,“data” 是与目标关联的数据 [有关更多详细信息,请参见 epoll(7)]。
“pos” 是目标文件的当前偏移量(十进制形式)[参见 lseek(2)],“ino” 和 “sdev” 是目标文件所在的 inode 和设备编号,均以十六进制格式表示。
Fsnotify 文件¶
对于 inotify 文件,格式如下
pos: 0
flags: 02000000
mnt_id: 9
ino: 63107
inotify wd:3 ino:9e7e sdev:800013 mask:800afce ignored_mask:0 fhandle-bytes:8 fhandle-type:1 f_handle:7e9e0000640d1b6d
其中 “wd” 是监视描述符(十进制形式),即目标文件描述符编号,“ino” 和 “sdev” 是目标文件所在的 inode 和设备, “mask” 是事件掩码,所有这些都以十六进制形式表示 [有关更多详细信息,请参见 inotify(7)]。
如果内核是用 exportfs 支持构建的,则目标文件的路径将被编码为文件句柄。文件句柄由三个字段 “fhandle-bytes”、“fhandle-type” 和 “f_handle” 提供,所有这些都以十六进制格式表示。
如果内核在没有 exportfs 支持的情况下构建,则文件句柄将不会打印出来。
如果尚未附加任何 inotify 标记,则将省略 “inotify” 行。
对于 fanotify 文件,格式为
pos: 0
flags: 02
mnt_id: 9
ino: 63107
fanotify flags:10 event-flags:0
fanotify mnt_id:12 mflags:40 mask:38 ignored_mask:40000003
fanotify ino:4f969 sdev:800013 mflags:0 mask:3b ignored_mask:40000000 fhandle-bytes:8 fhandle-type:1 f_handle:69f90400c275b5b4
其中 fanotify “flags” 和 “event-flags” 是 fanotify_init 调用中使用的值,“mnt_id” 是挂载点标识符,“mflags” 是与标记关联的标志的值,这些标志与事件掩码分开跟踪。“ino” 和 “sdev” 是目标 inode 和设备,“mask” 是事件掩码,“ignored_mask” 是要忽略的事件的掩码。所有这些都以十六进制格式表示。“mflags”、“mask” 和 “ignored_mask” 的合并提供了有关 fanotify_mark 调用中使用的标志和掩码的信息 [有关详细信息,请参见 fsnotify 手册页]。
虽然前三行是强制性的且始终打印,但其余行是可选的,如果尚未创建任何标记,则可以省略。
Timerfd 文件¶
pos: 0
flags: 02
mnt_id: 9
ino: 63107
clockid: 0
ticks: 0
settime flags: 01
it_value: (0, 49406829)
it_interval: (1, 0)
其中 “clockid” 是时钟类型,“ticks” 是发生的计时器到期次数 [有关详细信息,请参见 timerfd_create(2)]。“settime flags” 是用于设置计时器的八进制形式的标志 [有关详细信息,请参见 timerfd_settime(2)]。“it_value” 是计时器到期前的剩余时间。“it_interval” 是计时器的间隔。请注意,可以使用 TIMER_ABSTIME 选项设置计时器,该选项将显示在 “settime flags” 中,但 “it_value” 仍然显示计时器的剩余时间。
DMA 缓冲区文件¶
pos: 0
flags: 04002
mnt_id: 9
ino: 63107
size: 32768
count: 2
exp_name: system-heap
其中 “size” 是 DMA 缓冲区的大小(以字节为单位)。“count” 是 DMA 缓冲区文件的文件计数。“exp_name” 是 DMA 缓冲区导出器的名称。
3.9 /proc/<pid>/map_files - 关于内存映射文件的信息¶
此目录包含表示进程正在维护的内存映射文件的符号链接。示例输出
| lr-------- 1 root root 64 Jan 27 11:24 333c600000-333c620000 -> /usr/lib64/ld-2.18.so
| lr-------- 1 root root 64 Jan 27 11:24 333c81f000-333c820000 -> /usr/lib64/ld-2.18.so
| lr-------- 1 root root 64 Jan 27 11:24 333c820000-333c821000 -> /usr/lib64/ld-2.18.so
| ...
| lr-------- 1 root root 64 Jan 27 11:24 35d0421000-35d0422000 -> /usr/lib64/libselinux.so.1
| lr-------- 1 root root 64 Jan 27 11:24 400000-41a000 -> /usr/bin/ls
链接的名称表示映射的虚拟内存边界,即 vm_area_struct::vm_start-vm_area_struct::vm_end。
map_files 的主要目的是以快速的方式检索一组内存映射文件,而不是解析 /proc/<pid>/maps 或 /proc/<pid>/smaps,这两者都包含更多记录。同时,可以打开(2) 来自两个进程列表的映射并比较它们的 inode 编号,以确定哪些匿名内存区域实际上是共享的。
3.10 /proc/<pid>/timerslack_ns - 任务 timerslack 值¶
此文件提供任务的 timerslack 值(以纳秒为单位)。此值指定可以延迟正常计时器的时间量,以便合并计时器并避免不必要的唤醒。
这允许调整任务的交互性与功耗之间的权衡。
将 0 写入文件会将任务的 timerslack 设置为默认值。
有效值范围为 0 - ULLONG_MAX
应用程序设置该值必须具有 PTRACE_MODE_ATTACH_FSCREDS 级别的权限才能更改指定的任务的 timerslack_ns 值。
3.11 /proc/<pid>/patch_state - Livepatch 修补操作状态¶
启用 CONFIG_LIVEPATCH 时,此文件显示任务的修补状态值。
值“-1”表示没有修补程序正在转换中。
值“0”表示修补程序正在转换中,并且任务未修补。如果要启用修补程序,则任务尚未修补。如果要禁用修补程序,则任务已取消修补。
值“1”表示修补程序正在转换中,并且任务已修补。如果要启用修补程序,则任务已修补。如果要禁用修补程序,则任务尚未取消修补。
3.12 /proc/<pid>/arch_status - 任务架构特定状态¶
启用 CONFIG_PROC_PID_ARCH_STATUS 时,此文件显示任务的架构特定状态。
示例¶
$ cat /proc/6753/arch_status
AVX512_elapsed_ms: 8
描述¶
x86 特定条目¶
AVX512_elapsed_ms¶
如果机器上支持 AVX512,则此条目显示自上次记录 AVX512 使用情况以来经过的毫秒数。该记录在任务被调度出去时尽力而为地进行。这意味着该值取决于两个因素
任务在 CPU 上花费的时间而没有被调度出去。使用 CPU 隔离和单个可运行任务,这可能需要几秒钟。
自上次任务被调度出去以来的时间。根据被调度出去的原因(时间片耗尽、syscall ...),这可能需要任意长的时间。
因此,该值不能被认为是精确的和权威的信息。使用此信息的应用程序必须了解系统上的整体情况,以便确定任务是否是真正的 AVX512 用户。可以使用性能计数器获得精确的信息。
值“-1”表示未记录 AVX512 使用情况,因此该任务不太可能是 AVX512 用户,但这取决于工作负载和调度情况,也可能是上面提到的假阴性。
3.13 /proc/<pid>/fd - 打开文件的符号链接列表¶
此目录包含表示进程正在维护的打开文件的符号链接。示例输出
lr-x------ 1 root root 64 Sep 20 17:53 0 -> /dev/null
l-wx------ 1 root root 64 Sep 20 17:53 1 -> /dev/null
lrwx------ 1 root root 64 Sep 20 17:53 10 -> 'socket:[12539]'
lrwx------ 1 root root 64 Sep 20 17:53 11 -> 'socket:[12540]'
lrwx------ 1 root root 64 Sep 20 17:53 12 -> 'socket:[12542]'
进程的打开文件数存储在 /proc/<pid>/fd 的 stat() 输出的 “size” 成员中,以便快速访问。-------------------------------------------------------
3.14 /proc/<pid/ksm_stat - 关于进程的 ksm 状态的信息¶
启用 CONFIG_KSM 时,每个进程都有此文件,该文件显示 ksm 合并状态的信息。
示例¶
/ # cat /proc/self/ksm_stat
ksm_rmap_items 0
ksm_zero_pages 0
ksm_merging_pages 0
ksm_process_profit 0
ksm_merge_any: no
ksm_mergeable: no
描述¶
ksm_rmap_items¶
正在使用的 ksm_rmap_item 结构的数目。结构 ksm_rmap_item 存储虚拟地址的反向映射信息。 KSM 将为进程的每个 ksm 扫描页面生成一个 ksm_rmap_item。
ksm_zero_pages¶
启用 /sys/kernel/mm/ksm/use_zero_pages 时,它表示通过 KSM 合并了多少个空页面与内核零页面。
ksm_merging_pages¶
它表示此进程中有多少页面参与 KSM 合并(不包括 ksm_zero_pages)。它与 /proc/<pid>/ksm_merging_pages 显示的内容相同。
ksm_process_profit¶
KSM 带来的利润(节省的字节)。 KSM 可以通过合并相同的页面来节省内存,但也可以消耗额外的内存,因为它需要生成许多 rmap_items 来保存每个扫描页面的简短 rmap 信息。其中一些页面可能被合并,但有些页面可能在多次检查后无法合并,这些都是无利可图的内存消耗。
ksm_merge_any¶
它指定进程的 “mm” 是否由 prctl() 添加到 KSM 的候选列表,以及是否在进程级别完全启用了 KSM 扫描。
ksm_mergeable¶
它指定进程的 mms 的任何 VMA 当前是否适用于 KSM。
有关 KSM 的更多信息可以在 Kernel Samepage Merging 中找到。
第 4 章:配置 procfs¶
4.1 挂载选项¶
支持以下挂载选项
hidepid=
设置 /proc/<pid>/ 访问模式。
gid=
设置授权学习进程信息的组。
subset=
仅显示 procfs 的指定子集。
hidepid=off 或 hidepid=0 表示经典模式 - 每个人都可以访问所有 /proc/<pid>/ 目录(默认)。
hidepid=noaccess 或 hidepid=1 表示用户可能无法访问任何 /proc/<pid>/ 目录,但他们自己的目录除外。现在可以保护敏感文件(例如 cmdline、sched*、status)免受其他用户的侵害。这使得无法了解任何用户是否运行特定程序(如果该程序没有通过其行为显示自身)。作为额外的奖励,由于其他用户无法访问 /proc/<pid>/cmdline,因此现在可以保护通过程序参数传递敏感信息的编写不佳的程序免受本地窃听者的侵害。
hidepid=invisible 或 hidepid=2 表示 hidepid=1 加上所有 /proc/<pid>/ 对其他用户将完全不可见。这并不意味着它隐藏了具有特定 pid 值的进程是否存在的事实(可以通过其他方式了解,例如通过 “kill -0 $PID”),但它隐藏了进程的 uid 和 gid,否则可以通过 stat()’ing /proc/<pid>/ 了解。这大大简化了入侵者收集有关运行进程的信息的任务,无论某些守护程序是否以提升的权限运行,其他用户是否运行某些敏感程序,其他用户是否根本运行任何程序等等。
hidepid=ptraceable 或 hidepid=4 表示 procfs 应仅包含调用者可以 ptrace 的 /proc/<pid>/ 目录。
gid= 定义了一个组,该组被授权了解进程信息,否则 hidepid= 会禁止这样做。如果您使用诸如 identd 之类的守护程序,该守护程序需要了解有关进程信息的信息,只需将 identd 添加到此组即可。
subset=pid 隐藏 procfs 中所有与任务无关的顶级文件和目录。
第 5 章:文件系统行为¶
最初,在 pid 命名空间出现之前,procfs 是一个全局文件系统。这意味着系统中只有一个 procfs 实例。
当添加 pid 命名空间时,会在每个 pid 命名空间中挂载一个单独的 procfs 实例。因此,procfs 挂载选项在同一命名空间内的所有挂载点之间是全局的
# grep ^proc /proc/mounts
proc /proc proc rw,relatime,hidepid=2 0 0
# strace -e mount mount -o hidepid=1 -t proc proc /tmp/proc
mount("proc", "/tmp/proc", "proc", 0, "hidepid=1") = 0
+++ exited with 0 +++
# grep ^proc /proc/mounts
proc /proc proc rw,relatime,hidepid=2 0 0
proc /tmp/proc proc rw,relatime,hidepid=2 0 0
并且仅在重新挂载 procfs 后,挂载选项才会更改所有挂载点
# mount -o remount,hidepid=1 -t proc proc /tmp/proc
# grep ^proc /proc/mounts
proc /proc proc rw,relatime,hidepid=1 0 0
proc /tmp/proc proc rw,relatime,hidepid=1 0 0
此行为与其他文件系统的行为不同。
新的 procfs 行为更像其他文件系统。每个 procfs 挂载都会创建一个新的 procfs 实例。挂载选项影响自己的 procfs 实例。这意味着有可能在同一个 pid 命名空间中拥有多个 procfs 实例,这些实例显示具有不同过滤选项的任务
# mount -o hidepid=invisible -t proc proc /proc
# mount -o hidepid=noaccess -t proc proc /tmp/proc
# grep ^proc /proc/mounts
proc /proc proc rw,relatime,hidepid=invisible 0 0
proc /tmp/proc proc rw,relatime,hidepid=noaccess 0 0