Linux 中最重要的目錄之 /proc
一、概述
Linux 系統中,一切皆為文件的理念提供了最高級的抽象,開發者只需要使用一套 API 就可以操作 Linux 系統中的絕大部分資源。
而在整個 Linux 目錄樹中, 有一個最重要的 (虛擬) 目錄: /proc,在系統啟動時創建,在系統關閉時銷毀。
內核態通過簡潔的文件內容 API 形式,將系統的大部分狀態在用戶態充分暴露出來,這樣用戶/程序直接讀取 /proc 目錄下面的相關文件,就可以獲取到對應的系統/進程等狀態和數據,真可謂大道至簡。
總之,想要知道當前系統中運行了哪些進程、每個進程都打開了哪些文件、進程的 CPU、內存等使用情況如何、每個進程啟動了幾個線程、當前有哪些 TCP/UDP 連接、每個網卡收發的字節數等等,都可以在 /proc 目錄中找到答案。
BTW, 直接讀取 /proc 目錄下面的文件內容,也是很多 Linux 常用命令和開源系統監控組件的實現原理。
本文主要從開發者使用 Linux 系統的角度,著重分析一下 /proc 目錄下可以獲取到的四類信息:
- 系統
- 硬件/設備
- 網絡
- 進程
二、系統相關
1. 內核版本/編譯器/編譯時間
$ cat /proc/version
Linux 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 -a
Linux 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
2. 系統負載
輸出系統最近 1/5/15 分鐘的平均負載。
$ cat /proc/loadavg
0.03 0.01 0.00 1/300 1272
對比 uptime 命令的輸出:
$ uptime
14:12:36 up 7:44, 0 user, load average: 0.03, 0.01, 0.00
3. 硬中斷
輸出系統中的硬件中斷情況,按照 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
...
4. 軟中斷
輸出系統中的硬件中斷情況,按照 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
5. 內核參數
通過讀寫文件來動態調整內核參數,例如在TCP 100 萬長連接的參數調優[2] 一文中,就涉及到很多 /proc/sys 目錄下面相關文件的參數修改,限于篇幅,這里不再贅述了。
三、硬件相關
1. CPU
輸出機器上所有 (邏輯) CPU 型號、核心數、頻率等詳細信息。
$ cat /proc/cpuinfo
processor : 0
vendor_id : GenuineIntel
cpu family : 6
...
processor : 1
vendor_id : GenuineIntel
cpu family : 6
...
可以在此基礎上計算出 (邏輯) CPU 的數量。
$ cat /proc/cpuinfo | grep processor | wc -l
常用的 Linux 命令中的 lscpu,輸出的源數據就來自 /proc/meminfo 文件。
2. 內存
輸出機器上物理內存、交換分區、緩存等信息。
$ cat /proc/meminfo
MemTotal: 8138112 kB
MemFree: 6372192 kB
MemAvailable: 7426484 kB
Buffers: 74336 kB
Cached: 1177368 kB
SwapCached: 0 kB
...
常用的 Linux 命令中的 free 和 top,輸出的源數據就來自 /proc/meminfo 文件。
$ free
total used free shared buff/cache available
Mem: 8138112 711636 6372192 1108 1301156 7426476
Swap: 0 0 0
四、網絡相關
1. 網卡數據統計
輸出網卡接口的統計信息,例如發送數據量、接收數據量、錯誤數量。
$ cat /proc/net/dev
Inter-| 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 0
docker0: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
常用的 ifconfig 命令,數據源就是 /proc/net/dev 文件。
$ ifconfig
docker0: 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 0
eth0: 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 0
lo: 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
2. 網絡連接狀態和對列
輸出 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
3. ARP 表
ARP 表存儲著當前局域網中各主機的 IP 地址到 MAC 地址的映射關系。
$ cat /proc/net/arp
IP address HW type Flags HW address Mask Device
10.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
1. 啟動參數
輸出進程的啟動命令及其參數。
$ sudo cat /proc/207/cmdline
/usr/bin/dockerd-p/var/run/docker.pid--mtu=1460
2. 進程狀態
輸出進程的 pid、ppid、內存使用等信息。
$ sudo cat /proc/207/status
# output
Name: dockerd
Umask: 0022
State: S (sleeping)
Tgid: 207
Ngid: 0
Pid: 207
PPid: 1
...
Threads: 10
...
Cpus_allowed: 3
Cpus_allowed_list: 0-1
Mems_allowed: 00000000,00000001
Mems_allowed_list: 0
...
3. 函數調用棧
輸出進程當前正在執行的的函數調用棧。
$ 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
4. 線程狀態
輸出進程
$ sudo ls -l /proc/207/task/
# output
total 0
dr-xr-xr-x 7 root root 0 May 20 13:43 206
dr-xr-xr-x 7 root root 0 May 20 13:44 212
dr-xr-xr-x 7 root root 0 May 20 13:44 213
dr-xr-xr-x 7 root root 0 May 20 13:44 214
dr-xr-xr-x 7 root root 0 May 20 13:44 215
dr-xr-xr-x 7 root root 0 May 20 13:44 221
dr-xr-xr-x 7 root root 0 May 20 13:44 229
dr-xr-xr-x 7 root root 0 May 20 13:44 249
dr-xr-xr-x 7 root root 0 May 20 13:44 263
dr-xr-xr-x 7 root root 0 May 20 13:44 275
下面是使用 pstree 命令輸出的線程結果。
$ pstree -p 207
5. 打開文件
輸出 dockerd 進程打開的所有文件句柄 (fd)。
$ sudo ls -l /proc/207/fd/
6. 內存映射
輸出進程內存映射區域(例如堆、共享庫),smaps 細化到每個區域的 RSS 和臟頁統計,用于內存泄漏分析 (具體的分析方法,感興趣的讀者可以自行搜索)。
$ sudo cat /proc/212/maps
$ sudo cat /proc/212/smaps
會逐行輸入類似如下的內存映射信息:
Linux 命令中的 pmap 的數據源就是 maps 和 smaps 文件。
$ sudo pmap -x 207
7. OOM
Linux 內核 OOM killer 會在內存不足時,選擇性地殺掉用戶進程。
它的運行規則簡單來說就是,OOM killer 會為每個用戶進程設置一個權重值,這個權重值越高,被 kill 的概率越高,反之概率越低。
每個進程的權重值存放在 /proc/{pid}/oom_adj 中,大多數進程的默認權重值都是 0。
$ sudo cat /proc/207/oom_adj
8. 網絡連接
進程網絡相關的文件都在 /proc/{pid}/net 目錄下面,可以根據不同的協議類型查看對應的文件,輸出結果和 /proc/net/ 下面的文件基本類似,限于篇幅,這里就不再贅述了。