Linux目录结构全解:FHS、挂载点与排错地图
刚拿到一台别人维护过的服务器第一件让人头大的事往往不是业务跑不起来而是东西到底放哪了。应用装在哪个目录、日志被滚到哪去了、自己编译的程序为什么在另一台机器上就找不着——这些问题的答案最后都会指向同一个基本功Linux目录结构。它不像很多人想的那样只是张根目录下有哪些文件夹的清单而是一整套关于什么类型的文件该放在哪里的约定。背下这张清单不算本事能靠它快速定位问题、规划磁盘、避免误删才是有用的能力。这篇内容我打算按设计逻辑—具体目录分工—虚拟文件系统—挂载规划—排错实战—常见误区的顺序讲透。不管你是刚接触Linux的新手还是天天敲命令但没系统梳理过的老用户读完应该都能建立起一张清晰的目录地图并且能直接用到日常排查和部署里。1. 别急着背目录名先搞懂Linux为什么要长成树很多人学Linux目录结构的方式是直接背/etc放配置、/bin放命令、/var放变化的数据背完当时记住了过两周又混。真正记得牢的方式是先理解它背后的几个设计原则目录名字只是结论。理解了动机哪天真忘了某个目录的用途也能靠逻辑推出来。1.1 一棵从/长出来的树没有盘符这回事Linux的文件系统是一棵以根目录/为起点的树。所有文件、目录、设备、甚至内存里的状态最终都挂在这棵树的某个节点上。它没有Windows那种C:\ D:\的盘符概念一块新硬盘接进来必须挂载到树上的某个目录才能被访问。这个目录就叫挂载点。这个设计带来的直接后果是路径永远是唯一的。不会出现同一个文件在C盘D盘各有一份还互相打架的情况。你只要记住从根出发的路径就能定位到唯一目标。反过来说当你发现/data这个目录的大小在变它可能根本不是根分区的一部分而是另一块盘挂上来的——这一点在后面讲df和du的区别时会反复用到。提示路径开头的/表示从根出发的绝对路径它和目录名里的斜杠是两回事。新手最容易把/etc/nginx写成etc/nginx结果在当前目录下拼命找越找越迷糊。1.2 FHS标准它是约定不是铁律Linux目录的布局基本遵循FHSFilesystem Hierarchy Standard文件系统层次标准。这个名字听起来很官方但你要清楚它的性质它是一份推荐约定不是内核强制执行的规则。你可以自己建一个/myapp目录把东西全塞进去系统照样跑。但从维护角度说不遵守约定的代价是别人接手你的机器时要重新摸索一遍出了问题连日志都找不到。FHS的核心思路是按文件的生命周期和用途来分区存放大致分三类静态的、系统级的东西可执行程序、库文件、配置文件比如/bin、/lib、/etc。会不断变化的、运行时产生的数据日志、缓存、队列、数据库文件典型代表是/var。用户自己的东西个人文件、用户级配置比如/home下面的各个用户目录。把会变的和不变的分开好处非常实际。你可以给/var单独分一块盘日志写爆了也不会把根分区撑满导致整机卡死你可以把/home单独挂载重装系统时数据不受影响。这才是目录结构真正值钱的地方。1.3 和盘符思维的正面冲突从Windows转过来的朋友最容易在三个地方卡壳。第一是程序装在哪。Windows下装软件默认进Program FilesLinux里系统自带程序和你自己编译的程序是分开的前者在/usr/bin等目录后者通常该放/usr/local或用包管理器装到/opt。随手把编译产物丢进/bin下次系统升级就可能被覆盖掉。第二是配置在哪。Windows软件配置常常和程序放一起Linux里配置文件几乎都集中在/etc程序本体和配置是分离的。这带来的好处是程序升级不影响配置坏处是你删程序时容易忘了/etc下还留着一堆配置。第三是数据在哪。Windows里你习惯把东西放在用户文件夹下Linux里对应的是/home/用户名而系统管理员账户的数据在/root这两个是完全不同的地方权限也不一样。这个差异后面还会单独展开。2. 可执行文件的三层分工/bin、/sbin、/usr 到底谁管谁根目录下最容易让人混乱的就是那几个装命令的目录/bin、/sbin、/usr/bin、/usr/sbin、/usr/local/bin。它们之间存在历史的演进也存在明确的分工逻辑。搞清楚这套分工你才能回答我自己写的脚本放哪最合适这种天天遇到的问题。2.1 bin和sbin的那点区别先给个直观的对照目录存放内容普通用户能用吗典型命令/bin所有用户都要用的基础命令能ls、cp、cat/sbin系统管理与维护命令一般需要rootfdisk、reboot/usr/bin大多数用户级程序能python3、git/usr/sbin非关键的系统管理程序一般需要rootnginx、sshd/usr/local/bin你自己编译安装的程序看权限自编译工具s这个前缀就是 superuser超级用户的意思。放/sbin里的命令通常是给系统管理员用的比如分区、关机、网络配置这类操作普通用户直接跑往往权限不够。但要注意这个划分在现代发行版里越来越名存实亡——普通用户也能看到/sbin下的命令只是执行时权限拦着而已。2.2 /usr 合并那堆软链接是怎么来的如果你在较新的发行版上手敲ls -l /可能会看到这样的结果$ ls -l / lrwxrwxrwx. 1 root root 7 /bin - usr/bin lrwxrwxrwx. 1 root root 7 /lib - usr/lib lrwxrwxrwx. 1 root root 8 /sbin - usr/sbin/bin已经不是一个真目录了它是指向/usr/bin的软链接。这个变化叫usrmergeusr目录合并。历史上/bin和/usr/bin是两个独立的物理位置目的是让根分区保持极小能先挂载起来启动系统。后来磁盘容量不再是问题这种拆分反而带来麻烦比如同一个命令在两条路径下出现不同版本排查起来很折磨人。合并之后所有东西物理上都在/usr下老路径用软链接兼容。这个细节的实际意义在哪当你用find搜索某个命令、或者用du统计磁盘占用时如果没注意软链接可能会把同一份文件统计两遍或者顺着软链接绕进死循环。find默认不跟随符号链接就是为这类场景做的保护。2.3 /lib 与 /lib64动态库缺失时的第一反应/lib以及64位系统的/lib64存放的是共享库也就是.so结尾的动态链接文件。程序运行时依赖这些库缺了就会报经典的错误error while loading shared libraries: libxxx.so.1: cannot open shared object file看到这行报错排查方向很明确先确认这个库到底在不在系统里再用ldconfig检查动态库搜索路径。# 查找某个库文件的实际位置 find / -name libxxx.so* 2/dev/null # 查看某个程序依赖哪些库 ldd /usr/bin/yourprogram # 把 /usr/local/lib 加入搜索路径后刷新缓存 echo /usr/local/lib /etc/ld.so.conf.d/local.conf ldconfig自己编译安装的软件库文件经常被装到/usr/local/lib而这个路径默认不一定在搜索范围内于是程序一跑就报库找不到。很多人遇到这个问题的第一反应是把库手动拷到/lib下这属于能跑但不干净的做法——系统升级、库版本冲突时容易出事。正确姿势是把路径登记进/etc/ld.so.conf.d/再执行ldconfig。注意ldconfig会重建动态库缓存改完配置一定要执行它否则新路径不生效。这是个非常高频的明明配了却不生效的坑。3. /etc、/var、/home日常打交道最多的三个目录如果说前面那些目录是了解即可那这一节的三位就是天天要用的。配置文件、日志、用户数据几乎所有实际问题和它们有关。掌握这三个目录的组织方式日常运维的效率会明显不一样。3.1 /etc配置文件的地盘是怎么分的/etc是系统级配置的集中地。它的组织方式有个隐含规律按软件名建目录或命名文件。比如/etc/nginx/—— Nginx的配置目录/etc/ssh/sshd_config—— SSH服务端配置/etc/passwd、/etc/shadow—— 用户账户信息/etc/fstab—— 开机自动挂载配置/etc/hosts、/etc/resolv.conf—— 主机名解析和DNS配置这种一个软件一个文件或目录的方式好处是定位快。你需要改什么服务的配置直接去/etc下找同名文件就行。但它也带来一个新手常踩的坑改配置前不备份。直接编辑/etc/fstab写错一个挂载项重启后系统可能进不去改/etc/ssh/sshd_config配错了可能导致远程连不上。改这类关键文件之前先备份一份xxx.bak是最便宜的安全垫。另一个实用细节是/etc/skel。这个目录里的文件会在新建用户时被自动复制到用户的家目录。想给所有新用户预置统一的.bashrc或初始文件改/etc/skel就够了不用一个个手动配。这个是很多人建完用户才想起来的事情。3.2 /var磁盘悄悄涨满的元凶通常在这里/var存的是variable数据也就是运行过程中不断变化的内容。它的子目录各有分工值得单独列清楚子目录用途容易出问题的点/var/log系统与应用日志日志不清理会持续增长/var/cache缓存文件某些包管理器缓存可占数GB/var/lib程序运行状态数据数据库文件常放这里/var/spool队列数据邮件、打印任务堆积/var/tmp临时文件重启后不清空易被忽略磁盘用着用着满了的情况八成能在/var里找到答案。特别是/var/log和/var/log/journalsystemd的日志如果没配上限长年累积能吃掉好几个G。定位方法很直接# 看整体磁盘使用 df -h # 找出 /var 下占用最大的前十个目录 du -h --max-depth1 /var | sort -hr | head -10提示df -h看的是分区文件系统层面的用量du看的是目录层面的实际占用。两者对不上是常事——被删除但进程还占用的文件du看不到df却算进去。这时候要用lsof | grep deleted找出那个幽灵文件。3.3 /home 与 /root两个家的权限边界/home下面按用户名存放普通用户的数据/root则是root用户自己的家目录。它们最核心的差别在权限普通用户不能进/root而管理员可以进任意用户的家目录。这个不对称性在排查用户问题时很有用——用户说我的配置没生效你su - 用户名切过去一看往往是他改了系统级的/etc配置却没意识到自己家目录里的~/.bashrc才是真正生效的那份。这里顺带说下用户级配置目录。除了/home里那些显式的文件现代Linux还有个XDG规范把用户级配置和数据分得更细~/.config/—— 用户级应用配置~/.cache/—— 用户级缓存删了没影响~/.local/share/—— 用户级应用数据很多新装的软件尤其是桌面环境下的不再往家目录根下丢点文件而是遵守这套规范。理解这套目录你就知道为什么清缓存通常就是删~/.cache为什么重装某个桌面软件后配置还在——因为它在~/.config里躺着。4. 那些看不见的目录/proc、/sys、/dev、/run 的运行机制前面讲的都是实实在在躺在磁盘上的文件。但Linux目录树里还有一批假的目录它们不占磁盘空间内容全在内存里反映的是内核和硬件的实时状态。这类目录叫虚拟文件系统。不搞懂它们你对Linux目录结构的理解就缺了一大块。4.1 /proc把内核和进程状态摊开给你看/proc是所有虚拟文件系统里最常被用到的。它把内核内部的状态以文件形式暴露出来。每个进程都有一个以PID命名的子目录比如/proc/1234里面记录了该进程的几乎所有信息# 查看某个进程的启动命令 cat /proc/1234/cmdline # 查看进程打开的文件描述符 ls -l /proc/1234/fd # 查看内存信息 cat /proc/meminfo # 查看CPU信息 cat /proc/cpuinfo/proc/meminfo和/proc/cpuinfo就是很多监控工具的底层数据来源。free、top这些命令本质上是读取并格式化/proc里的文件。理解这一点你就能自己写简单的监控脚本而不用依赖任何工具。/proc下面的/proc/sys是个特殊区域它对应内核的可调参数。改这里的值等于临时调整内核行为比如# 临时开启IP转发重启后失效 echo 1 /proc/sys/net/ipv4/ip_forward临时改的东西重启就没了。要持久化得写进/etc/sysctl.conf或/etc/sysctl.d/下的文件再执行sysctl -p。这个临时 vs 持久的区分是调内核参数时最容易混淆的地方。4.2 /sys 与 /dev硬件和设备的映射关系/sys是sysfs文件系统比/proc更规整地描述了设备和驱动之间的层级关系。它按总线—设备—驱动的结构组织ls /sys/class/net/就能列出所有网络接口。日常手敲命令时你不会天天进/sys但做硬件调试、写驱动、查设备属性时它是唯一权威来源。/dev存的则是设备文件。Linux一切皆文件的理念在这里体现得最彻底硬盘是/dev/sda终端是/dev/tty随机数设备是/dev/random。现代Linux通过udev机制动态管理这些设备节点插上一个USB设备/dev下会自动出现对应的条目拔掉又自动消失。这不是磁盘上真有文件被创建删除而是内核通知udev去更新/dev的视图。注意/dev下的设备文件不要随便读写尤其别对裸设备直接dd一个手滑就可能覆盖磁盘分区表。这不是危言耸听是真实血泪。4.3 /run 与临时运行状态/run是个相对较新的目录较老的系统叫/var/run现在通常是软链接。它存的是系统启动后产生的运行时数据比如PID文件、socket文件、锁文件。特点是内容放在内存里tmpfs重启就清空。为什么PID文件放/run合理因为它本来就是临时的、和当前这次启动绑定的东西。程序启动时把PID写进/run/xxx.pid停止时删掉系统重启这个文件自然就没了不会留下过期状态误导下次判断。理解了/run的生命周期你就明白为什么有些服务脚本判断进程是否在运行时要去读/run而不是/var。5. 挂载点规划/boot、/opt、/srv、/mnt 到底该不该单独分区装系统时安装程序会让你规划分区。新手常常一路默认全部塞进根分区。这在小机器上没问题但只要有那么点数据量或者运维需求就值得认真想想哪些目录该独立挂载。这节讲清楚每个候选目录的实际用途和分区策略。5.1 几个候选独立分区目录的真实定位/boot存放内核和引导程序相关文件。很多方案建议它单独分区且不常写入。如果根分区用了LVM或者加密之类的复杂方案还能让/boot用简单格式单独放着引导更稳。/opt第三方大型软件包的地盘比如某些商业软件会整个装到这里。它的定位是可选的、附加的软件和系统自带程序分开。/srvservice data存放对外提供服务的数据比如网站内容。这个目录在实际使用中相当冷门很多发行版默认是个空目录但它的语义是明确的。/mnt和/media临时挂载点。/mnt传统上是管理员手动挂载用的/media更多被桌面环境用来挂载可移动介质。这个区分不是强制的但养成习惯后不会乱。判断某个目录要不要单独分区的原则很简单看它的数据增长是否会失控是否会影响到关键系统运行。日志会失控所以/var值得单独分用户数据会失控且重要所以/home值得单独分/boot涉及引导可靠性也常被单独处理。5.2 容量估算的实战经验分区最头疼的是容量给多少。给个基于常见场景的经验值仅供参考具体还得看你机器干什么目录建议容量估算依据/20~50G系统本身加软件桌面环境多留些/boot1~2G多内核版本共存时需要更多/var按日志量10G起步日志、数据库、缓存都在这/home视用户数尽量大用户数据的天然仓库swap内存的1~2倍或按需现代大内存机器可适当减小这里有个反直觉的点数据库的数据文件按FHS约定应该放到/var/lib下很多人在分区时给/var留得很少结果数据库一涨就把/var撑爆连带日志都写不进去。如果你机器的主要用途是跑数据库/var的容量必须按数据量来估而不能按日志目录这个印象来给。提示挂载关系随时可以用mount或findmnt查看。findmnt -T /var/log能直接告诉你某个路径属于哪个挂载点排查这个目录到底是哪块盘时非常顺手。6. 用目录结构反推三个高频场景的定位链路理论讲完了关键还是要用在排错上。目录结构的价值不在于背而在于你遇到问题时能顺着它快速缩小范围。下面用三个真实高频场景把前面的知识点串起来。6.1 磁盘写满但找不到大文件现象df -h显示根分区快满了可用空间告急但du扫下来却没发现明显的大目录。这个矛盾非常经典背后通常是文件被删除但进程仍持有句柄。排查链路# 第一步确认是哪个分区满了 df -h # 第二步从那个分区对应的挂载点开始找大目录 du -h --max-depth1 / 2/dev/null | sort -hr | head # 第三步如果df和du严重不符找被删除但仍被占用的文件 lsof L1 | grep deletedlsof L1会列出所有链接数小于1的文件也就是被删了但还开着的。找到占用它的进程后重启该进程空间就会释放。这一步是新手最容易卡住的地方——因为文件在目录树里根本看不见du自然扫不到得靠句柄这条线索。还有个更快的办法是直接看/proc/*/fdls -l /proc/*/fd 2/dev/null | grep deleted这个用法直接利用了/proc的进程视图把前面讲的虚拟文件系统知识用上了。6.2 命令找不到与动态库缺失的区分现象敲一个命令报command not found或者程序启动报库加载失败。这两个问题看起来都像东西不在但根因完全不同排查方向也不同。command not found是可执行文件查找失败问题出在PATH环境变量。查一下当前PATHecho $PATH which yourcommand type yourcommand如果which找不到但你知道文件确实存在多半是它所在目录没进PATH。这时候要么用绝对路径调用要么把目录加进PATH写进~/.bashrc或/etc/profile.d/下的文件。库缺失则是运行时找不到依赖问题出在动态库搜索路径用之前讲过的ldd和ldconfig处理。两个问题的表象相似本质一个是找不到主程序一个是找到主程序但依赖没齐。先分清是哪一类能省下大量瞎试的时间。6.3 服务起不来时该翻哪些目录服务启动失败按目录顺序过一遍通常能定位到大部分原因配置去/etc/服务名/下看配置有没有写错、路径有没有指对。日志去/var/log/下找该服务的日志或者用journalctl -u 服务名看systemd日志。运行时状态去/run/服务名/看PID、socket文件是否存在判断它是不是真的没起来。数据目录去/var/lib/服务名/看数据目录权限、磁盘空间够不够。可执行文件确认二进制文件位置和依赖库都正常。这个顺序不是死的但它体现了目录结构的一个核心价值不同类型的故障信息被放在了约定的位置。知道约定排查就是按图索骥不知道约定就变成到处乱翻。7. 新手常踩的几个坑以及背后的目录逻辑最后这部分挑几个我在实际使用中反复见到、也反复提醒别人的误区。每一条背后其实都对应着对目录结构某个理解上的偏差。7.1 把 /tmp 和 /var/tmp 当成一回事很多人以为这两个都是临时目录于是随便用。实际上它们的生命周期不一样/tmp通常会被系统定期清理有些发行版配置了基于时间的自动清理而/var/tmp的设计意图是重启后仍保留用于那些需要跨重启存活的临时数据。所以如果你写个脚本把需要跨重启保留的中转文件放到/tmp某天它可能就莫名消失了。反过来把一个巨大的临时文件一直丢在/var/tmp不管它会一直占着磁盘不清。选哪个目录取决于你的数据需不需要活过重启。7.2 自己编译的软件随手丢进 /bin这个前面提过这里再强调一次因为它太常见。/bin、/usr/bin是包管理器管理的地盘手动往里塞文件有三个问题升级时可能被覆盖、包管理器校验时会报文件冲突、卸载时容易留残余。正确的落点是/usr/local下对应的子目录或者干脆用一个独立目录再通过软链接放进PATH。# 编译安装时指定前缀是常规做法 ./configure --prefix/usr/local make make install这样可执行文件进/usr/local/bin库进/usr/local/lib配置进/usr/local/etc各归各位和系统管理的东西互不干扰。7.3 忽略了 /usr/local 的PATH优先级/usr/local/bin在PATH里的位置通常排在系统目录前面或后面取决于发行版默认配置。这个顺序决定了当同一个命令名在不同目录都存在时系统用哪个。你可以这样确认which -a python3它会列出PATH中所有匹配项从上到下就是实际查找顺序。理解这个顺序你才能在我明明装的是新版跑起来还是旧版这类问题里快速反应过来——大概率是旧版在PATH里排得更靠前。注意修改PATH建议写进~/.bashrc或/etc/profile.d/下独立文件别直接改/etc/profile主文件。用独立文件管理将来要撤销只删一个文件就行不会把主配置搅乱。7.4 中文文件名或解压乱码根源常在编码而不在目录热词里解压文件乱码是高频问题虽然不完全是目录结构问题但和文件命名、路径处理相关。典型情况是在Windows下打包的压缩文件文件名用GBK编码在Linux下解压时按UTF-8解释就乱码了。处理思路是明确指定编码# 用 unzip 指定编码解压 unzip -O GBK yourfile.zip # 或者用 7z 指定字符集 7z x -mcp936 yourfile.7z这个问题提醒我们目录结构和文件系统之上还有一层编码约定。跨平台搬运文件时编码和路径分隔符都会带来意外提前留个心眼能少走很多弯路。8. 我个人在实际操作中的一点体会Linux目录结构这东西刚学时觉得是纯粹的记忆负担用久了才发现它其实是一套非常讲究的信息架构。它把会变的和不变的、系统级的和用户级的、落盘的和内存里的这些维度通过目录位置表达了出来。你只要建立起这套映射很多问题根本不需要查资料——看到报错里提到某个路径脑子里大概就能浮现出它属于哪一类该去哪里找下一步线索。我自己的习惯是把根目录当成一份索引来用。遇到没见过的目录先ls一下看结构再读该目录下的README或man描述。时间长了/proc、/sys、/run这些虚拟目录会从神秘区域变成随手可查的窗口。另外强烈建议在改/etc下任何关键配置前先备份改/etc/fstab之前先在虚拟机里试一遍。这些习惯看起来笨但它们能让你在真正关键的机器上少出大岔子。还有个小技巧值得分享想快速理解一台陌生机器的目录布局先跑findmnt看挂载关系再du --max-depth1看各顶层目录的体量。这两个命令组合起来几分钟就能对一台机器的存储格局有个大致判断比逐个目录翻要高效得多。