尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

误删 /dev 别慌:CentOS 设备节点重建与在线救援指南

误删 /dev 别慌:CentOS 设备节点重建与在线救援指南 去年帮朋友收拾一台 CentOS 7.9 服务器时我亲眼目睹了一条rm -rf /dev把运行中的系统打成了半瘫状态进程没死但新开终端的 shell 一直报错想执行任何读写设备的操作都失败。我当时的第一反应是赶紧重启好在朋友拦住了我最后花了两个小时在救援环境里重建 /dev才把系统和数据完整捞回来。这篇文章就把从“在线止损”到“救援模式重建”的完整路线写清楚。无论你是正在踩这个坑还是想提前把救援流程背下来这篇都值得花十分钟看完。尤其是最后那套工程化护栏能帮你避免重复事故。1. 误删 /dev 到底伤在哪故障机制、现象和重启后的连锁反应1.1 /dev 不是普通目录你删的是内核与你之间的接口Linux 里几乎所有硬件访问都要经过 /dev 下的设备节点。/dev/null、/dev/sda、/dev/ttyS0这些文件本质上是内核暴露给用户空间的一扇门。程序打开一个设备节点就等于和内核里的某个设备驱动建立通信管道。这里的关键是这些节点在 CentOS 7/8/9 上通常由 devtmpfs 和 udev 动态维护。devtmpfs 是内核自带的设备文件系统系统启动时挂载到 /dev内核会在设备注册时自动生成对应节点。udev在 CentOS 7 里实际是 systemd-udevd则在 devtmpfs 基础上补充符号链接、权限和应用层规则。也就是说/dev 不是静态目录你删除的节点有可能会被内核再次生成但前提是触发一次设备事件或重新挂载 devtmpfs。很多人以为 /dev 只是几个符号链接删了还能自动回来。现实是如果你删的是运行中的 devtmpfs节点不会立刻全部恢复因为内核只在设备热插拔或事件发生时重建节点。更麻烦的是udev 的规则状态也可能受影响部分自定义链接会彻底丢。而一旦你直接重启系统启动早期阶段需要读取 /dev/console、/dev/null、/dev/disk 等节点缺失时就会卡在开机阶段或者直接 panic。1.2 删掉之后你会遇到哪些具体症状在线阶段 vs 重启阶段在线阶段最典型的症状是“系统还在但很多事干不了”命令重定向失败。比如echo test /dev/null直接报 No such file or directory。任何依赖 /dev/tty、/dev/pts 的程序异常。ssh 进来后 shell 显示正常但新程序无法分配终端。运维工具调用磁盘分区、文件系统检查时报找不到设备。报错常见的还有/dev/null: No such file or directory、unable to open /dev/fd之类。如果你抱着侥幸心理重启情况会从“半瘫”变成“无法进入系统”。开机阶段 initramfs 切换根目录时需要挂载根设备而这个挂载动作往往依赖 /dev 下的设备节点就算 initramfs 能自己找到根设备切换后 systemd 在 sysroot 里找不到 /dev/console也会出现 kernel panic 或卡在黑屏。所以误删之后的第一条铁律是先不要重启先用在线方式修复。2. 还有 shell 就先做止损在线重建 devtmpfs 的最短路径2.1 先看挂载关系别急着乱建目录很多人在 /dev 被删后第一反应是mkdir /dev然后手动创建一堆空文件。这是最没用的做法。你应该先确认当前 /dev 有没有被卸载mount | grep on /dev cat /proc/mounts | grep /dev 正常系统会显示/dev on /dev type devtmpfs (rw,nosuid,size...。如果 /dev 仍然挂着 devtmpfs说明只是目录里的文件被删了文件系统本身还在。这个判断非常重要因为对于 devtmpfs最快的恢复方式不是手工建文件而是重新触发内核生成节点。如果 mount 输出里看不到 /dev 挂载那可能是误删过程中把挂载点搞得不可见。此时再重新挂载mkdir -p /dev mount -t devtmpfs devtmpfs /dev在 CentOS 7.9 上执行这条命令后内核会立刻按当前设备模型重新生成基础节点。这个过程很快几秒钟到十几秒。2.2 重建节点集合的四个动作我踩过坑之后的建议是按下面顺序执行不要跳步第一步重新挂载或二次挂载 devtmpfs。如果 /dev 挂载还在直接mount -o remount,rw /dev如果挂载点没了则mount -t devtmpfs devtmpfs /dev这里为什么要remount而不是卸载再挂载因为当前系统里所有打开的 fd 可能还指向 /dev 下的老节点直接卸载会让活跃进程彻底失去访问通道触发更多连锁故障。remount 能保持文件系统实例不变只让内核重算节点集合。第二步触发 udev 重新扫描硬件设备。udevadm trigger --typesubsystems --actionadd udevadm trigger --typedevices --actionadd有人只执行udevadm trigger这通常够了但我会强烈建议同时带类型参数。原因在于trigger的默认行为虽然会重放设备事件但某些驱动需要在add动作下才会重新生成节点而子系统事件可以补上总线层面的遗漏。第三步让 systemd-udevd 重新读取规则并处理事件。systemctl restart systemd-udevd udevadm settle很多人会忽略这一步认为 trigger 就完了。实际上 trigger 是往 udev 的队列里塞事件如果 udevd 自身状态不对比如规则数据库加载不完整事件会一直卡住。restart 后强制重建状态再用settle等待队列清空。第四步验证关键节点是否齐全。ls -l /dev/null /dev/zero /dev/urandom /dev/tty /dev/console /dev/sda这里有个容易误判的点如果你的根分区是 LVM还得检查/dev/mapper/下是否有对应的卷组逻辑卷节点。缺少/dev/mapper/centos-root这种节点时在线系统可能还能跑但下次启动大概率失败。在线修复的成功率很高前提是你别在修复过程中重启。修完之后我还建议顺手把关键服务重启一遍因为部分进程是在设备节点缺失时启动的可能已经进入了降级状态。3. 启动黑屏或卡在开机阶段急救模式/initramfs 下恢复 /dev3.1 从 grub 进入 initramfs 交互 shellrd.break如果你是在误删后重启系统停在“Give root password for maintenance”或者黑屏说明 initramfs 阶段还能救。CentOS 的 initramfs 由 dracut 生成自带一个交互 shell 开关。重启到 grub 菜单时选中当前内核启动项按e进入编辑模式找到以linux16或linux开头的那一行在行尾加上rd.break然后按Ctrlx引导。系统会进入 dracut 的 early shell此时真正的根文件系统通常已经挂载在/sysroot默认只读。你需要把它改成可写然后在 /sysroot 下重建虚拟文件系统mount -o remount,rw /sysroot mount -t devtmpfs devtmpfs /sysroot/dev mount -t proc proc /sysroot/proc mount -t sysfs sysfs /sysroot/sys mount -t devpts devpts /sysroot/dev/pts注意顺序先挂 devtmpfs 再挂 devpts。devpts 是给伪终端用的它需要 devtmpfs 先存在。此时再 chroot 进去chroot /sysroot /bin/bash进了 chroot 后执行udevadm trigger --actionadd udevadm settle systemctl restart systemd-udevd exit然后按Ctrld退出 chroot输入exit继续引导。整个流程约 5 到 10 秒就能重新生成 /dev 节点大多数情况下比安装盘救援更快。3.2 若 rd.break 不适用走 CentOS 安装盘 rescue 模式有时候 dracut 参数不生效或者你连 grub 界面都看不到那就用 CentOS 安装 ISO 启动。选择Troubleshooting再选Rescue a CentOS Linux system系统会自动发现硬盘上的 Linux 安装并挂载到/mnt/sysimage。这个模式下救援环境已经帮我们挂好了 /proc、/sys、/dev但它挂载的 /dev 是救援环境自己的 devtmpfs不是目标系统的。所以先确认分区被正确识别lsblk pvs lvs如果输出为空很可能是 LVM 卷组没有激活。激活后再挂载所有逻辑卷vgchange -ay mount -o rw /dev/mapper/centos-root /mnt/sysimage然后补挂目标系统的虚拟文件系统mount -t devtmpfs devtmpfs /mnt/sysimage/dev mount -t proc proc /mnt/sysimage/proc mount -t sysfs sysfs /mnt/sysimage/sys mount -t devpts devpts /mnt/sysimage/dev/ptschrootchroot /mnt/sysimage /bin/bash在 chroot 内部执行udevadm trigger udevadm settle systemctl restart systemd-udevd最后退出重启。这个方法是大多数资料推荐的优点是不依赖原系统的 grub 配置缺点是如果 /dev 被删时殃及了 udev 规则目录/etc/udev/rules.d你需要额外对比备份或借助 rescue 环境的规则补齐。不过正常情况下规则目录和 /dev 不在一起误删一般只是删了挂载点内容不会连规则一起消失。3.3 chroot 之后最容易漏掉的两件事我在帮朋友修复时第一次 chroot 进去后节点确实重新生成了但网络服务起不来网卡接口名 eth0 变成了随机名。原因很简单udev 的网卡命名规则依赖/etc/udev/rules.d/70-persistent-net.rules这个文件在旧系统里可能已经被 systemd 的 link 文件取代。如果误删期间把/run/udev下的状态清理掉了net 接口名就得重新绑定。解决办法是进入 chroot 后查看ls /etc/udev/rules.d/ ls /lib/udev/rules.d/只要这两个目录还在90% 的场景都能恢复。另一个容易漏的挂载点是/dev/shm。systemd 在正常启动时会自动挂载它但在手动 rescue 环境里没人帮你挂。chroot 后执行mkdir -p /dev/shm mount -t tmpfs tmpfs /dev/shm忘记这一条系统虽然能启动但很多依赖共享内存的应用比如某些数据库、桌面程序会在运行时报共享内存不足。4. 标签完全失效时手动重建关键设备节点与主、次设备号的取号方法4.1 必须有这些节点系统才能活下去自动挂载 devtmpfs 是首选但也存在两种需要手工 mknod 的情况一是系统内核或救援环境没有启用 devtmpfs常见于老旧的精简救援盘二是你想在无法自动生成节点的故障环境中手动造出最小可用的设备集合。这时你要记住设备节点由“主设备号 次设备号”唯一标识。字符设备和块设备的编号规则不同创建参数也不同。下面是 CentOS 系统最常用的一组关键节点我整理成表方便对照节点路径类型主设备号次设备号权限用途/dev/null字符13666黑洞设备重定向常用/dev/zero字符15666生成零字节流/dev/urandom字符19666非阻塞随机数/dev/random字符18666阻塞随机数/dev/tty字符50666当前终端/dev/console字符51600系统控制台/dev/ptmx字符52666伪终端主设备/dev/loop0块70660回环设备挂载镜像用/dev/sda块80660第一块 SCSI/SATA 盘/dev/sda1块81660第一块盘的第一个分区比如创建 /dev/nullmknod -m 666 /dev/null c 1 3创建 /dev/sdamknod -m 660 /dev/sda b 8 0但这里一定要提醒主设备号 8 只对应传统的 SCSI/SATA 设备。在 KVM 虚拟机里virtio 磁盘通常使用 250 以上的动态主设备号nvme 设备的主设备号也不是 8而是 259。死记硬背这张表去手动建 nvme 节点必然失败。4.2 从 sysfs 里读取真正的主次号避免背错号最可靠的做法是从/sys/block/下读取每个块设备的真实编号因为 sysfs 由内核直接维护永远和驱动当前状态一致。查看 /dev/sda 的真实主、次设备号cat /sys/block/sda/dev输出类似8:0对应主设备号 8次设备号 0。查看分区cat /sys/block/sda/sda1/dev输出8:1有了这个值再配合 mknod 就万无一失。对于 LVM 逻辑卷设备节点路径可能在/dev/mapper/但底层设备在/sys/block/dm-0里。查看方式for dev in /sys/block/dm-*; do echo $dev - $(cat $dev/dev) done注意/sys/block 里可能看不到 dm 设备需要先pvs、lvs确认卷组是否激活。激活后 dm 设备就会出现。4.3 批量生成 sd 节点的小脚本如果磁盘分区较多一个个手敲太慢。我写过一个小脚本遍历 /sys/block 下的 sd 设备及其分区自动生成对应节点for dev in /sys/block/sd*; do name$(basename $dev) major_minor$(cat $dev/dev) major${major_minor%:*} minor${major_minor#*:} [ -e /dev/$name ] || mknod -m 660 /dev/$name b $major $minor for part in $dev/${name}*; do [ -e $part/dev ] || continue part_name$(basename $part) part_mm$(cat $part/dev) part_major${part_mm%:*} part_minor${part_mm#*:} [ -e /dev/$part_name ] || mknod -m 660 /dev/$part_name b $part_major $part_minor done done在手动创建完基础节点后再挂载 devtmpfs 也可以但如果 devtmpfs 不可用这组 sd 节点基本够你启动根文件系统了。创建完检查一下ls -l /dev/sd* fdisk -l /dev/sdafdisk 能正常输出分区表说明节点已经生效。5. 手边没有救援盘时的土办法把故障盘挂到另一台 Linux 上做恢复5.1 让另一台机器正确识别故障盘如果手上没有 CentOS 安装盘也没有 LiveCD你还有一条路找一台同样能启动的 Linux 机器把故障盘接上去。这里的关键不是直接 chroot而是先确保另一台机器的内核能识别故障盘的所有分区。以 SATA 盘为例接到第二台机器后执行lsblk如果能看到对应设备说明内核已经识别。看不到就检查线缆、电源或者主板 SATA 口必要时用dmesg | tail看内核日志。识别成功后不要着急挂载先记录分区表fdisk -l /dev/sdb gdisk -l /dev/sdb这一步的价值在于即使后面误操作你手里有分区表的完整副本。我一般会顺手存一份到故障盘之外的地方比如 U 盘fdisk -l /dev/sdb /mnt/usb/sdb_fdisk_backup.txt5.2 激活 LVM 并挂载目标根分区很多 CentOS 系统根分区在 LVM 里接上第二台机器后vgchange -ay才能激活卷组。执行后确认逻辑卷列表vgchange -ay lvs然后挂载根逻辑卷mkdir -p /mnt/root mount -o rw /dev/mapper/centos-root /mnt/root如果根分区是普通分区直接挂/dev/sdb2之类即可。挂载成功后补挂 /dev、/proc、/sysmount -t devtmpfs devtmpfs /mnt/root/dev mount -t proc proc /mnt/root/proc mount -t sysfs sysfs /mnt/root/sys mount -t devpts devpts /mnt/root/dev/pts这时 chroot 进去修复手段和前面小节没区别chroot /mnt/root /bin/bash udevadm trigger udevadm settle systemctl restart systemd-udevd用这台“别人家的机器”做修复要注意别在原机器没有断电的情况下热拔硬盘容易损坏文件系统拔盘前先umount /mnt/root再关机取下。还有一点修复完成后第一次启动目标机器时建议先进入 grub 菜单加上nomodeset和rd.debug查看启动日志避免因为显示驱动问题误以为 /dev 没修好。5.3 修复完成后先别急着接回生产环境我在朋友那台机器上就是通过这种方式恢复的。当时接第二台机器修复完成后我没有直接把盘接回原服务器而是先在第二台机器上加一个调试内核参数在/boot/grub2/grub.cfg对应启动项里临时加rd.break启动确认 initramfs 能生成 /dev 后才正常引导。这样多做一次验证能提前发现一个隐蔽问题如果故障盘原来的 initramfs 里没有包含与第二台机器硬件匹配的驱动那接回原机可能没问题但你没法在第二台机器上完整测试。先用 rd.break 确认根文件系统里的 /dev 能生成至少保证系统在救援关键路径上是通的。6. 一台机器干倒之后我立的规矩防止再次误删的工作习惯6.1 危险命令必须有护栏乖系统管理员之所以会手滑多半是命令写得太顺手。最常见的就是在脚本里使用变量时没有判空结果rm -rf $dir/里的 dir 为空实际执行成rm -rf /一路删到根。或者本来想rm -rf /dev/tmp少敲一段就变成了rm -rf /dev。我自己的护栏有三道第一脚本开头检查关键变量。比如[ -z $WORKDIR ] echo WORKDIR is empty, abort exit 1只要变量为空立刻终止绝不执行后续 rm。第二不要在根目录下直接操作 /dev、/sys、/proc 这些特殊挂载点。需要清理时先进入具体目录再使用相对路径白名单写法像这样cd /var/tmp/cleanup rm -f file1 file2用rm -f接具体文件名而不是rm -rf *。第三把 rm 的危险性提升到不可逆操作级别。在交互式 shell 里可以给 rm 加个别名例如alias rmrm --preserve-rootCentOS 的 coreutils 默认已经包含--preserve-root保护但有些精简版或者旧版可能没启用手动加上更稳妥。6.2 不备份 /dev 的节点但要备份能重建它的元数据很多人问“/dev 被删我提前 tar 一份 /dev 不就行了吗”我的回答是不推荐也没必要。devtmpfs 下的节点是动态的tar 出来的旧节点往往与当前内核设备号不一致恢复时反而添乱。真正需要备份的是分区表、LVM 元数据、udev 自定义规则和网卡命名规则。分区表可以用gdisk /dev/sda的 backup 功能导出LVM 元数据可以用vgcfgbackup导出。这些文件很小却能在灾难时救回整个卷组结构。比如vgcfgbackup -f /backup/vg_centos_backup.txtudev 规则目录则定期压包放到另一个分区或远端tar czf /backup/udev_rules_$(date %Y%m%d).tar.gz /etc/udev/rules.d有了这套东西即使误删 /dev 后又殃及了规则目录也能快速恢复自定义符号链接和权限。6.3 定期用虚拟机演练救援流程最后一条是我在事故之后养成的习惯每半年在虚拟机上做一次“误删 /dev 救援演练”。具体做法很简单开一台 CentOS 虚拟机拍快照然后在系统内执行rm -rf /dev/*断掉快照权限强制重启逼着自己只用命令行从 rd.break 或安装 ISO 里把系统救回来。第一次演练我只用了二十分钟练熟之后五分钟内就能重建 /dev。这个过程带来的收益是实打实的真到生产环境出事那天你不会对着屏幕手忙脚乱脑子里的命令序列会自动跑出来。我个人最大的体会是误删 /dev 这类事故技术上往往不是最难解决的难的是判断“当前这一步该做什么”。在线阶段别急着重启重启之后先走 rd.break实在不行再用安装盘和 LiveCD最后才轮到手工 mknod——这个顺序牢记于心CentOS 系统的数据大概率都能保住。
返回列表