Linux设备排查核心:/proc、/sys、/dev三目录原理与实战

📅 发布时间:2026/10/20 18:54:23
Linux设备排查核心:/proc、/sys、/dev三目录原理与实战
Linux下排查设备问题我打交道最多的三个地方就是/proc、/sys和/dev。身边不少朋友用Linux一两年lsblk、lspci、free这些命令背得滚瓜烂熟可真遇到系统不识别新插的硬件、搞不清某个设备的内核状态、或者想写个脚本自动监控硬件变化时就卡住了——不知道去哪查查到了又看不懂。这篇文章把这三大目录从原理到实操拆开讲透覆盖CPU、内存、磁盘、网卡、GPU、USB设备的查询方法附上我实际踩过的坑和排查经验。适合系统管理员、运维工程师也适合刚接触Linux但想真正理解设备管理机制的开发者。1. /proc文件系统内核的实时快照1.1 虚拟文件系统到底是什么/proc里装的东西不是真实磁盘上的文件它是内核对用户态暴露的一组实时接口。你可以理解为内核把当前运行状态通过文件的方式展示给你读这些文件就等于在问内核“你现在什么情况”写某些文件就等于在给内核下指令。文件大小显示为0不占用磁盘空间每次读取都是实时生成的输出——这就是它叫虚拟文件系统的原因。这个设计很巧妙。没有/proc之前想知道系统负载、进程列表、内存占用基本要靠内核提供专门的系统调用或者解析各种底层数据结构普通用户根本无从下手。有了/proc以后内核状态结构化地呈现在文件树里用cat一读就行脚本也可以直接解析。我早期排查线上问题时第一条命令永远是cat /proc/loadavg这个习惯到现在都没改。/proc挂载在系统启动时自动完成不需要额外操作。它在内存里占用空间几乎可以忽略不计但读取频繁时会有一定开销所以监控脚本不要几毫秒就读一次生产环境通常5秒到10秒采集一次就够了。1.2 CPU信息查询/proc/cpuinfo这是入门第一个要读的文件。/proc/cpuinfo包含系统所有处理器的完整信息我通常这样看cat /proc/cpuinfo输出会按processor编号列出每个逻辑CPU的信息。关键字段有这些processor逻辑CPU编号从0开始。physical id物理CPU插槽编号。查物理颗数看这一列去重后的数量。core id该CPU核在物理芯片上的编号。配合physical id能算出物理核心数。cpu cores每个物理CPU的核数。siblings每个物理CPU包含的逻辑CPU总数。model nameCPU型号比如“Intel(R) Xeon(R) Gold 6330”。stepping、cache size、flags步进版本、缓存容量、支持的指令集标志。看一颗物理CPU配了多少逻辑核最稳的计算方式是grep physical id /proc/cpuinfo | sort -u | wc -l # 物理CPU颗数 grep cpu cores /proc/cpuinfo | head -1 # 每颗物理CPU的核数 grep processor /proc/cpuinfo | wc -l # 逻辑CPU总数逻辑CPU总数 物理颗数 × 每颗核数 × 每核线程数。如果逻辑CPU数量是物理核数的两倍基本可以断定开启了超线程。我在某台双路服务器上就见过40核物理核心却显示80个逻辑CPU一开始吓一跳一查才知道超线程开了性能排查时必须分清楚这种差异否则压力测试数值会误导判断。/proc/cpuinfo的flags字段值得关注。例如某次开发环境要求支持AVX512指令集直接grep一下就知道有没有grep -o avx512f /proc/cpuinfo | head -1有这个输出说明CPU支持没有就说明你在这台机器上编译依赖AVX512的程序会收到非法指令错误提前筛掉能省不少排查时间。1.3 内存信息查询/proc/meminfo内存查询的老祖宗就是/proc/meminfo。free命令的数据来源正是这里但没有哪个老手会只看free的输出——因为/proc/meminfo的字段更细、更有参考价值。核心字段字段含义MemTotal物理内存总量MemFree完全未使用的内存量MemAvailable新进程实际可用的估算值比MemFree更准确Buffers块设备缓存Cached页面缓存文件读写的缓存SwapTotalswap分区总量SwapFreeswap剩余量Dirty等待写回磁盘的脏页数量Committed_AS当前已承诺的内存总量这里最容易被误解的是MemFree和MemAvailable的区别。MemFree只算完全空闲的页而Linux为了提升性能会把大量内存用于页面缓存当新进程申请内存时这些缓存页可以回收所以MemAvailable会大于MemFree。生产环境看可用内存直接看MemAvailable才能反映真实风险。有次报警显示MemFree跌破100MB但MemAvailable还有8GB其实虚惊一场恰恰是文件缓存把空闲页占满了。关注内存增长趋势时我习惯用Dirty字段做判断依据。Dirty值持续居高不下说明有进程在大量写文件写回速度跟不上产生的速度通常伴随IO瓶颈。这种情况从meminfo的字面数据里看不出来但Dirty增长曲线可以帮你定位是存储跟不上而不是内存泄漏。1.4 进程维度/proc/PID目录/proc不只是全局信息的集合每个正在运行的进程都有一个以PID命名的子目录这是排查进程问题的金矿。我排查进程异常时经常访问以下几个文件cat /proc/1234/cmdline # 该进程的完整启动命令多个参数用\0分隔 cat /proc/1234/status # 进程状态、内存、线程数、权限等 cat /proc/1234/environ # 环境变量列表 ls -l /proc/1234/fd/ # 该进程打开的文件描述符 cat /proc/1234/limits # 进程资源限制包括nofile和nproc最实用的是status文件里的State字段。R表示运行中S表示睡眠D表示不可中断睡眠——看到D状态频繁出现八成是IO卡住了。有一次MySQL写入变慢我遍历了所有进程的status发现好几个进程卡在D状态配合iostat一看磁盘IO队列深度接近上限问题很快定位到磁盘性能而不是数据库配置。fd目录也很经典。通过查看/pid/fd下的符号链接能知道进程到底打开了哪些文件ls -l /proc/$(pgrep -f server) | wc -l如果文件描述符数量逼近ulimit限制那就是典型的句柄泄漏前兆。我见过一个Java进程打开了两万多个文件句柄不释放最终被系统杀掉排查时靠的就是fd目录快速统计数量。1.5 其他常用/proc条目还有一些/proc条目是我日常用得极其频繁的/proc/loadavg前两列是1分钟、5分钟、15分钟的平均负载第三个字段是当前可运行进程数/总进程数。判断负载高低不能只看这个数字要结合CPU逻辑核心数。/proc/uptime第一列是系统开机秒数第二列是空闲秒数总和。/proc/interrupts各CPU的中断次数分布。多队列网卡绑核是否生效从这个文件可以看出端倪。/proc/net/dev各网络接口的收发字节数、包数、错误数、丢包数。网络监控脚本的核心数据源。/proc/partitions系统识别的磁盘分区及其块数快速确认盘有没有被识别。这些文件很少成为搜索热点但它们是系统性能排查的基础数据。我记得某次网卡丢包严重从/proc/net/dev统计值里看到rx_dropped持续增长定位到环形缓冲区太小通过调整网卡队列参数解决这类问题用别的工具反而不直观。2. /sys文件系统设备模型的众生相2.1 sysfs的设计缘起/proc解决的是内核状态输出问题但设备管理需要更清晰的结构。2.6内核引入了sysfs通过/sys目录对外展现设备层级模型。和/proc相比/sys更专注于“设备”本身设备之间怎么连接、驱动和设备的绑定关系、设备的属性如何读写。它把内核中的设备模型完整暴露出来。可以这样理解/proc是内核运行状态的“体检报告”/sys是设备拓扑图的“活地图”。设备树、驱动绑定、电源状态、热插拔信息去/sys找几乎都有。/sys也是虚拟文件系统不占磁盘空间但它有一个特点目录和文件之间的符号链接非常普遍以此表达设备之间的关联关系。比如/sys/class/net/eth0/device通常是指向PCI设备目录的符号链接顺着它就能从网络接口一路查到物理设备。2.2 /sys的核心目录结构/sys下面几个一级目录各管一摊新手容易看晕我梳理一下目录内容/sys/bus按总线类型组织的设备如pci、usb、platform/sys/class按设备功能分类如net、block、input、leds、thermal/sys/devices设备对象的核心组织目录真实设备树/sys/block块设备目录指向/sys/devices下对应设备/sys/module已加载内核模块及其参数/sys/kernel内核全局参数如mm、security等实际排查设备时我最常用的是/sys/class因为它按功能归类语义直观。比如想看网络接口有哪些ls /sys/class/net结果会列出物理网卡、虚拟接口、网桥、隧道设备等。配合循环遍历能拿到每个接口的详细属性for iface in /sys/class/net/*; do echo 接口: ${iface##*/} cat $iface/address # MAC地址 cat $iface/speed # 协商速率部分接口可能不支持 cat $iface/operstate # 接口状态up或down done这个脚本在云主机上很好用因为云主机的网卡命名可能是eth0、ens192或者其他随机名字通过/sys/class/net加PCI地址可以确定哪个物理槽位对应哪个接口对物理机上配网尤其重要。我处理过一次物理服务器双网卡绑定系统里网卡名和物理PCI槽位对不上靠的就是读/sys/class/net/eth0/device/address反向对应PCI地址再配合lspci定位到物理接口。2.3 通过/sys识别硬件信息/sys里几乎每个设备目录下都有若干只读属性文件。识别硬件时重点看这几个vendor供应商ID比如PCI设备的0x8086是英特尔。device设备ID结合vendor可以确认具体型号。modalias设备的“模态别名”内核模块自动加载时依据它来匹配驱动。uevent设备的属性键值对包含设备号、驱动名、物理位置等。driver符号链接指向当前绑定该设备的驱动目录。读取这些属性的命令很简单cat /sys/class/net/eth0/device/vendor cat /sys/class/net/eth0/device/device cat /sys/class/net/eth0/device/uevent例如输出vendor为0x8086、device为0x10fb就能查出这是Intel X710网卡。这个方法比lspci更底层因为这些信息直接来自内核设备模型不需要额外工具能确认驱动绑定的设备到底是不是预期的硬件。USB设备同样可以这样查cat /sys/bus/usb/devices/usb1/1-1/1-1:idVendor cat /sys/bus/usb/devices/usb1/1-1/1-1:idProduct在某些国产化系统和精简容器镜像里lspci、lsusb这些工具可能没装但/sys一定还在。我遇到过一台只装了最小计算环境的服务器没有lspci命令网卡识别全靠/sys的信息完成排障。2.4 热插拔与uevent机制/sys目录下很多设备都有uevent文件。它有两个作用一个是向用户态报告设备事件另一个是允许用户态主动触发设备重新扫描。对于可热插拔设备如USB设备内核会生成“添加”“移除”等事件udev守护进程监听并据此创建设备节点、加载驱动、执行自定义规则。手动触发设备重新扫描时向uevent写入change即可echo change /sys/class/net/eth0/device/uevent我在排查网卡聚合链路时用过这个方法。修改了网卡驱动参数后不想重启机器就靠这个命令让网卡重新初始化实测能触发驱动重新绑定部分场景下比ifdown/ifup更干净。不过要提醒一点这个操作在生产环境上要非常谨慎对正在承载流量的网卡执行会瞬间断网务必事先评估影响。2.5 只读与可写属性/sys下的大多数文件是只读的但你也会遇到可以写的文件。典型例子是/sys/class/leds下的每个LED亮度控制接口。服务器前面板常有可编程LED灯往brightness文件写数值能控制亮灭和闪烁。这在带外管理和自动运维中很有用脚本可以根据监控状态让LED变色报警。但凡是能写的风险都很高。/sys/class/thermal/cooling_device*/cur_state可以调节风扇等级/sys/devices/system/cpu/cpu*/cpufreq/scaling_governor可以修改CPU调频策略。乱写可能导致过热或者性能异常。我的原则是只读属性随便看可写属性除非明确知道后果否则绝对不碰。3. /dev目录设备节点的世界3.1 设备节点与驱动的桥梁/dev目录存放的是设备文件也叫设备节点。它不像/proc和/sys那样是纯目录树结构而是一个具体文件形式的节点集合。每个节点对应一个内核驱动暴露的设备接口用户态程序通过open、read、write、ioctl操作这些设备节点就能和设备交互。设备文件不占磁盘空间。统计/dev时经常看到大小为0的文件那是因为设备节点本身只是标识信息真正的数据在驱动里。创建它靠的是mknod命令指定主设备号和次设备号内核通过这两个编号找到对应驱动。在系统启动早期/dev由devtmpfs管理内核直接把已知的设备节点放进去随后udev根据规则补充和调整。这也解释了为什么你往/dev里手动创建的文件重启后会消失——它是内存中的虚拟文件系统。3.2 字符设备与块设备的区别/dev下的设备文件分两类。最常见的区分标志是ls -l输出结果的第一个字符ls -l /dev/sda /dev/tty0 brw-rw---- 1 root disk 8, 0 某月某日 /dev/sda crw--w---- 1 root tty 4, 0 某月某日 /dev/tty0b开头的是块设备以块为单位读写支持随机访问有缓存数据经过页缓存和缓冲区。典型代表是磁盘sda、ssda、分区sda1还有NVMe的nvme0n1。c开头的是字符设备以字节流为单位顺序读写不支持随机定位一般没有缓存。典型代表是串口ttyS0、虚拟终端tty以及输入设备event0。理解这个区别能帮你判断为什么对某个设备可以用dd直接读取而另一个设备上dd会卡住或者只能从头到尾顺序读。我在调试串口设备时用cat /dev/ttyUSB0直接看输出这就是标准的字符设备玩法。3.3 主设备号与次设备号ls -l输出中的两个数字是主设备号和次设备号。主设备号标识设备对应的驱动类型次设备号标识驱动内部第几个实例。例如/dev/sda显示的是8, 0其中8是SCSI磁盘驱动的主设备号0表示第一块盘中第0个分区。查看系统当前注册的全部设备号信息可以访问/proc/devicescat /proc/devices这个文件展示了字符设备和块设备两个区段里面列出的主设备号与/dev下节点的对应关系一目了然。当某个驱动没有成功加载或者设备节点缺失时从/proc/devices里能直接看到内核是否还认识这个设备号这对判断“是设备坏了还是驱动没挂上”很有帮助。手工创建设备节点的场景现在很少了因为udev会自动完成。但调试驱动时仍然会用mknod临时创建节点mknod /tmp/mytest c 240 0240是自定义驱动的动态主设备号。调试完删掉即可不需要担心污染/dev。3.4 udev动态管理与规则/dev里的节点不是一成不变的。USB设备插进去udev收到内核事件后立即创建节点并赋予自定义权限拔出时节点自动消失。这一套联动机制的配置在/etc/udev/rules.d/下。很多嵌入式项目里串口设备每次插入后设备名可能在ttyUSB0和ttyUSB1之间漂移导致程序连接的串口号不稳定。解决办法是用udev规则按设备的序列号、USB路径固定名字。比如创建/etc/udev/rules.d/99-usb-serial.rulesSUBSYSTEMtty, ATTRS{serial}A50285BI, SYMLINKmy_device写完后重新加载规则udevadm control --reload-rules之后/run/中会生成名为my_device的符号链接指向真实的ttyUSB设备。这种固定映射在做串口网关时非常实用我在树莓派上搞过类似方案彻底解决了设备重启后串口名称变化导致的通信中断问题。3.5 通过/dev直接访问设备的实操日常操作中用dd读写块设备最典型。备份分区或制作启动U盘时dd if/dev/sda of/backup/sda.img bs4M statusprogress dd if/path/to/system.iso of/dev/sdb bs4M statusprogress对磁盘分区直接操作要慎之又慎。dd操作时搞错if和of或者目标设备写错数据秒没。每次执行前我都习惯先lsblk确认设备名再执行dry-run或用status参数观察有没有读写。这个习惯让我避免过至少三次“灾难级误操作”。访问字符设备同样简单。读串口数据cat /dev/ttyUSB0向串口发送数据echo -n AT /dev/ttyUSB0串口设备读写时当前用户必须属于dialout组否则会报权限错误。把用户加入组以后重新登录即可生效usermod -aG dialout 某用户4. 三个目录的定位与实战对比4.1 三者分工一览三个目录经常被混在一起说但各自分工明确。对比表格放在这里遇到具体问题先看表定位目录本质主要内容典型场景/procprocfs虚拟文件系统内核运行状态、进程信息、系统统计查负载、内存、进程打开文件、内核参数/syssysfs虚拟文件系统设备模型总线、驱动、设备属性查设备型号、驱动绑定、热插拔事件/devdevtmpfs虚拟文件系统设备节点与符号链接访问设备、挂载分区、串口通信、dd备份三者之间也有交互。比如/proc/partitions告诉你系统分了哪些盘/sys/block里是块设备属性/dev下的节点是实际按块设备访问的接口。查一个磁盘的问题三个目录经常要配合着看。4.2 实战场景一磁盘信息三连查新插入一块NVMe磁盘查看它的完整信息。第一步确认系统是否识别cat /proc/partitions如果能看到nvme0n1说明内核已经认识这块盘。第二步看设备属性cat /sys/class/block/nvme0n1/size cat /sys/class/block/nvme0n1/device/model cat /sys/class/block/nvme0n1/device/serial第三步确认设备节点存在ls -l /dev/nvme0n1如果/proc/partitions能识别但/dev下没有节点通常是udev没来得及创建或者规则有问题可以手动触发udevadm trigger我处理过几次整列磁盘在重启后设备节点缺失的情况基本都是udev规则冲突最终靠udevadm trigger和查看dmesg解决。4.3 实战场景二网卡信息排查网卡问题排查是这三个目录配合得最典型的场景。先看接口名称与统计cat /proc/net/dev这个文件列出所有接口的流量统计排查丢包时重点看rx_dropped和tx_dropped两列。再看接口属性cat /sys/class/net/eth0/speed cat /sys/class/net/eth0/operstatespeed显示协商速率operstate显示接口状态。最后确认物理设备信息cat /sys/class/net/eth0/device/uevent一次网卡速度协商成100Mb/s的问题排查我就是因为看到speed100、operstateup而物理链路明明支持千兆才定位到网线或者对端交换机端口问题。如果只看/proc/net/dev不对比/sys属性很容易以为是流量统计或者驱动的问题。4.4 实战场景三USB设备的综合识别插入一个USB设备后ls /dev/ttyUSB* dmesg | tail -20dmesg能看到设备名创建的具体信息比如“cp210x converter now attached to ttyUSB0”。如果要确认设备的厂商和产品IDcat /sys/bus/usb/devices/2-1/idVendor cat /sys/bus/usb/devices/2-1/idProduct再配合udevadm查看设备事件udevadm info -a -n /dev/ttyUSB0这个命令输出的信息非常完整包括供应商、序列号、驱动信息写udev规则时直接照抄属性即可。我写固定串口规则时主要靠这个命令拿属性比盲猜规则关键词高效得多。4.5 一个综合信息收集脚本实战中我经常用一小段Shell脚本一次性收集系统硬件信息脚本核心就是读取前面讲的三个目录#!/bin/bash echo 硬件信息收集 echo --- CPU --- grep model name /proc/cpuinfo | head -1 grep -c processor /proc/cpuinfo echo --- 内存 --- grep -E MemTotal|MemAvailable /proc/meminfo echo --- 块设备 --- for disk in /sys/block/*/device; do name${disk%%/device} echo 设备: ${name##*/} cat $disk/model 2/dev/null cat $disk/vendor 2/dev/null done echo --- 网络接口 --- for iface in /sys/class/net/*; do echo 接口: ${iface##*/}, 状态: $(cat $iface/operstate) done echo --- 已识别分区 --- cat /proc/partitions这个脚本不依赖额外软件包在最小化系统、容器内外都能跑。生产环境中我把它当作巡检脚本的雏形根据自己的需求不断扩充字段最终沉淀成标准运维工具。5. 常见问题与排查技巧实录5.1 为什么/proc里找不到某些信息很多人习惯在/proc里找设备型号、厂商ID这些硬件静态信息但有些设备信息在/proc里根本不存在或者只有一部分。原因是/proc的设计初衷是暴露进程和内核状态不负责完整的设备拓扑展示。设备静态信息、驱动绑定关系、电源状态这些应该在/sys里找。我的经验法则是动态运行状态去/proc设备模型静态属性去/sys。例如磁盘IO统计在/proc/diskstats里但磁盘型号和序列号在/sys/block里。这两个目录混着找信息才齐全。5.2 设备节点缺失或者权限不足设备节点缺失时先不要急着mknod优先排查udev是否正常工作systemctl status systemd-udevd udevadm settle udevadm trigger --subsystem-matchblockudev规则冲突、服务异常退出都会导致设备节点创建失败。权限不足则往往是用户不在对应组里看ls -l确认ls -l /dev/ttyACM0如果权限是root:root普通用户需要加入dialout组并使用chmod临时放开。生产环境不建议直接用chmod 777改设备权限应该用udev规则配置组权限重启后依然生效。5.3 读取/sys文件时的常见坑/sys下的文件读取有两个我反复遇到的坑。第一部分属性文件只有在设备处于特定状态时才存在比如某些Intel网卡在接口down状态下没有speed文件直接cat会报No such file脚本需要做存在性判断再读取。第二有符号链接的路径要认真核对/sys/class/net/eth0/device是符号链接读取时如果网卡被reset或者重新绑定驱动链接会暂时指向不存在的路径脚本处理时要允许出错。我写巡检脚本时对/sys文件一律做兜底cat /sys/class/net/eth0/speed 2/dev/null || echo unknown5.4 性能和安全注意事项频繁遍历/sys和/proc会带来不小的CPU开销尤其是遍历/proc下所有PID目录时。机器上进程数上万个时用for循环遍历每个status文件消耗可能达到百分之几的CPU高峰期做操作会影响业务。建议采集间隔不要低于几秒遍历时使用快速工具或者在业务低峰期执行。安全方面要留个心眼。/sys和/proc中允许写入的文件虽然是少数但权限设置不当可能被低权限用户写入。我在安全加固时会检查是否放宽了/proc/sys下的权限并建议部署监控关键可写属性特别是那些能改内核参数和导出设备的接口。没有明确需求时保持内核默认权限最稳妥。5.5 从/dev反向确认设备是否被占用对设备节点操作前还有一个关键检查——设备是否已被占用。比如想卸载一个USB设备先看谁占用了它lsof /dev/ttyUSB0 fuser -v /dev/ttyUSB0这个信息对卸载U盘尤其重要。曾经有一次我直接umount分区提示target is busy排查半天才发现是一个后台脚本持续读取U盘内的日志文件。这种情况在/dev这种动态节点环境中非常常见提前用fuser确认占用进程能避免不少无谓的排查。最后说点实际经验把/proc、/sys、/dev这三套体系真正用顺之后最大的改变是不再遇到什么问题都先去装工具。很多设备排查场景下系统自带的最小环境就够了。我个人的习惯是第一眼先看/proc里和进程、负载、内存、网络统计相关的状态第二步打开/sys看设备型号、驱动绑定的细节最后需要真正操作设备、读写数据或挂载分区的再去动/dev节点。这个流程覆盖了从“系统认不认这个设备”到“设备能不能正常用”的全过程。最后分享一个小技巧写完任何巡检脚本都要在容器化的精简环境里先跑一遍。因为容器里可能没有/sys/class/block的部分子目录或者/proc下的进程目录权限受限这些脚本对文件存在性的鲁棒性要求很高。提前加上2/dev/null和默认值兜底再上生产环境能省掉很多半夜被叫起来查脚本问题的麻烦。