Linux 上线跑一段时间后,新装一台机器,第一件事我永远是先ls /看一遍。你可能会觉得这有什么好看的,无非就是 bin、etc、usr 那几套。但说真的,很多干了几年运维的人,被问起“/usr 是干嘛的”都得愣一下,更别提遇到磁盘写满、命令找不着、软件装上跑不起来这类问题时的慌乱样了。
Linux 目录结构全解析是个老生常谈的话题,可越基础的东西越值得反复琢磨。目录结构不只是让你记住每个目录叫什么,它背后是一整套系统设计的哲学:什么东西放哪、什么数据可变、什么文件能删、什么目录重启就没了。把这些弄明白,你在排查问题时能少走很多弯路,也好理解为什么网上那些“优化脚本”动不动就让你清空这个目录、删除那个文件——有些是合理的,有些就是在坑你。
这篇文章我会从设计标准、核心目录拆解、实际运维场景、常见问题四个角度,把我这些年跟目录打交道踩过的坑和攒下来的经验一次说清楚。
1. 先搞清楚Linux为什么长这样:目录体系的设计思路与标准
很多人第一次接触 Linux 目录结构时,脑子里都是乱的:为什么/bin和/usr/bin里都有ls?为什么有的目录是空的?为什么/proc里全是数字文件夹?要理解这些,得先弄清 Linux 目录体系不是哪个工程师拍脑袋定的,而是有统一规范的。
1.1 FHS 标准到底在管什么
FHS,全称 Filesystem Hierarchy Standard,翻译过来就是“文件系统层级标准”。它的核心目标是让所有类 Unix 系统在目录组织上保持一致的风格,这样你从一台发行版换到另一台,或者从一套大型机迁到一个小服务器,都能快速找到配置文件、日志、程序所在的位置。
FHS 并不强制每个目录里放什么文件,它规定的是哪些目录必须存在、它们的定位是什么、彼此之间什么关系。比如它明确规定/bin存放的是“系统最基本的用户命令”,/etc放“本机配置文件”,/var放“会变大的运行数据”。你把这几个目录定位吃透,后面所有问题都迎刃而解。
不同发行版之间存在一些细微差异——比如RHEL/CentOS 系和 Debian/Ubuntu 系在/lib、/usr/lib的处理上就有些不一样——但大框架都是遵循 FHS 的。所以学一套,走遍各发行版都不慌。
1.2 三个维度看懂任何目录
要快速判断一个目录是干嘛的,我总结了三个维度:可共享还是不可共享、可变还是静态、系统必需还是用户可选。
- 可共享的目录,比如
/usr、/opt,可以放在网络文件系统上,让多台机器共用一套程序; - 不可共享的,比如
/etc、/boot,每台机器都必须有自己的副本,因为里面的配置和内核是针对特定主机的; - 可变的目录,比如
/var、/tmp,里面的数据会持续增长或频繁刷新; - 静态的,比如
/usr、/lib,安装完系统之后基本不再变化(除非你升级软件包)。
把这个矩阵画出来,你看任何目录都能快速归类。比如/var/log就是“不可共享+可变”,而/usr/share就是“可共享+静态”。分区规划时非常依赖这个分类,因为它直接决定了哪些数据需要单独挂载、哪些数据必须天天备份。
我还见过一种更实用的看法:Linux 的目录结构本质上是在做存取隔离——程序文件放/usr和/bin,配置放/etc,运行过程产生的临时数据放/tmp和/run,持久化的状态数据放/var/lib。这就像家里做饭:刀和锅放厨房,油盐酱醋放调料架,吃剩的菜放冰箱,当然不能把刀扔床底下去。
2. 核心目录逐个拆解:从根目录到临时目录,一次说明白
目录结构这东西,光看理论没意思,得真正走进每一个目录里翻一翻。下面我会按实际使用频率和重要性,把这几个最核心的目录挨个拆开讲。不光是“这个目录叫什么”,更重要的是“里面出了问题会怎样”。
2.1 根目录与系统启动链条:/bin、/sbin、/boot、/lib
/是整个文件系统的大树根,所有目录都从它分叉出去。但真正的秘密藏在根目录下的几个子目录里。先看/bin和/sbin。
历史原因,早期 Unix 系统会把系统启动时必须用到的基础命令单独放在/bin,把需要 root 权限才能执行的系统管理命令放在/sbin。现在的发行版大多数已经把/bin、/sbin做成了/usr/bin、/usr/sbin的符号链接,也就是说你看到的/bin/ls其实就是/usr/bin/ls。这是为了把根分区做得尽量小,让更多内容统一收到/usr下。理解这一点,你才不会被“为什么两个目录都有一堆命令”的现象整懵。
/boot目录里装的是内核镜像(比如 vmlinuz 开头的一堆文件)、initramfs 镜像和引导加载程序的配置文件。简单说,机器通电后,固件先加载引导程序,引导程序再去/boot里找内核。有些新手手贱去清“大文件”,把/boot里内核文件删了,重启直接进不了系统,这种事我见过不止一次。这个目录基本不用动,更新内核要靠安装包管理器统一处理。
/lib和/lib64在传统布局里是放系统启动阶段需要的共享库文件,现在各主流发行版同样把它们链接到了/usr/lib和/usr/lib64。当你下载一个绿色版软件,运行时报“cannot open shared object file”,十有八九就是程序依赖的.so动态库不在这些目录的搜索路径里。
2.2 配置、运行与设备:/etc、/var、/dev
/etc是 Linux 系统里我最敏感的一个目录,因为它存放了几乎所有服务的配置文件。从网卡的配置、DNS 设置,到 SSH 服务端配置、用户组信息,全都在这。在这个目录里改错一个字段,轻则某个服务起不来,重则整机开不了机。
拿最常见的例子说:/etc/passwd里记录了用户账户,/etc/shadow里存密码哈希,/etc/ssh/sshd_config控制 SSH 登录策略,/etc/sysctl.conf是内核参数配置。我强烈建议你在动这些文件之前先备份,哪怕只是cp一份带个后缀。
/var是“会变大”的目录,其中/var/log和/var/lib最典型。所有服务运行时的日志都打在/var/log下,系统排错首先翻这里;软件运行时产生的状态数据,比如数据库的文件、软件包的元数据,在/var/lib下。这个目录经常是磁盘空间告急的元凶,我们后面实操环节会专门聊怎么排查。
/dev是设备文件的存放点。Linux 里一切皆文件,硬盘、U 盘、终端、声卡在/dev下都以文件形式呈现。比如/dev/sda代表第一块硬盘,/dev/tty0是当前终端。你平时做磁盘分区、挂载操作时认的就是这些设备节点。
2.3 用户目录与软件资源:/home、/root、/usr、/opt
/home是普通用户的家目录所在地,每个用户一个子目录,用户的个人文件、配置、缓存都在里面。/root是管理员的家目录,普通用户一般无权访问,因为它的权限是 700。
很多人一看到/usr就以为是“用户目录”,这是一个经典误区。/usr全称其实是 Unix Software Resources,它是系统里软件资源的核心存放区。你可以把它看作整个系统软件的“第二根目录”:/usr/bin放可执行程序,/usr/lib放库文件,/usr/share放与架构无关的共享数据,比如文档、图标、时区信息。
刚接触 Linux 开发的人更容易混淆的是/usr/local。这个目录是给管理员手动编译安装的软件用的,FHS 规定/usr/local应当独立于系统的软件包管理器。你自己./configure && make && make install装出来的程序,默认就是进这里。这样做的最大好处是:就算你哪天把系统自带的软件搞崩了,/usr/local里的东西一般不受影响。
/opt是给第三方大型软件准备的,比如某些商业软件、集成开发环境,它们会把独立的目录放到/opt/软件名下。相比普通安装方式,这套布局的好处是卸载时只要删掉对应目录就能清干净。
2.4 虚拟文件系统与临时目录:/proc、/sys、/run、/tmp
这四个目录是新手最容易产生鬼打墙感觉的地方。先说/proc,它是一个不占用磁盘空间的内存虚拟文件系统,内核把运行中的信息实时映射到这里。/proc/cpuinfo里能看到 CPU 型号,/proc/meminfo里有物理内存状态,每个进程运行时的数据在/proc/<进程号>下。你在里面看到的所有文件大小都是 0,因为内容本身就是动态产生的,也正因如此,du统计磁盘空间时它会自动被忽略。
/sys和/proc类似,但它更侧重内核设备模型和驱动信息,比如查看和管理硬件属性。/run是一个 tmpfs(内存文件系统),存的是系统启动以来创建的运行时数据,比如 pid 文件、socket 文件,重启后清空,千万别在里面放持久化内容。
/tmp大家用得最多,它是所有用户都能写的临时目录。但要注意两点:第一,系统可能定期清理里面太久没动过的文件;第二,它具备 sticky bit 特性,表现在目录权限的 others 部分是 t,意思是你能在自己的文件里随便删,但删不了别人的。不少事故案例就是有人没搞清楚这点,试图在/tmp里维护什么持久数据结果重启后全没了。
3. 把目录结构用进实际运维和开发场景
知道每个目录是干嘛的只是入门,真正有价值的是在目录出问题时知道怎么处理。我会带你走三个最常见的实战场景,把前面讲的目录知识串起来。
3.1 磁盘空间不够时,先查哪几个目录
生产环境最容易遇到的告警就是磁盘空间不足。遇到这个情况,我一般不是直接rm -rf一个看着很大的目录,而是按顺序做三步定位。
第一步,用df -h看哪块分区满了。比如根分区/满了,而/data还很空,说明要清理目标在根分区下面。第二步,用du -sh /*(或对/var、/usr等大目录单独执行)从根目录往下逐层找,看哪个目录占用最大。第三步,进到占用最大的目录里再用du -sh * | sort -rh | head -20排行,揪出元凶。
# 查看各分区整体使用情况 df -h # 统计根目录下每个一级目录的占用,便于定位 du -sh /* 2>/dev/null | sort -rh # 深入某个目录,比如最常见的/var,继续定位 du -sh /var/* 2>/dev/null | sort -rh根据我经手的案例,/var/log里疯狂增长的某个服务日志是最普遍的原因,其次是/var/lib下数据库文件的膨胀,还有/tmp下没有被正确清理的临时文件。记住,du看到的是文件占用的块大小,不是文件逻辑大小,两者在稀疏文件等特殊场景下会不一致。
3.2 环境变量PATH配置中的目录角色
新人经常遇到的另一个问题是:明明软件装了,在终端里敲命令却提示 command not found。这时候就要检查 PATH 环境变量。PATH 是一个目录列表,系统按照顺序去这些目录里找可执行文件。
用whereis或which可以看某个命令的真实路径。比如which python3返回/usr/bin/python3,说明它是在标准系统目录里的。但如果你手动编译装到了/usr/local/bin,而你的 PATH 里没有包含这个路径,那命令就找不到。
# 查看当前PATH里有哪些目录 echo $PATH # 临时追加目录 export PATH=$PATH:/usr/local/mytool/bin # 永久写入用户配置文件 echo 'export PATH=$PATH:/usr/local/mytool/bin' >> ~/.bashrc source ~/.bashrc把命令放到/usr/local/bin的好处是它通常在 PATH 里比/usr/bin更靠前,所以你编译安装的新版本会自动覆盖同名的系统版本。这也解释了为什么我们用包管理器升级过的软件有时候不生效——很可能 PATH 的前面藏着另一个目录里的同名旧命令。
3.3 部署软件时文件该放哪里:目录落位思路
部署一个新服务的时候,你要考虑的不只是“能不能跑起来”,还有“维护的时候方不方便”。不同安装形式对目录的落位有很大差别:
- 用包管理器(apt、yum、dnf)安装的软件,会按照发行版约定自动分散到
/usr/bin、/etc、/var/lib、/var/log里,好处是升级方便、依赖关系清晰; - 手动编译安装的软件,默认进
/usr/local,相关文件集中在/usr/local/bin、/usr/local/etc等子目录,卸载时直接删除对应目录即可; - 厂商提供的绿色软件包,常常解压到
/opt下自己的独立目录,然后/opt/xxx/bin里放启动脚本; - 容器和虚拟环境的方式则完全没有固定目录概念,镜像内部自成一套,我们只要做好目录挂载映射即可。
一份软件装好之后,它往往同时会在静态区(/usr)和可变区(/etc、/var)落脚。做备份时,千万不要只盯着一处,更要重视/etc下对应服务配置文件的备份。
4. 常见问题与排查技巧实录
最后这部分我整理了几组高频出现的问题和排查思路,遇到同类情况可以直接照着查。
4.1 磁盘空间不足的隐藏杀手:被删除但仍被占用的文件
这是一个让很多人头疼的场景:df -h明明显示根分区用了 95%,但du -sh /一圈跑下来,把所有文件加起来,空间占用远没有那么大。这时候十有八九是有进程打开了一个文件,随后文件被删除,但进程还在写它的句柄,所以磁盘空间没释放。
排查方法是用lsof找带 deleted 标记的文件。
# 列出所有已删除但仍在打开中的文件,并查看它们占用的空间 lsof | grep deleted # 如果明确某个目录下挂了服务,可以针对性过滤 lsof +L1 /var/log | grep -i deleted找到对应的 PID 后,重启相关进程或者整服务,空间就释放了。注意千万不要在生产环境上直接盲目 kill 进程,先确认是什么服务,评估影响面再动手。
4.2 inode 耗尽导致“磁盘还有很多,但文件写不进去”
有些场景下明明df -h显示空间还有几十 G,但创建文件时系统提示 “No space left on device”。这时候要看df -i,因为 Linux 文件系统除了限制大小之外,还限制 inode 数量。inode 说白了是文件系统的“户口本”,每个文件都要占有一条记录。当磁盘里堆积了大量小文件(比如临时缓存、Swoole 运行的 socket 文件、邮件队列等),inode 被耗光,磁盘再空也写不了新文件。
# 查看inode使用率 df -i # 找到某个目录下海量小文件的具体位置,结合文件数量定位 find / -xdev -type f | wc -l解决思路很直接:删除无用的小文件,或者找到罪魁祸首目录做清理。如果某个应用本身就是高频创建临时文件的类型,那就得考虑把它的临时目录挂载到 tmpfs 上,直接在内存中读写,避免对磁盘 inode 的消耗。
4.3 改了 /etc 下的配置导致服务起不来或系统进不去
这是每个运维必会上的一课。有一次我修改了/etc/sysctl.conf,想着调大某个内核参数,结果重启之后服务直接起不来了。排查到最后发现是参数格式写错了,内核拒绝加载。
遇到这类问题别慌,按下面几步走:
- 如果只是某个服务起不来,先用
systemctl start 服务名查看错误输出,再查看/var/log/messages或/var/log/syslog里的相关记录; - 如果系统完全进不了图形界面或网络起不来,可以尝试进入单用户模式或救援模式,把出问题的配置先改回来;
- 从今往后养成好习惯:改任何一个
/etc下的核心配置之前,先做好备份。我习惯用cp保留一个带日期的原始版本,比如cp /etc/nginx/nginx.conf /etc/nginx/nginx.conf.bak.20240101。
在/etc上还有一条必须记住:很多软件在升级时会检查配置文件的哈希值,如果它发现你手动改过,可能不会直接覆盖你改完的文件,而是生成一个.rpmnew或.dpkg-dist新文件,让你自己合并。看到这类文件别觉得奇怪,它其实是系统在保护你的自定义配置。
4.4 临时目录、日志目录增长过快怎么有效治理
日志目录无限增长是新手最容易忽略的问题。处理思路并不是“扛到满了再删”,而是从一开始就做好轮转与限制。
logrotate是绝大多数发行版自带的日志轮转工具,它按配置定期切割日志、压缩、删除旧文件。你可以修改/etc/logrotate.d/下的配置来控制某些日志的保留频率和份数。例如对某个应用,我们希望日志一天切一次,保留 7 份,就可以写一段配置:
/var/log/myapp/*.log { daily rotate 7 compress delaycompress missingok notifempty copytruncate }这样即使日志暴涨,磁盘空间也会被限制在一个可控范围。如果你嫌logrotate麻烦,还可以直接在应用程序内部开启按大小切割日志的选项,比如 Nginx、Tomcat 等都有对应配置。
4.5 目录权限与使用者混淆导致的权限拒绝
最后说一个很容易踩的坑:即使你的文件在/usr/local/bin里、路径也在 PATH 里,但你执行不了。这往往是因为权限设置不合理。一个可执行文件必须至少满足两个条件:文件自身有 x 权限,以及调用者的身份能穿过文件所在目录的权限限制。
我用ls -l查看一个项目演示脚本的时候,经常发现有人手工vim创建出来的脚本权限是 644,也就是没有执行权限。这时候直接chmod +x 脚本名即可。但要注意,如果目录权限有问题,比如某个中间层目录是 700,其他用户永远无法进入,普通用户执行同一个脚本时依然会报 Permission denied。
# 查看文件权限 ls -l /usr/local/bin/xxx # 给脚本加执行权限 chmod +x /usr/local/bin/xxx # 查看目录权限,确定可穿越 ls -ld /usr /usr/local /usr/local/bin很多人只记住了chmod 777这个万能解,但生产中我真的不建议随手用 777,因为它把所有用户都变成了可读可写可执行,任何非授权用户都能篡改你的程序文件,安全风险极大。更合理的做法是:文件归属某个用户,目录按需给组或其他人开权限,执行位只给需要的使用者开。
最后再分享一个小技巧:遇到任何跟目录、空间、权限相关的疑难杂症,我的习惯是先跑一遍df -h、df -i、ls -ld、mount四个命令,把分区使用、inode、关键目录权限、挂载方式都看清楚,再往下排查。目录结构的知识看似基础,但它就像系统的骨架,哪天你发现某个目录里的文件消失得莫名其妙,或者某个软件的行为匪夷所思,回头对照一下它所属目录的定位,答案往往就浮出来了。