https://mp.weixin.qq.com/s/8Nr0yCa2QreMONR1zCUcAA
概述
Linux 系统中,一切皆为文件的理念[1]提供了最高级的抽象,开发者只需要使用一套 API 就可以操作 Linux 系统中的绝大部分资源。
而在整个 Linux 目录树中, 有一个最重要的 (虚拟) 目录: /proc
,在系统启动时创建,在系统关闭时销毁。
内核态通过简洁的文件内容 API 形式,将系统的大部分状态在用户态充分暴露出来,这样用户/程序直接读取 /proc 目录下面的相关文件,就可以获取到对应的系统/进程等状态和数据,真可谓大道至简。
总之,想要知道当前系统中运行了哪些进程、每个进程都打开了哪些文件、进程的 CPU、内存等使用情况如何、每个进程启动了几个线程、当前有哪些 TCP/UDP 连接、每个网卡收发的字节数等等,都可以在 /proc
目录中找到答案。
BTW, 直接读取 /proc 目录下面的文件内容,也是很多 Linux 常用命令和开源系统监控组件的实现原理。
本文主要从开发者使用 Linux 系统的角度,着重分析一下 /proc 目录下可以获取到的四类信息:
-
系统
-
硬件/设备
-
网络
-
进程
系统相关
内核版本/编译器/编译时间
$ cat /proc/versionLinux version 6.6.87+ (builder@f33bfd62b873) (Chromium OS 17.0_pre498229-r33 clang version 17.0.0 (/var/cache/chromeos-cache/distfiles/egit-src/external/github.com/llvm/llvm-project 14f0776550b5a49e1c42f49a00213f7f3fa047bf), LLD 17.0.0) #1 SMP PREEMPT_DYNAMIC Sat May 3 09:33:57 UTC 2025
可以看到,输出的信息远比 uname
命令获取到内容要详细。
$ uname -aLinux cs-100667547897-default 6.6.87+ #1 SMP PREEMPT_DYNAMIC Sat May 3 09:33:57 UTC 2025 x86_64 x86_64 x86_64 GNU/Linux
系统负载
输出系统最近 1/5/15 分钟的平均负载。
$ cat /proc/loadavg0.03 0.01 0.00 1/300 1272
对比 uptime
命令的输出
$ uptime14:12:36 up 7:44, 0 user, load average: 0.03, 0.01, 0.00
硬中断
输出系统中的硬件中断情况,按照 CPU 进行分组。
$ cat /proc/interrupts# output CPU0 CPU1 0: 269 0 IO-APIC 0-edge timer 1: 0 9 IO-APIC 1-edge i8042 4: 0 2067 IO-APIC 4-edge ttyS0 6: 70 0 IO-APIC 6-edge 8: 0 0 IO-APIC 8-edge rtc0 9: 0 0 IO-APIC 9-fasteoi acpi 10: 0 16876 IO-APIC 10-fasteoi virtio2...
软中断
输出系统中的硬件中断情况,按照 CPU 进行分组。
$ cat /proc/softirqs# output CPU0 CPU1 HI: 0 0 TIMER: 98735 84868 NET_TX: 2 4 NET_RX: 18737 28301 BLOCK: 0 29051 IRQ_POLL: 0 0 TASKLET: 44 30 SCHED: 200114 198635 HRTIMER: 0 0 RCU: 57886 61736
每个 CPU 都对应一个软中断内核线程运行状况,所以使用下面的命令,也可以获取到 CPU 软中断运行情况。
$ ps aux | grep softirq
内核参数
通过读写文件来动态调整内核参数,例如在TCP 100 万长连接的参数调优[2] 一文中,就涉及到很多 /proc/sys
目录下面相关文件的参数修改,限于篇幅,这里不再赘述了。
硬件相关
CPU
输出机器上所有 (逻辑) CPU 型号、核心数、频率等详细信息。
$ cat /proc/cpuinfoprocessor : 0vendor_id : GenuineIntelcpu family : 6...processor : 1vendor_id : GenuineIntelcpu family : 6...
可以在此基础上计算出 (逻辑) CPU 的数量。
$ cat /proc/cpuinfo | grep processor | wc -l
常用的 Linux 命令中的 lscpu
,输出的源数据就来自 /proc/meminfo
文件。
内存
输出机器上物理内存、交换分区、缓存等信息。
$ cat /proc/meminfoMemTotal: 8138112 kBMemFree: 6372192 kBMemAvailable: 7426484 kBBuffers: 74336 kBCached: 1177368 kBSwapCached: 0 kB...
常用的 Linux 命令中的 free
和 top
,输出的源数据就来自 /proc/meminfo
文件。
$ free total used free shared buff/cache availableMem: 8138112 711636 6372192 1108 1301156 7426476Swap: 0 0 0
网络相关
网卡数据统计
输出网卡接口的统计信息,例如发送数据量、接收数据量、错误数量。
$ cat /proc/net/devInter-| Receive | Transmit face |bytes packets errs drop fifo frame compressed multicast|bytes packets errs drop fifo colls carrier compressed lo: 31272563 12256 0 0 0 0 0 0 31272563 12256 0 0 0 0 0 0 eth0: 6263742 24376 0 0 0 0 0 0 18041254 18792 0 0 0 0 0 0docker0: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
常用的 ifconfig
命令,数据源就是 /proc/net/dev
文件。
$ ifconfigdocker0: flags=4099<UP,BROADCAST,MULTICAST> mtu 1460 ... RX packets 0 bytes 0 (0.0 B) RX errors 0 dropped 0 overruns 0 frame 0 TX packets 0 bytes 0 (0.0 B) TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1460 ... RX packets 24328 bytes 6256091 (6.2 MB) RX errors 0 dropped 0 overruns 0 frame 0 TX packets 18758 bytes 18034626 (18.0 MB) TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0lo: flags=73<UP,LOOPBACK,RUNNING> mtu 65536 ... RX packets 12216 bytes 31268081 (31.2 MB) RX errors 0 dropped 0 overruns 0 frame 0 TX packets 12216 bytes 31268081 (31.2 MB) TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
网络连接状态和对列
输出 TCP/UDP 网络连接,及其对应的端口号和状态等信息。
$ cat /proc/net/tcp$ cat /proc/net/udp
常用的 Linux 网络命令中, netstat
和 ss
命令就是对 /proc/net/tcp
、/proc/net/udp
、/proc/net/raw
等文件内容进行了二次加工,具体的过程这里就不展开了 。
下面是使用 netstat
和 ss
命令的输出结果
$ netstat -ant
$ ss -ant
ARP 表
ARP 表存储着当前局域网中各主机的 IP 地址到 MAC 地址的映射关系[3]。
$ cat /proc/net/arpIP address HW type Flags HW address Mask Device10.88.0.1 0x1 0x2 12:a5:12:b7:15:70 * eth0
下面是对等的 arp
命令。
$ arp -an? (10.88.0.1) at 12:a5:12:b7:15:70 [ether] on eth0
进程相关
每个进程都有一个进程 ID (数字) pid
,所以和进程相关的信息都在目录 /proc/{pid}
下面。
例如下面展示了 root 用户打开的相关进程:
...dr-xr-xr-x 9 root root 0 May 19 23:19 207/dr-xr-xr-x 9 root root 0 May 19 23:19 25/dr-xr-xr-x 9 root root 0 May 19 23:19 26/dr-xr-xr-x 9 root root 0 May 19 23:19 264/dr-xr-xr-x 9 root root 0 May 19 23:20 270/dr-xr-xr-x 9 root root 0 May 19 23:19 271/dr-xr-xr-x 9 root root 0 May 19 23:20 275/dr-xr-xr-x 9 root root 0 May 19 23:19 285/dr-xr-xr-x 9 root root 0 May 19 23:19 296/dr-xr-xr-x 9 root root 0 May 19 23:20 467/...
下文的示例中,统一以笔者机器上运行的 dockerd
进程为例,进行代码和命令输出结果演示。
root 207 1 0 23:19 ? 00:00:00 /usr/bin/dockerd -p /var/run/docker.pid --mtu=1460
演示 docker 进程 pid = 207
启动参数
输出进程的启动命令及其参数。
$ sudo cat /proc/207/cmdline/usr/bin/dockerd-p/var/run/docker.pid--mtu=1460
进程状态
输出进程的 pid、ppid、内存使用等信息。
$ sudo cat /proc/207/status# outputName: dockerdUmask: 0022State: S (sleeping)Tgid: 207Ngid: 0Pid: 207PPid: 1...Threads: 10...Cpus_allowed: 3Cpus_allowed_list: 0-1Mems_allowed: 00000000,00000001Mems_allowed_list: 0...
函数调用栈
输出进程当前正在执行的的函数调用栈。
$ sudo cat /proc/207/stack# output[<0>] futex_wait_queue+0xdd/0x130[<0>] futex_wait+0x179/0x300[<0>] do_futex+0x18f/0x1e0[<0>] __se_sys_futex+0x152/0x1d0[<0>] do_syscall_64+0x46/0xb0[<0>] entry_SYSCALL_64_after_hwframe+0x78/0xe2
线程状态
输出进程
$ sudo ls -l /proc/207/task/# outputtotal 0dr-xr-xr-x 7 root root 0 May 20 13:43 206dr-xr-xr-x 7 root root 0 May 20 13:44 212dr-xr-xr-x 7 root root 0 May 20 13:44 213dr-xr-xr-x 7 root root 0 May 20 13:44 214dr-xr-xr-x 7 root root 0 May 20 13:44 215dr-xr-xr-x 7 root root 0 May 20 13:44 221dr-xr-xr-x 7 root root 0 May 20 13:44 229dr-xr-xr-x 7 root root 0 May 20 13:44 249dr-xr-xr-x 7 root root 0 May 20 13:44 263dr-xr-xr-x 7 root root 0 May 20 13:44 275
下面是使用 pstree
命令输出的线程结果。
$ pstree -p 207
打开文件
输出 dockerd
进程打开的所有文件句柄 (fd)。
$ sudo ls -l /proc/207/fd/
内存映射
输出进程内存映射区域(例如堆、共享库),smaps 细化到每个区域的 RSS 和脏页统计,用于内存泄漏分析 (具体的分析方法,感兴趣的读者可以自行搜索)。
$ sudo cat /proc/212/maps
$ sudo cat /proc/212/smaps
会逐行输入类似如下的内存映射信息:
Linux 命令中的 pmap
的数据源就是 maps 和 smaps 文件。
$ sudo pmap -x 207
OOM
Linux 内核 OOM killer 会在内存不足时,选择性地杀掉用户进程。
它的运行规则简单来说就是,OOM killer 会为每个用户进程设置一个权重值,这个权重值越高,被 kill 的概率越高,反之概率越低。
每个进程的权重值存放在 /proc/{pid}/oom_adj
中,大多数进程的默认权重值都是 0。
$ sudo cat /proc/207/oom_adj
网络连接
进程网络相关的文件都在 /proc/{pid}/net
目录下面,可以根据不同的协议类型查看对应的文件,输出结果和 /proc/net/
下面的文件基本类似,限于篇幅,这里就不再赘述了。