/proc、/sys、/dev、/run
一、学习目标
学完本篇,你应当能够:
- 理解
/proc、/sys、/dev、/run 分别解决什么问题 - 掌握 Linux 内核向用户空间暴露进程、设备和运行状态的基本机制
- 能够通过
/proc 分析进程、CPU、内存、文件描述符和内核参数 - 能够通过
/sys 查看设备拓扑、驱动绑定和硬件属性 - 理解
/dev 中设备文件的本质,以及字符设备与块设备的区别 - 理解
/run 中 PID 文件、Socket、锁文件和运行时状态的作用 - 掌握虚拟文件系统在系统运维、故障排查和程序开发中的典型应用
- 避免误修改内核参数、设备节点和运行状态文件造成生产事故
二、从磁盘文件系统到虚拟文件系统
2.1 普通文件系统保存真实数据
常见的 ext4、XFS、Btrfs 等文件系统,主要负责将文件数据持久化到磁盘。
例如:
/etc/nginx/nginx.conf/home/user/document.md/var/log/syslog/var/lib/mysql/ibdata1
这些文件通常具有以下特点:
其基本访问链路如下:
应用程序↓系统调用↓VFS 虚拟文件系统层↓ext4、XFS、Btrfs↓块设备↓SSD 或 HDD
2.2 虚拟文件系统展示内核运行状态
/proc、/sys、/dev、/run 与普通磁盘目录不同。
其中大量文件并不是从磁盘中读取,而是由内核或用户空间服务在运行时动态生成。
例如:
cat /proc/meminfo
输出看起来像普通文本文件:
MemTotal: 32743496 kBMemFree: 1258704 kBMemAvailable: 12683420 kBBuffers: 241820 kBCached: 9486312 kB
但 /proc/meminfo 并不是磁盘上的普通文本文件。
当应用程序读取它时,内核会根据当前内存状态动态生成内容,再返回给用户空间。
可以将其理解为:
用户执行 cat↓cat 调用 open() 和 read()↓VFS 识别这是 procfs 文件↓内核读取当前内存统计数据↓内核动态生成文本内容↓返回给 cat↓显示到终端
因此,虚拟文件系统的核心作用是:
将内核对象、设备状态和运行时信息转换为用户空间熟悉的文件接口。
2.3 四个目录的职责边界
先建立整体认识:
可以简单概括为:
/proc →系统现在运行得怎么样/sys →系统中有哪些设备,它们如何组织/dev →应用程序如何访问这些设备/run →服务当前是否运行,以及如何与它通信
三、VFS:统一文件接口的基础
3.1 VFS 是什么
VFS 是 Virtual File System,即虚拟文件系统层。
它不是某一种具体文件系统,而是 Linux 内核提供的一套统一抽象接口。
上层应用程序通常只需要调用:
open()read()write()close()stat()ioctl()mmap()
不需要关心底层访问的是:
例如:
cat /etc/hostnamecat /proc/uptimecat /sys/class/net/eth0/operstate
从 cat 程序的角度看,它们都是:
打开文件↓读取内容↓输出内容
但在内核内部,这三个路径分别由不同文件系统实现:
/etc/hostname → ext4 或其他磁盘文件系统/proc/uptime → procfs/sys/class/net/eth0/operstate → sysfs
3.2 查看虚拟文件系统挂载情况
可以执行:
findmnt -t proc,sysfs,devtmpfs,tmpfs
典型输出:
TARGET SOURCE FSTYPE OPTIONS/proc proc proc rw,nosuid,nodev,noexec,relatime/sys sysfs sysfs rw,nosuid,nodev,noexec,relatime/dev udev devtmpfs rw,nosuid,relatime,size=.../run tmpfs tmpfs rw,nosuid,nodev,noexec,relatime
也可以分别查看:
findmnt /procfindmnt /sysfindmnt /devfindmnt /run
这里可以看到:
3.3 虚拟文件为什么显示大小为 0
执行:
ls -l /proc/meminfo
可能看到:
-r--r--r-- 1 root root 0 Jul 27 10:00 /proc/meminfo
文件大小显示为 0,但读取时却可以得到大量内容。
原因是:
因此,对 /proc 和 /sys 使用普通文件大小判断往往没有意义。
例如:
du -sh /proc
可能出现异常、权限错误或统计结果不具备实际参考价值。
四、/proc:进程与内核状态的观察窗口
4.1 /proc 的核心定位
/proc 挂载的是 procfs。
它最初主要用于暴露进程信息,后来逐渐扩展为内核状态和参数接口。
典型结构如下:
/proc/├── 1/├── 1234/├── cpuinfo├── cmdline├── devices├── diskstats├── filesystems├── interrupts├── loadavg├── meminfo├── mounts├── net/├── partitions├── self/├── stat├── swaps├── sys/├── sysrq-trigger├── uptime└── version
其中可以分为两类:
/proc/<PID>/ →某个具体进程的信息/proc/其他文件→系统整体和内核信息
4.2 /proc/<PID>:进程信息目录
系统中每个进程通常都有一个对应目录:
/proc/进程PID/
例如查看 PID 为 1234 的进程:
ls /proc/1234
可能看到:
attrauxvcgroupcmdlinecommcwdenvironexefdfdinfolimitsmapsmountinfonetrootsmapsstatstatustask
这意味着 Linux 将一个正在运行的进程,映射成了一组可读取的文件和符号链接。
4.3 查看进程命令行
cat /proc/1234/cmdline
cmdline 中不同参数使用空字符分隔,直接查看时可能挤在一起。
更易读的方式:
tr'\0'' ' < /proc/1234/cmdlineecho
例如:
java -jar application.jar --spring.profiles.active=prod
也可以使用:
ps -p 1234 -o pid,ppid,user,args
但需要理解,ps 的很多信息本质上也是从 /proc 中读取和整理出来的。
4.4 查看进程名称
cat /proc/1234/comm
输出可能是:
java
comm 通常只显示简短进程名,而 cmdline 显示完整启动参数。
4.5 查看进程状态
cat /proc/1234/status
典型内容:
Name: javaState: S (sleeping)Tgid: 1234Pid: 1234PPid: 1Uid: 1000 1000 1000 1000Gid: 1000 1000 1000 1000VmPeak: 8245320 kBVmSize: 7812384 kBVmRSS: 1024380 kBThreads: 82
常见字段:
| |
|---|
Name | |
State | |
Pid | |
PPid | |
Uid | |
Gid | |
VmSize | |
VmRSS | |
Threads | |
FDSize | |
常见进程状态:
4.6 查看进程可执行文件
readlink -f /proc/1234/exe
可能输出:
/usr/lib/jvm/java-17-openjdk-amd64/bin/java
/proc/<PID>/exe 是指向进程实际可执行文件的符号链接。
它在以下场景非常有用:
如果程序文件在启动后被删除,可能看到:
/usr/local/bin/app (deleted)
4.7 查看进程当前工作目录
readlink -f /proc/1234/cwd
例如:
/opt/application
进程使用相对路径读写文件时,通常以该目录作为基准。
如果应用生成的日志或临时文件找不到,可以检查其当前工作目录。
4.8 查看进程根目录
readlink -f /proc/1234/root
普通宿主机进程通常指向:
/
容器进程的 /proc/<PID>/root 可能对应其容器根文件系统。
可以利用这一点从宿主机观察容器内部文件:
ls /proc/容器进程PID/root
但生产环境中更推荐使用容器工具进行管理,不应随意通过该路径修改容器内部文件。
4.9 查看进程环境变量
tr'\0''\n' < /proc/1234/environ
可能包含:
JAVA_HOME=/usr/lib/jvm/java-17-openjdk-amd64PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/binSPRING_PROFILES_ACTIVE=prod
这在排查以下问题时非常有效:
需要注意:
环境变量中可能包含密码、Token、数据库连接和密钥信息。
读取和输出时必须注意权限与日志泄露风险。
4.10 查看进程打开的文件描述符
ls -l /proc/1234/fd
输出可能类似:
0 -> /dev/null1 -> /var/log/app/stdout.log2 -> /var/log/app/stderr.log3 -> socket:[48291]4 -> /var/lib/app/data.db5 -> pipe:[48320]
文件描述符通常包括:
0 →标准输入1 →标准输出2 →标准错误
其余可能是:
统计进程打开的文件描述符数量:
ls /proc/1234/fd | wc -l
查看软硬限制:
cat /proc/1234/limits
其中常见字段:
Max open filesMax processesMax locked memoryMax address space
当应用出现:
Too many open files
应同时检查:
ls /proc/1234/fd | wc -lgrep "Max open files" /proc/1234/limits
4.11 通过 /proc 排查已删除但未释放的文件
假设某应用日志被执行了:
rm /var/log/app/app.log
但应用仍然持有该文件描述符。
可以查看:
ls -l /proc/1234/fd | grep deleted
可能看到:
1 -> /var/log/app/app.log (deleted)
这意味着:
可以查看对应文件描述符:
ls -l /proc/1234/fd/1
查看进程正在写入的内容:
tail -f /proc/1234/fd/1
生产环境通常应通过以下方式释放:
不建议随意操作 /proc/<PID>/fd/<FD> 来修改业务数据。
4.12 查看线程信息
每个进程的线程位于:
/proc/<PID>/task/<TID>/
查看线程数量:
ls /proc/1234/task | wc -l
查看所有线程 ID:
ls /proc/1234/task
查看某个线程状态:
cat /proc/1234/task/线程ID/status
对于 Java 应用,可以结合:
top -H -p 1234
查看高 CPU 线程。
然后将线程 ID 转换为十六进制:
printf'%x\n'线程ID
再到 Java 线程转储中定位:
jstack 1234
这是排查 Java 高 CPU 问题的经典方法。
五、/proc 中的系统级信息
5.1 CPU 信息:/proc/cpuinfo
cat /proc/cpuinfo
常见字段包括:
processorvendor_idmodel namecpu MHzcache sizephysical idcore idsiblingscpu coresflags
快速查看逻辑 CPU 数量:
grep -c '^processor' /proc/cpuinfo
但生产环境更推荐:
lscpu
因为 lscpu 会将 /proc/cpuinfo 和 sysfs 等信息整理成更易理解的拓扑结构。
5.2 内存信息:/proc/meminfo
cat /proc/meminfo
常见字段:
| |
|---|
MemTotal | |
MemFree | |
MemAvailable | |
Buffers | |
Cached | |
SwapTotal | |
SwapFree | |
Slab | |
Dirty | |
Writeback | |
Shmem | |
运维判断内存是否紧张时,应重点关注:
MemAvailable
不能只看:
MemFree
因为 Linux 会主动使用空闲内存作为页缓存,以提升 I/O 性能。
5.3 系统负载:/proc/loadavg
cat /proc/loadavg
示例:
0.18 0.21 0.17 2/856 18432
字段含义:
0.18 →最近 1 分钟平均负载0.21 →最近 5 分钟平均负载0.17 →最近 15 分钟平均负载2/856 → 2 个可运行任务,共 856 个任务18432 →最近创建进程的 PID
平均负载不仅统计正在使用 CPU 的任务,也通常包含处于不可中断睡眠状态的任务。
因此:
负载高≠ CPU 使用率一定高
如果负载很高但 CPU 空闲较多,可能需要排查:
5.4 系统运行时间:/proc/uptime
cat /proc/uptime
示例:
938245.42 2876210.18
含义:
第一个值→系统启动后的秒数第二个值→所有 CPU 累计空闲秒数
更易读的方式:
uptime
5.5 系统统计:/proc/stat
cat /proc/stat
其中包含:
例如:
cpu 219283 183 82471 9374621 18231 0 9812 0 0 0
top、vmstat、mpstat 等工具都会读取相关内核统计数据。
CPU 使用率并不是内核直接提供的一个单独百分比,而是监控工具通过两次采样 /proc/stat,计算不同 CPU 时间字段的增量得到的。
5.6 磁盘统计:/proc/diskstats
cat /proc/diskstats
包含块设备的:
iostat 等工具会基于这些累计值进行采样计算。
更常用的查看方式:
iostat -x 1
5.7 中断统计:/proc/interrupts
cat /proc/interrupts
它显示:
这对于排查以下问题非常有价值:
5.8 文件系统支持列表:/proc/filesystems
cat /proc/filesystems
示例:
nodev sysfsnodev tmpfsnodev proc ext4 xfsnodev cgroup2
带有 nodev 的文件系统通常不直接依赖块设备,例如:
5.9 当前挂载信息
可以查看:
cat /proc/mounts
但在复杂环境中,更准确的进程级挂载信息通常位于:
cat /proc/self/mountinfo
日常运维更推荐使用:
findmnt
因为 findmnt 会将底层信息格式化后展示。
5.10 网络信息:/proc/net
/proc/net 暴露网络协议栈信息。
例如:
/proc/net/tcp/proc/net/udp/proc/net/unix/proc/net/dev/proc/net/route
查看网络接口统计:
cat /proc/net/dev
查看 Unix Socket:
cat /proc/net/unix
不过日常使用更推荐:
ss -lntpss -lxip -s linkip route
这些工具本质上会通过更合适的内核接口获取并整理信息。
六、/proc/sys:可读写的内核参数接口
6.1 /proc/sys 的作用
/proc/sys 将部分内核参数暴露为文件。
例如:
/proc/sys/net/ipv4/ip_forward/proc/sys/vm/swappiness/proc/sys/fs/file-max/proc/sys/kernel/hostname
读取 IP 转发设置:
cat /proc/sys/net/ipv4/ip_forward
输出:
0
表示未开启。
开启:
echo 1 > /proc/sys/net/ipv4/ip_forward
但这种修改通常只在当前运行周期内生效,重启后可能恢复。
6.2 sysctl 与 /proc/sys 的映射
以下两者本质上对应同一个内核参数:
cat /proc/sys/vm/swappiness
sysctl vm.swappiness
参数名称转换关系:
/proc/sys/vm/swappiness↓vm.swappiness
再例如:
/proc/sys/net/ipv4/tcp_fin_timeout↓net.ipv4.tcp_fin_timeout
临时修改:
sysctl -w vm.swappiness=10
永久配置通常写入:
/etc/sysctl.conf
或者:
/etc/sysctl.d/*.conf
例如:
cat > /etc/sysctl.d/90-custom.conf <<'EOF'vm.swappiness = 10net.ipv4.ip_forward = 1EOF
加载配置:
sysctl --system
6.3 常见内核参数
| |
|---|
vm.swappiness | |
fs.file-max | |
net.ipv4.ip_forward | |
net.core.somaxconn | |
net.ipv4.tcp_fin_timeout | |
kernel.pid_max | |
fs.inotify.max_user_watches | |
6.4 修改内核参数的风险
不能看到网上的“性能优化参数”就直接复制。
错误修改可能导致:
正确流程应为:
确认业务问题↓理解参数含义和单位↓记录原值↓临时修改↓监控效果↓验证无副作用↓再写入永久配置
修改前记录原值:
sysctl vm.swappiness
临时验证:
sysctl -w vm.swappiness=10
确认后再写入 /etc/sysctl.d/。
七、/proc/self:进程查看自身的入口
/proc/self 是一个特殊符号链接,始终指向当前访问进程自己的 /proc/<PID>。
执行:
readlink /proc/self
如果由 Shell 内置命令或外部程序执行,结果可能随具体进程变化。
例如:
ls -l /proc/self
可能看到:
/proc/self -> 24580
对于程序来说,/proc/self 很有价值,因为它不需要提前知道自己的 PID。
常见用途:
/proc/self/fd →当前进程文件描述符/proc/self/status →当前进程状态/proc/self/mountinfo →当前进程挂载视图/proc/self/cgroup →当前进程所属 cgroup
7.1 /dev/fd 与 /proc/self/fd
在很多系统中:
ls -l /dev/fd
可能看到:
/dev/fd -> /proc/self/fd
这使应用程序可以将文件描述符当作路径使用。
例如:
echo"hello" > /dev/fd/1
相当于写入标准输出。
八、/sys:Linux 设备模型的结构化视图
8.1 /sys 的核心定位
/sys 挂载的是 sysfs。
它主要用于暴露 Linux 内核设备模型中的对象和关系,包括:
典型结构:
/sys/├── block/├── bus/├── class/├── dev/├── devices/├── firmware/├── fs/├── kernel/├── module/└── power/
/proc 更偏向运行状态和进程信息,/sys 更偏向设备模型和对象关系。
8.2 /sys/devices:真实设备拓扑
/sys/devices 是 sysfs 的核心目录,反映设备在内核中的实际层级关系。
例如:
/sys/devices/pci0000:00//sys/devices/platform//sys/devices/system/cpu//sys/devices/system/node/
PCI 设备可能按照总线层级展开:
/sys/devices/pci0000:00/0000:00:1f.6/
其中可能包含:
driverdriver_overrideenablefirmware_nodeirqmodaliasnetpowerresourcesubsystemueventvendordevice
8.3 /sys/class:按功能分类的设备视图
真实设备拓扑可能很复杂,因此 sysfs 提供 /sys/class,按设备类型进行分类。
例如:
/sys/class/net//sys/class/block//sys/class/tty//sys/class/power_supply//sys/class/thermal//sys/class/backlight/
查看网络接口:
ls /sys/class/net
可能输出:
docker0eth0lowlan0
查看网卡状态:
cat /sys/class/net/eth0/operstate
输出:
up
查看 MAC 地址:
cat /sys/class/net/eth0/address
查看 MTU:
cat /sys/class/net/eth0/mtu
查看网卡速率:
cat /sys/class/net/eth0/speed
某些虚拟网卡或驱动可能不支持 speed 属性。
8.4 /sys/block:块设备视图
查看系统块设备:
ls /sys/block
可能输出:
loop0nvme0n1sdasr0
查看设备大小:
cat /sys/block/sda/size
该值通常以 512 字节扇区为单位。
计算容量:
sectors=$(cat /sys/block/sda/size)echo $((sectors * 512))
更常用的工具是:
lsblk
但 lsblk 会综合 sysfs 和 udev 数据形成结构化输出。
查看 I/O 调度器:
cat /sys/block/sda/queue/scheduler
可能输出:
[mq-deadline] none
方括号中的值表示当前调度器。
查看设备是否为旋转磁盘:
cat /sys/block/sda/queue/rotational
常见含义:
1 → HDD 等旋转设备0 → SSD、NVMe 或虚拟块设备
8.5 /sys/bus:总线与驱动关系
查看总线类型:
ls /sys/bus
可能包括:
acpicpui2cpciplatformscsiusbvirtio
PCI 总线目录:
/sys/bus/pci/├── devices/├── drivers/├── drivers_autoprobe├── drivers_probe└── uevent
查看 PCI 设备:
ls /sys/bus/pci/devices
查看驱动:
ls /sys/bus/pci/drivers
设备目录中的 driver 通常是符号链接,指向当前绑定驱动。
例如:
readlink -f /sys/bus/pci/devices/0000:00:1f.6/driver
可能输出:
/sys/bus/pci/drivers/e1000e
这表示该 PCI 设备绑定到了 e1000e 驱动。
8.6 /sys/module:内核模块状态
查看已加载模块:
ls /sys/module
也可以使用:
lsmod
查看某个模块参数:
ls /sys/module/模块名/parameters
例如:
ls /sys/module/nf_conntrack/parameters
读取参数:
cat /sys/module/nf_conntrack/parameters/hashsize
并非所有参数都允许运行时修改。
更安全的模块管理工具包括:
modinfomodprobelsmod
8.7 /sys/fs:文件系统相关对象
常见目录:
/sys/fs/cgroup//sys/fs/bpf//sys/fs/ext4//sys/fs/xfs/
其中 /sys/fs/cgroup 是现代 Linux 资源控制的重要入口。
可以查看当前 cgroup 版本:
stat -fc %T /sys/fs/cgroup
如果输出:
cgroup2fs
通常说明正在使用 cgroup v2。
Docker、systemd 和 Kubernetes 都会依赖 cgroup 控制:
8.8 /sys/dev:通过设备号反向查找设备
/sys/dev 按设备号建立索引:
/sys/dev/block//sys/dev/char/
例如:
ls -l /sys/dev/block/8:0
可能链接到:
../../devices/.../block/sda
其中:
8 →主设备号0 →次设备号
这与 /dev/sda 的设备号相对应。
九、/proc 与 /sys 的区别
两者都由内核提供,也都可以通过文件接口读取,但定位不同。
| /proc | /sys |
|---|
| | |
| | |
| | |
| /proc/<PID> | /sys/class |
| ps | lsblk |
| | |
| | |
可以简化理解为:
/proc 回答:这个进程正在做什么?系统当前负载和内存如何?内核参数当前是什么?/sys 回答:这是什么设备?它挂在哪条总线上?绑定了哪个驱动?它属于哪类设备?
十、/dev:设备访问入口
10.1 /dev 中保存的不是设备数据本身
/dev 中的文件称为设备节点。
例如:
/dev/sda/dev/nvme0n1/dev/tty/dev/null/dev/random
这些文件不是硬件内容的普通副本,而是应用程序访问内核设备驱动的入口。
访问链路:
应用程序↓打开 /dev/sda↓VFS 识别设备文件↓根据设备号找到驱动↓驱动访问真实硬件
10.2 查看设备文件类型
执行:
ls -l /dev/sda /dev/tty /dev/null
可能看到:
brw-rw---- 1 root disk 4, 0 Jul 27 10:00 /dev/sdacrw-rw-rw- 1 root tty 5, 0 Jul 27 10:00 /dev/ttycrw-rw-rw- 1 root root 1, 3 Jul 27 10:00 /dev/null
第一位表示文件类型:
b →块设备c →字符设备
后面的数字:
4, 05, 01, 3
分别是:
主设备号 major次设备号 minor
10.3 主设备号与次设备号
主设备号用于定位设备驱动或设备类型。
次设备号用于区分同一驱动管理的不同设备。
例如:
/dev/sda → 8, 0/dev/sda1 → 8, 1/dev/sda2 → 8, 2
可以使用:
stat /dev/sda
或者:
ls -l /dev/sda
查看设备号。
10.4 字符设备与块设备
字符设备
字符设备通常以字节流方式访问,数据按顺序处理。
典型设备:
/dev/tty/dev/null/dev/random/dev/urandom/dev/zero
特点:
块设备
块设备以固定大小的数据块为基本访问单位,通常支持随机访问。
典型设备:
/dev/sda/dev/sda1/dev/nvme0n1/dev/mapper/ubuntu--vg-root
常用于:
文件系统通常构建在块设备之上:
/dev/sda1↓ext4↓挂载到 /
10.5 常见特殊设备
/dev/null
任何写入其中的数据都会被丢弃。
command > /dev/null 2>&1
读取时立即返回文件结束。
cat /dev/null
/dev/zero
持续产生零字节。
生成测试文件:
ddif=/dev/zero of=test.img bs=1M count=100
注意,该命令会真实写入 100MB 数据。
如果只需要稀疏文件,可以使用:
truncate -s 100M test.img
/dev/random 与 /dev/urandom
用于提供随机数据。
head -c 32 /dev/urandom | base64
现代 Linux 中,日常应用通常使用系统提供的安全随机 API,不应自行设计随机数方案。
/dev/tty
表示当前进程的控制终端。
echo"hello" > /dev/tty
即使标准输出被重定向,也可以尝试直接写向控制终端。
/dev/stdin、/dev/stdout、/dev/stderr
通常链接到:
/dev/stdin → /proc/self/fd/0/dev/stdout → /proc/self/fd/1/dev/stderr → /proc/self/fd/2
例如:
echo"error" > /dev/stderr
10.6 /dev/pts:伪终端
通过 SSH、终端模拟器或远程会话登录时,通常使用伪终端。
查看当前终端:
tty
可能输出:
/dev/pts/2
伪终端结构大致如下:
终端模拟器或 SSH↓PTY 主设备↓PTY 从设备 /dev/pts/2↓Shell
应用程序看到的通常是 /dev/pts/<编号>。
10.7 /dev/shm:共享内存文件系统
/dev/shm 通常是一个 tmpfs 挂载点。
查看:
findmnt /dev/shm
它用于 POSIX 共享内存,也可能被应用程序用作高速内存文件区域。
查看占用:
df -h /dev/shm
常见问题包括:
Docker 中可以配置:
docker run --shm-size=1g ...
10.8 /dev/mapper:设备映射器
/dev/mapper 常见于:
例如:
/dev/mapper/ubuntu--vg-root/dev/mapper/cryptdata
查看映射关系:
lsblkdmsetup ls
10.9 稳定设备名称
/dev/sda、/dev/sdb 等名称可能随设备发现顺序变化。
生产环境挂载配置更推荐使用:
UUID文件系统标签 LABEL/dev/disk/by-id//dev/disk/by-uuid//dev/disk/by-path/
查看:
ls -l /dev/disk/by-uuidls -l /dev/disk/by-idblkid
/etc/fstab 中推荐使用 UUID:
UUID=xxxx-xxxx /data ext4 defaults 0 2
而不是依赖:
/dev/sdb1 /data ext4 defaults 0 2
十一、设备节点是如何出现的:devtmpfs 与 udev
11.1 静态 /dev 的历史问题
早期 Linux 系统可能预先创建大量设备节点。
问题是:
现代 Linux 通常使用动态设备管理。
11.2 devtmpfs 的作用
内核发现设备后,可以通过 devtmpfs 创建基础设备节点。
例如检测到磁盘后生成:
/dev/sda
devtmpfs 主要保证设备节点能够及时出现。
11.3 udev 的作用
udev 是用户空间设备管理器,现代系统中通常由 systemd-udevd 提供。
其主要职责包括:
基本流程:
硬件接入↓内核识别设备↓内核创建设备对象↓产生 uevent↓systemd-udevd 接收事件↓匹配 udev 规则↓设置权限和符号链接↓设备出现在 /dev 中
11.4 查看设备 udev 属性
例如查看磁盘:
udevadm info --query=all --name=/dev/sda
查看完整属性链:
udevadm info --attribute-walk --name=/dev/sda
查看网络设备:
udevadm info /sys/class/net/eth0
11.5 监控设备事件
udevadm monitor
插入 U 盘或连接设备时,可以看到内核与 udev 事件。
更详细:
udevadm monitor --kernel --udev --property
这对排查以下问题很有帮助:
十二、/run:本次启动周期的运行状态中心
12.1 /run 的核心定位
/run 通常挂载在 tmpfs 上。
它用于保存系统从本次启动到关机期间的运行状态。
典型内容:
/run/├── containerd/├── docker/├── lock/├── log/├── mount/├── NetworkManager/├── redis/├── sshd/├── systemd/├── user/└── *.pid
它具有以下特点:
查看挂载:
findmnt /run
典型输出:
TARGET SOURCE FSTYPE OPTIONS/run tmpfs tmpfs rw,nosuid,nodev,noexec,relatime,size=...
12.2 为什么需要 /run
过去,运行状态可能分散在:
/var/run/var/lock/dev/.run
但 /var 在启动早期不一定已经挂载。
因此现代 Linux 使用 /run 作为启动早期就可用的统一运行状态目录。
现在通常可以看到:
ls -ld /var/run /var/lock
输出类似:
/var/run -> /run/var/lock -> /run/lock
12.3 PID 文件
服务可能将主进程 PID 写入:
/run/nginx.pid/run/sshd.pid/run/redis/redis-server.pid
查看:
cat /run/nginx.pid
可能输出:
1824
再检查:
ps -p "$(cat /run/nginx.pid)" -f
PID 文件用于:
但 PID 文件并不是判断服务是否健康的充分条件。
可能出现:
现代 systemd 服务很多情况下并不依赖传统 PID 文件,而是直接通过 cgroup 跟踪进程。
12.4 Unix Socket 文件
很多服务使用 Unix Domain Socket 进行本机进程间通信。
例如:
/run/docker.sock/run/containerd/containerd.sock/run/php/php8.3-fpm.sock/run/mysqld/mysqld.sock/run/systemd/private
查看 Socket:
ss -lx
查看文件类型:
file /run/docker.sock
输出可能是:
/run/docker.sock: socket
Unix Socket 的优点:
例如 Docker CLI 访问:
/run/docker.sock
有些系统中:
/var/run/docker.sock
实际上是通过 /var/run -> /run 指向同一个 Socket。
12.5 Docker Socket 的安全风险
能够访问 Docker Socket 的用户,通常拥有非常高的系统控制能力。
例如:
docker run -v /:/host ...
可以挂载宿主机根目录。
因此:
- 不要将 Docker Socket 暴露给不可信容器
- 不要让 Web 应用直接访问 Docker Socket
- 应对 Docker API 增加严格认证与访问控制
12.6 锁文件
/run/lock 用于保存锁状态。
锁文件用于避免:
查看:
ls -l /run/lock
需要理解:
锁文件本身的存在并不一定意味着资源仍被有效锁定。
有些锁通过:
实现方式不同,不能看到锁文件就直接删除。
12.7 /run/user/<UID>
登录用户通常有自己的运行时目录:
/run/user/1000
查看:
echo"$XDG_RUNTIME_DIR"
可能输出:
/run/user/1000
这里可能保存:
- PulseAudio 或 PipeWire Socket
目录通常具有严格权限:
ls -ld /run/user/1000
可能为:
drwx------ 10 user user ...
普通用户不应访问其他用户的运行时目录。
12.8 systemd RuntimeDirectory
systemd 可以自动为服务创建 /run 子目录。
服务单元示例:
[Service]User=appGroup=appRuntimeDirectory=myappRuntimeDirectoryMode=0750ExecStart=/opt/myapp/bin/start
systemd 启动服务时会创建:
/run/myapp
并设置正确的用户和权限。
服务停止后,可以由 systemd 清理。
这比在启动脚本中手工执行:
mkdir -p /run/myappchown app:app /run/myapp
更加规范。
十三、/run 与 /tmp 的区别
二者都可能基于 tmpfs,但用途不同。
不应将长期业务数据写入:
/run
因为重启后数据会丢失。
也不应将服务 Socket 和 PID 文件随意放在:
/tmp
因为 /tmp 是公共可写目录,存在权限和安全风险。
十四、四个目录之间如何协同
以一块新插入的 USB 磁盘为例。
14.1 内核发现设备
USB 设备接入后,内核通过 USB 和存储驱动识别设备。
在 sysfs 中创建设备对象:
/sys/devices/.../
并通过分类视图暴露:
/sys/class/block/sdb
或者:
/sys/block/sdb
14.2 创建设备节点
内核与 udev 协作,在 /dev 中创建:
/dev/sdb/dev/sdb1
并可能创建稳定链接:
/dev/disk/by-id/.../dev/disk/by-uuid/...
14.3 用户空间挂载设备
用户或桌面服务执行挂载:
mount /dev/sdb1 /mnt/usb
挂载信息会出现在:
/proc/self/mountinfo
也可以通过:
findmnt
查看。
14.4 运行状态写入 /run
桌面挂载服务、udisks 或其他服务可能在 /run 中保存:
因此,一个设备从被识别到可使用,可能同时涉及:
/sys →描述设备是什么/dev →提供设备访问入口/proc →展示挂载和进程使用情况/run →保存用户空间服务运行状态
十五、生产案例一:进程 CPU 使用率过高
假设 Java 进程 PID 为 4821。
15.1 确认进程
ps -p 4821 -o pid,ppid,user,%cpu,%mem,etime,args
查看可执行文件:
readlink -f /proc/4821/exe
查看工作目录:
readlink -f /proc/4821/cwd
15.2 查看线程
top -H -p 4821
找到高 CPU 线程,例如线程 ID:
4896
转换为十六进制:
printf'%x\n' 4896
假设输出:
1320
生成线程转储:
jstack 4821 > /tmp/jstack-4821.txt
搜索:
grep -n -A 30 'nid=0x1320' /tmp/jstack-4821.txt
这样可以将 Linux 线程与 Java 调用栈关联起来。
15.3 查看线程状态
cat /proc/4821/task/4896/status
也可以查看线程内核栈:
cat /proc/4821/task/4896/stack
读取内核栈通常需要 root 权限。
十六、生产案例二:应用提示文件句柄耗尽
应用报错:
Too many open files
16.1 查看当前打开数量
ls /proc/4821/fd | wc -l
16.2 查看进程限制
grep -i 'open files' /proc/4821/limits
例如:
Max open files 1024 524288 files
表示:
软限制 1024硬限制 524288
16.3 查看具体文件描述符
ls -l /proc/4821/fd | head -100
分类统计:
ls -l /proc/4821/fd 2>/dev/null \ | awk '{print $NF}' \ | sed 's/socket:\[[0-9]*\]/socket/' \ | sed 's/pipe:\[[0-9]*\]/pipe/' \ | sort \ | uniq -c \ | sort -nr \ | head
进一步使用:
lsof -p 4821
判断是否存在:
16.4 systemd 服务限制
查看服务限制:
systemctl show 服务名 -p LimitNOFILE
覆盖配置:
systemctl edit 服务名
写入:
[Service]LimitNOFILE=65535
加载并重启:
systemctl daemon-reloadsystemctl restart 服务名
仅提高上限不能解决文件描述符泄漏。必须先确定使用量增长是否合理。
十七、生产案例三:磁盘满了但找不到大文件
17.1 查看文件系统
df -hT
17.2 统计目录
du -xhd1 /var 2>/dev/null | sort -h
如果 df 与 du 差距很大,排查已删除文件:
lsof +L1
或者直接检查目标进程:
ls -l /proc/进程PID/fd | grep deleted
17.3 确认文件描述符占用
假设:
5 -> /var/log/app/app.log (deleted)
查看对应进程:
ps -p 进程PID -f
处理方式:
systemctl reload 应用服务
若应用不支持重载日志:
systemctl restart 应用服务
重启后重新检查:
df -hTlsof +L1
十八、生产案例四:网卡存在但无法联网
18.1 查看接口是否存在
ls /sys/class/net
18.2 查看接口状态
cat /sys/class/net/eth0/operstate
可能值:
updownunknowndormantlowerlayerdown
18.3 查看物理链路
cat /sys/class/net/eth0/carrier
常见值:
1 →有链路0 →无链路
18.4 查看驱动
readlink -f /sys/class/net/eth0/device/driver
查看 PCI 设备信息:
lspci -nnk | grep -A 3 -i ethernet
查看内核日志:
journalctl -k | grep -i -E 'eth0|link|firmware'
查看接口配置:
ip addr show eth0ip route
查看统计:
ip -s link show eth0
由此可以区分:
十九、生产案例五:Docker 命令无法连接守护进程
错误:
Cannot connect to the Docker daemon at unix:///var/run/docker.sock
19.1 检查 Socket
ls -l /run/docker.sock
或者:
ls -l /var/run/docker.sock
19.2 检查 Docker 服务
systemctl status docker --no-pager
19.3 查看日志
journalctl -u docker -n 100 --no-pager
19.4 检查 Socket 监听
ss -lx | grep docker
19.5 检查权限
stat /run/docker.sock
典型权限:
srw-rw---- root docker
检查当前用户组:
id
如果用户未加入 docker 组,可以使用:
sudo docker ps
将用户加入 docker 组意味着赋予接近 root 的系统控制能力,必须谨慎。
二十、容器中的 /proc、/sys、/dev 与 /run
20.1 容器并没有自己的内核
Docker 容器共享宿主机 Linux 内核。
容器中的:
/proc/sys/dev
仍然由宿主机内核提供,但受到以下机制限制:
容器看到的是经过隔离后的系统视图。
20.2 容器中的 /proc
容器内执行:
ps aux
通常只能看到同一 PID Namespace 中的进程。
容器内的 PID 1,可能对应宿主机上的另一个 PID。
宿主机查看映射:
docker inspect \ --format '{{.State.Pid}}' \容器名
20.3 容器中的 /sys
容器中的 /sys 通常会受到只读挂载和权限限制。
普通容器不能任意修改宿主机设备参数。
但使用以下高权限配置会显著降低隔离:
--privileged--cap-add-v /sys:/sys
生产环境应避免不必要的特权容器。
20.4 容器中的 /dev
Docker 会为容器提供一组受限设备,例如:
/dev/null/dev/zero/dev/random/dev/urandom/dev/tty
默认不会将宿主机所有设备暴露给容器。
需要使用特定硬件时,可以显式映射:
docker run --device=/dev/ttyUSB0 ...
GPU 通常通过容器运行时和设备映射机制暴露。
20.5 容器中的 /run
容器中的 /run 通常属于容器自身挂载命名空间。
它用于容器内部服务的:
如果应用依赖 /run/myapp,需要确保目录在容器启动时创建,或者使用 tmpfs:
services:app:tmpfs:-/run
二十一、安全风险与操作边界
21.1 不要随意写入 /proc/sys
例如:
echo 1 > /proc/sys/net/ipv4/ip_forward
虽然命令简单,但它会立即改变内核网络行为。
修改前必须:
21.2 不要随意写入 /sys
某些 /sys 属性写入后会立即影响:
例如某些系统允许:
echo 0 > /sys/devices/system/cpu/cpu1/online
这可能使 CPU 核心离线。
不能将 /sys 当作普通配置目录处理。
21.3 不要直接修改 /dev 块设备
以下命令可能直接破坏文件系统或磁盘数据:
ddif=/dev/zero of=/dev/sda
对块设备执行写操作前,必须确认:
lsblk -ffindmntblkid
特别要注意设备名称可能变化。
21.4 不要随意删除 /run 文件
删除 PID、Socket 或锁文件可能导致:
应先确认:
file 文件路径ss -lxps -p PIDlsof 文件路径systemctl status 服务名
21.5 不要将敏感信息暴露到日志
以下路径中可能包含敏感信息:
/proc/<PID>/environ/proc/<PID>/cmdline/run/*/*.sock/run/user/<UID>/
如果在命令行参数中传递密码:
application --password=secret
可能被其他有权限用户通过:
ps/proc/<PID>/cmdline
看到。
因此敏感信息更适合通过:
进行传递。
二十二、常用工具与虚拟文件系统的关系
许多 Linux 命令并不是凭空获取系统信息,而是读取虚拟文件系统或调用相应内核接口。
| |
|---|
ps | /proc/<PID> |
top | /proc/stat、/proc/meminfo、/proc/<PID> |
free | /proc/meminfo |
uptime | /proc/uptime |
lscpu | /proc/cpuinfo |
lsblk | /sys/class/block |
ip | |
ss | |
sysctl | /proc/sys |
lsmod | /proc/modules |
lspci | |
udevadm | |
findmnt | /proc/self/mountinfo |
lsof | /proc/<PID>/fd |
理解底层来源后,即使高级命令不可用,也可以直接从虚拟文件系统获得基础信息。
例如极简容器中没有 ps,仍可以执行:
ls /proc | grep -E '^[0-9]+$'
查找进程目录。
二十三、虚拟文件系统排查命令集
23.1 查看挂载类型
findmnt /procfindmnt /sysfindmnt /devfindmnt /run
23.2 查看进程信息
cat /proc/进程PID/statustr'\0'' ' < /proc/进程PID/cmdlinereadlink -f /proc/进程PID/exereadlink -f /proc/进程PID/cwdtr'\0''\n' < /proc/进程PID/environ
23.3 查看文件描述符
ls -l /proc/进程PID/fdls /proc/进程PID/fd | wc -lcat /proc/进程PID/limits
23.4 查看系统状态
cat /proc/cpuinfocat /proc/meminfocat /proc/loadavgcat /proc/uptimecat /proc/statcat /proc/diskstats
23.5 查看内核参数
sysctl -asysctl vm.swappinesssysctl fs.file-maxsysctl net.ipv4.ip_forward
23.6 查看设备和驱动
ls /sys/classls /sys/class/netls /sys/blockls /sys/busls /sys/modulereadlink -f /sys/class/net/eth0/device/driver
23.7 查看设备节点
ls -l /dev/sdals -l /dev/ttyls -l /dev/nulllsblk -fblkid
23.8 查看 udev 信息
udevadm info --query=all --name=/dev/sdaudevadm info --attribute-walk --name=/dev/sdaudevadm monitor --kernel --udev --property
23.9 查看运行状态
findmnt /runfind /run -maxdepth 2 -type s 2>/dev/nullss -lxls -l /run/lockls -ld /run/user/*
二十四、四大目录速查表
| | | |
|---|
/proc/<PID> | | | |
/proc/meminfo | | | |
/proc/loadavg | | | |
/proc/sys | | | |
/sys/devices | | | |
/sys/class | | | |
/sys/bus | | | |
/sys/module | | | |
/dev | | | |
/dev/pts | | | |
/dev/shm | | | |
/run | | | |
/run/user | | | |
/run/lock | | | |
二十五、建立正确的理解模型
不能将这四个目录简单理解为“几个特殊文件夹”。
更准确的模型是:
Linux 内核├──进程管理├──内存管理├──网络协议栈├──设备模型├──驱动系统├──文件系统└──资源控制↓通过 VFS 和其他内核接口↓映射为用户空间可访问的对象↓/proc /sys /dev /run↓ps、top、free、lsblk、ip、systemctl、Docker 等工具
其中:
/proc
让用户空间能够观察进程和内核运行状态。
/sys
将内核设备模型和对象关系结构化地展现出来。
/dev
让应用程序通过文件接口访问设备驱动。
/run
让用户空间服务保存当前启动周期中的通信端点和运行状态。
二十六、本篇总结
本篇需要掌握以下核心结论:
/proc、/sys、/dev、/run 中的大量内容不是普通磁盘文件,而是内核或运行时服务动态生成的接口- VFS 让普通磁盘文件、虚拟文件、设备节点和网络文件系统拥有相对统一的访问方式
/proc/<PID> 将每个进程映射为目录,可以观察命令行、环境变量、线程、内存和文件描述符/proc/meminfo、/proc/stat、/proc/loadavg 等文件是大量监控工具的数据来源/proc/sys 是内核参数接口,sysctl 是更规范的读取和修改工具/sys/sys/devices 展示真实设备拓扑,/sys/class 提供按功能分类的简化视图/dev 中的设备节点不是硬件数据副本,而是应用程序访问设备驱动的入口- 字符设备按字节流访问,块设备通常按数据块访问并支持随机寻址
- devtmpfs 负责提供基础设备节点,udev 负责权限、命名、链接和热插拔规则
/run 保存当前启动周期中的 PID、Socket、锁和服务运行状态,重启后通常消失/var/run 和 /var/lock 在现代系统中通常分别链接到 /run 和 /run/lock- Docker、systemd、SSH、数据库和桌面环境都会大量依赖
/run 中的 Unix Socket 和状态文件 - 容器共享宿主机内核,但通过 Namespace、cgroup 和权限控制获得受限的
/proc、/sys 和 /dev 视图 - 理解这些底层接口后,可以更准确地排查高 CPU、内存异常、文件句柄耗尽、磁盘空间不释放、设备驱动异常和服务通信失败等问题