/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 与我联系。

第 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 能力。这适用于所有只读信息,如 mapsenvironpagemap 等。唯一的例外是 mem 文件,由于其读写性质,需要 CAP_SYS_PTRACE 能力和更高的 PTRACE_MODE_ATTACH 权限;CAP_PERFMON 能力不授予对其他进程的 /proc/PID/mem 的访问权限。

请注意,打开 /proc/<pid> 或其包含的任何文件或子目录的文件描述符不会阻止 <pid> 在 <pid> 退出的情况下被其他进程重用。对与已死进程对应的打开 /proc/<pid> 文件描述符的操作永远不会对内核可能(通过偶然)也分配了进程 ID <pid> 的任何新进程起作用。相反,对这些 FD 的操作通常会失败,并出现 ESRCH。

表 1-1:/proc 中进程特定的条目

文件

内容

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 文件并扫描页表。它很慢,但非常精确。

表 1-2:状态字段的内容(截至 4.19)

字段

内容

名称

可执行文件的文件名

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

非自愿上下文切换的次数

表 1-3:statm 字段的内容(截至 2.6.8-rc3)

字段

内容

size

总程序大小(页)

(与 status 中的 VmSize 相同)

resident

内存部分的大小(页)

(与 status 中的 VmRSS 相同)

shared

共享的页数

(即由文件支持,与 status 中的 RssFile+RssShmem 相同)

trs

“代码”的页数

(不包括 libs;已损坏,包括数据段)

lrs

库页数

(在 2.6 上始终为 0)

drs

数据/堆栈的页数

(包括 libs;已损坏,包括库文本)

dt

脏页数

(在 2.6 上始终为 0)

表 1-4:stat 字段的内容(截至 2.6.30-rc7)

字段

内容

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 本质上是竞争的(只有在单个读取调用中才能实现一致的输出)。

当在修改内存映射时对这些文件进行部分读取时,通常会表现出这种情况。尽管存在竞争,但我们确实提供了以下保证

  1. 映射的地址永远不会向后移动,这意味着没有两个区域会重叠。

  2. 如果在 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 中列出。并非所有文件都将存在于您的系统中。这取决于内核配置和已加载的模块,哪些文件存在,哪些文件缺失。

表 1-5:/proc 中的内核信息

文件

内容

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

过去 1、5 和 15 分钟的负载平均值;

当前可运行的进程数(正在运行或在就绪队列中);系统中进程总数;上次创建的 pid。除“当前可运行的进程数”和“系统中进程总数”外,所有字段都用一个空格分隔,这两个字段用斜杠 ('/') 分隔。例如:0.61 0.61 0.55 3/828 22084

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 区域的信息。每个区域一行,包含区域的虚拟地址范围、大小(以字节为单位)、创建者的调用者信息以及可选信息(具体取决于区域的类型)

pages=nr

页面数

phys=addr

如果指定了物理地址

ioremap

I/O 映射 (ioremap() 及其他)

vmalloc

vmalloc() 区域

vmap

vmap()ed 页面

user

VM_USERMAP 区域

vpages

页面指针的缓冲区已被 vmalloced(巨大区域)

N<node>=nr

(仅在 NUMA 内核上)在内存节点 <node> 上分配的页面数

> 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 列出了文件及其含义。

表 1-8:/proc/net 中的 IPv6 信息

文件

内容

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)

表 1-9:/proc/net 中的网络信息

文件

内容

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 中显示的四个文件。

表 1-10:/proc/parport 中的文件

文件

内容

autoprobe

已获得的任何 IEEE-1284 设备 ID 信息。

devices

使用该端口的设备驱动程序列表。设备当前正在使用该端口的名称旁边会出现一个 +(可能不会出现在任何设备旁边)。

hardware

并行端口的基本地址、IRQ 线和 DMA 通道。

irq

parport 正在用于该端口的 IRQ。这是一个单独的文件,允许您通过写入一个新值(IRQ 号或 none)来更改它。

1.6 /proc/tty 中的 TTY 信息

有关可用和实际使用的 tty 的信息可以在目录 /proc/tty 中找到。您将在此目录中找到驱动程序和线路规程的条目,如表 1-11 所示。

表 1-11:/proc/tty 中的文件

文件

内容

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 完成。但存在几个问题

    1. CPU 将不会等待 I/O 完成,iowait 是任务等待 I/O 完成的时间。当 CPU 由于未完成的任务 I/O 而进入空闲状态时,将在该 CPU 上调度另一个任务。

    2. 在多核 CPU 中,等待 I/O 完成的任务不在任何 CPU 上运行,因此每个 CPU 的 iowait 难以计算。

    3. /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 显示了每个按设备目录中的文件。

表 1-12:/proc/fs/ext4/<devname> 中的文件

文件

内容

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

  • R = 可以执行读取操作

  • W = 可以执行写入操作

  • U = 可以执行取消消隐

flags

  • E = 已启用

  • C = 首选控制台

  • B = 主启动控制台

  • p = 用于 printk 缓冲区

  • b = 不是 TTY,而是盲文设备

  • a = 当 CPU 离线时可以安全使用

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 使用情况以来经过的毫秒数。该记录在任务被调度出去时尽力而为地进行。这意味着该值取决于两个因素

  1. 任务在 CPU 上花费的时间而没有被调度出去。使用 CPU 隔离和单个可运行任务,这可能需要几秒钟。

  2. 自上次任务被调度出去以来的时间。根据被调度出去的原因(时间片耗尽、syscall ...),这可能需要任意长的时间。

因此,该值不能被认为是精确的和权威的信息。使用此信息的应用程序必须了解系统上的整体情况,以便确定任务是否是真正的 AVX512 用户。可以使用性能计数器获得精确的信息。

值“-1”表示未记录 AVX512 使用情况,因此该任务不太可能是 AVX512 用户,但这取决于工作负载和调度情况,也可能是上面提到的假阴性。

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