面对一台不熟悉的 Linux,最容易做的事是先运行 ls /,然后被几十个目录名淹没。目录清单当然有用,但它还没有回答真正的问题:我们看到的是磁盘上的持久数据、内核刚刚生成的状态,还是一个通向设备驱动的入口?当前命令又以什么身份运行,能看到多大的系统边界?
这一篇不追求把每个目录都背下来。我们要建立一条可以重复使用的观察路线:先确定层次和身份,再辨认对象类型,然后追踪路径、挂载与资源。这样排查问题时,每条结论都能落到具体证据上。
文中的系统目录以只读方式观察。不要向 /proc/sys、可写的 /sys 属性或未知 /dev 节点重定向内容;它们虽然长得像文件,写入语义却可能是修改内核参数或驱动设备。
你可以先把运行中的 Linux 画成三层。最下方是硬件,包括 CPU、内存、存储设备和网络接口;中间是 Linux 内核;上方是用户空间,也就是 Shell、命令、后台服务和应用进程所在的地方。
当用户空间的 ls 想读取目录时,它不会绕过内核直接碰磁盘。ls 发起系统调用,内核通过 VFS 解析路径、检查权限,再向具体文件系统取目录项。如果需要的数据已在缓存中,这次读取甚至不一定发生真实的磁盘 I/O。由此可见,“运行了一条命令”“进入了内核”“访问了硬件”是三个不同事件。
内核态与用户态描述的是 CPU 权限和内存访问边界。普通进程运行在用户态;内核处理系统调用或中断时运行在内核态。一个进程即使属于 root,也仍然是用户空间进程。root 权限很高,但不等于“进入内核态”。

同一条命令可能在用户空间发起、由内核处理,并在必要时访问硬件;箭头表示请求与结果的往返。
观察结果总有一个视角。相同命令由不同用户、在不同容器或挂载命名空间中执行,输出可能不同。因此不要急着解释目录,先回答三个问题:正在使用哪个内核、当前环境叫什么、命令以谁的身份运行。
先运行:
uname -srmo
hostname
cat /etc/os-releaseuname -srmo 依次给出内核名称、内核发行号、机器架构和操作系统标识。例如一次 Debian 容器观察到:
Linux 6.10.14-linuxkit aarch64 GNU/Linux这行说明用户空间正在使用 LinuxKit 提供的 6.10.14 内核,架构是 aarch64。它没有说明 Debian 的发行版本。Debian、Ubuntu 等发行版信息属于用户空间,通常由 /etc/os-release 描述。
hostname 返回当前 UTS 命名空间中的主机名。完整主机环境里它常是管理员配置的名称;容器中它可能只是临时容器 ID。主机名适合标记“这份输出来自哪个环境”,不适合单独证明硬件身份。
再运行:
id典型输出如下:
uid=1000(learner) gid=1000(learner) groups=1000(learner),27(sudo)内核做权限判断时主要使用数值 UID、GID 和附加组;learner 这样的名字是用户空间提供的可读映射。id 比 whoami 信息更完整,因为它同时显示有效用户、主组和附加组。
一次最小容器的默认输出可能是:
uid=0(root) gid=0(root) groups=0(root)这只说明容器内进程以 UID 0 运行。它没有把进程变成内核代码,也不自动意味着能看到或控制完整主机环境。容器运行时还会用命名空间、能力集合、设备白名单和挂载选项继续收窄边界。

uname、hostname、id 回答三个不同问题:使用什么内核、处于哪个命名空间标识、以什么用户和组运行。
把三条命令并在一起,可以快速保存观察上下文:
printf 'kernel: '; uname -srmo
printf 'host: '; hostname
printf 'user: '; id遇到未知路径时,最稳妥的顺序不是立刻 cat,而是先看元数据和类型。目录、软链接、FIFO、socket、字符设备都能出现在同一棵目录树里;对它们读取,结果可能是列目录、跟随链接、等待另一个进程,甚至触发驱动行为。
ls -l 的第一列对单个对象使用 ls -ld PATH。-d 很重要:当 PATH 是目录或指向目录的软链接时,它要求 ls 展示这个对象本身,而不是展开目录内容。
ls -ld /etc /dev/null /tmp/example-link一行长格式可以拆成这些字段:
lrwxrwxrwx 1 root root 8 Jul 15 06:47 note-link -> note.txt
│└──┬───┘ │ │ │ │ │ └─ 名称与链接目标
│ │ │ │ │ └─ 修改时间
│ │ │ │ └─ 大小;设备节点处常是主、次设备号
│ │ │ └─ 组
│ │ └─ 所有者
│ └─ 权限位:所有者、组、其他用户
└─ 类型字符类型字符是第一道分流:
ls -l 开头的 total 不是“文件数量”,而是当前列出目录项所占文件系统块的合计,具体块单位还受实现和环境影响。
file、cat 与 less 各回答什么file PATH 会依次使用文件系统属性、magic 规则以及文本/语言特征去分类。它不只看扩展名,也不只看文件开头的几个字节。一个叫 photo.jpg 的对象完全可能被识别成 UTF-8 文本。
file /bin/ls /etc/hosts /dev/null可能得到:
/bin/ls: ELF 64-bit LSB pie executable, ARM aarch64, ...
/etc/hosts: ASCII text
/dev/null: character special (1/3)file 默认不跟随软链接,适合先确认“眼前这个名字是不是链接”;需要检查目标时再用 file -L LINK。它给出的是分类判断,不是安全保证。未知可执行文件即使类型识别成功,也不应因此运行。
确认是短文本后,cat FILE 可以直接输出。确认是较长文本时,使用 less FILE,或把长命令输出送入分页器:
less /etc/services
findmnt | less在 less 中,空格向后翻页,b 向前翻页,/关键词 向下搜索,n 跳到下一项,q 退出。精简或嵌入式环境可能没有 less,这时可以尝试 more,或者先用 head 取少量内容。

先判断对象类型,再选择阅读工具;对 FIFO、socket 和设备节点,停止在元数据观察通常更安全。
本节可以浓缩成一个四步问题链:它是什么文件系统对象?如果是普通文件,内容格式是什么?如果是链接,目标在哪里?如果确实是文本,长度适合 cat 还是 less?
根目录 / 把不同文件系统和用途接到一棵统一目录树上。理解它时,按职责分组比背诵名称更可靠:哪些是程序和配置,哪些是持久变化的数据,哪些是本次启动的状态,哪些是内核接口。
现代发行版常使用 usr-merge:/bin、/sbin、/lib 可能是指向 /usr 中对应目录的软链接。看到 /bin -> usr/bin 并不是系统损坏,而是把程序和库统一到 /usr 树中的布局选择。用下面的命令核对,不要凭旧印象判断:
ls -ld /bin /sbin /lib /var/run
readlink -f /bin
readlink -f /var/run
目录树把配置、程序、持久变化数据、启动期状态和内核接口统一接到 / 下,但它们的生命周期并不相同。
/proc 是运行状态窗口/proc 挂载的是 proc 文件系统。它把内核数据结构以目录和文本接口呈现给用户空间。很多条目只有在读取时才由内核组织出来,因此不能把它理解成“系统定期写到磁盘的一批状态文件”。
先确认后端:
stat -f -c '%T' /proc
findmnt -T /procGNU/Linux 中常见输出是 proc。然后从几个只读入口开始:
cat /proc/sys/kernel/osrelease
cat /proc/uptime
grep -E '^(MemTotal|MemAvailable|SwapTotal|SwapFree):' /proc/meminfo
grep -E '^(Name|Pid|PPid|Uid|Gid):' /proc/self/status/proc/<PID> 对应一个正在运行的进程。进程退出后,目录可以立即消失;PID 以后还可能被新进程复用。/proc/self 则指向“当前读取 /proc 的进程”。例如由 awk 读取 /proc/self/status,其中 Name 和 Pid 描述的是那个 awk 进程,不是启动它的 Shell。
/proc/meminfo 展示内存统计,free 会利用其中的信息形成更便于阅读的表格。MemAvailable 是内核对无需大量交换即可供新工作使用内存的估计值,通常比只盯着 MemFree 更有解释力。
/proc 不是整体只读。/proc/sys 下有许多运行时内核参数,向它们写值会改变系统行为。探索阶段只读取已知条目,不使用 echo ... > /proc/sys/...。
/sys 是内核对象与设备模型/sys 挂载的是 sysfs。它主要导出内核对象、对象属性以及它们之间的关系。与 /proc 的“运行状态和进程视图”相比,/sys 更像一棵设备与子系统关系图。
stat -f -c '%T' /sys
ls -1 /sys常见顶层入口包括:
/sys/class 与 /sys/block 中的条目经常是软链接,最终指向 /sys/devices。这让同一个对象既能按物理/虚拟层级观察,也能按功能类别查找:
ls -l /sys/class/net
readlink -f /sys/class/net/lo
ls -l /sys/block | head一次容器观察中,回环接口解析为:
/sys/devices/virtual/net/lovirtual 说明它是内核中的虚拟网络设备,不对应一张独立物理网卡。这个例子也说明,/sys 中的链接关系本身就是重要信息。
sysfs 属性通常使用简短文本值,但“可以 cat”不等于“它是普通文本配置文件”。有些属性允许写入,写操作会调用内核子系统或驱动的处理逻辑。只读探索可以查看 operstate、size、ro 等已知属性,不应对陌生属性做试写。
/dev 是设备 I/O 入口/dev 中的块设备和字符设备节点让用户进程通过文件操作请求驱动完成 I/O。节点本身通常不保存设备数据;它记录类型以及主、次设备号,内核据此找到对应驱动和设备实例。
ls -l /dev/null /dev/zero /dev/random /dev/tty
file /dev/null /dev/zero /dev/random /dev/tty一次最小容器得到:
crw-rw-rw- 1 root root 1, 3 /dev/null
crw-rw-rw- 1 root root 1, 5 /dev/zero
crw-rw-rw- 1 root root 1, 8 /dev/random
crw-rw-rw- 1 root root 5, 0 /dev/tty四者都是字符设备,但语义不同:
/dev/null 丢弃写入数据,读取立即返回文件结束。/dev/zero 按读取请求持续产生零字节;无边界读取不会像普通文件那样自然结束。/dev/random 提供随机字节接口,行为由内核随机数子系统决定。/dev/tty 表示当前进程的控制终端;进程没有控制终端时,打开它可能失败。块设备的首字符是 b,常用于磁盘或逻辑卷;字符设备的首字符是 c,按字节流或设备定义的操作工作。网络接口通常没有与之对应的块/字符设备节点,它们通过网络系统调用、netlink 和 sysfs 等接口管理。所以“所有硬件都在 /dev”并不准确。
现代系统的 /dev 往往由 devtmpfs 和用户空间设备管理共同维护。容器运行时会再筛选可见节点,一份精简的 /dev 清单只能说明当前容器获准使用哪些入口,不能推出完整主机的设备清单。

/proc 侧重进程和运行状态,/sys 侧重内核对象与关系,/dev 侧重 I/O 入口;三者共享路径接口,但后端语义不同。
符号链接是一个独立文件,内容是目标路径文本。目标可以是绝对路径,也可以是相对路径,甚至可以暂时不存在。相对目标从“链接所在目录”开始解析,不是从执行命令时的工作目录开始解析。
mkdir -p /tmp/welearn-link-demo/releases
printf 'v1\n' > /tmp/welearn-link-demo/releases/app-1.0
ln -s releases/app-1.0 /tmp/welearn-link-demo/current然后比较三种观察:
ls -l /tmp/welearn-link-demo/current
readlink /tmp/welearn-link-demo/current
readlink -f /tmp/welearn-link-demo/current输出分别回答:
current -> releases/app-1.0
releases/app-1.0
/tmp/welearn-link-demo/releases/app-1.0ls -l 同时展示链接名和目标;readlink 原样输出链接保存的目标文本;readlink -f 解析每个软链接、.、.. 和重复分隔符,给出规范化绝对路径。做通用路径规范化时也可以使用 realpath。
如果目标被删除,链接文件仍存在,但访问目标会得到“没有那个文件或目录”。这叫断链。链式链接则是链接指向另一个链接,需要逐层解析。排查时可组合:
ls -ld LINK
readlink LINK
readlink -f LINK
file LINK
file -L LINK注意 ln -s TARGET LINK_NAME 的参数顺序。漏掉 -s 会创建硬链接;硬链接直接增加同一个 inode 的目录项,不保存“目标路径文本”,不能按软链接方式解释。

相对目标从链接所在目录解析;readlink 看保存文本,readlink -f 看最终规范路径。
rm -rf /tmp/welearn-link-demo同一个路径名在不同环境中可能连接到不同文件系统。先问“它挂载在哪里、类型是什么”,再解释容量和状态:
findmnt -T /
findmnt -T /proc
findmnt -T /sys
findmnt -T /devfindmnt -T PATH 按路径查找承载它的挂载,重点看目标、文件系统类型、来源和选项。一次容器输出的关键部分是:
/ overlay overlay rw,relatime,...
/proc proc proc rw,nosuid,nodev,noexec,relatime
/sys sysfs sysfs ro,nosuid,nodev,noexec,relatime
/dev tmpfs tmpfs rw,nosuid,size=65536k,mode=755这里的 /sys 带 ro,说明当前挂载只读;不能据此断言所有 Linux 环境中的 sysfs 都只读。/ 使用 overlay,说明容器根目录由镜像只读层和容器可写层叠加形成。
df 回答“承载这个路径的文件系统还有多少空间”:
df -hT / /proc /sys /dev它不是目录递归用量工具。df -hT /var/log 显示的是承载 /var/log 的整个文件系统容量,不是日志目录大小;目录内容用量通常用 du -sh /var/log,但对大型目录运行 du 会遍历大量文件,不属于轻量的第一步观察。
proc 和 sysfs 在 df 中常显示 0 容量,因为它们不是普通持久存储。/dev 若由 tmpfs 承载,会显示内存文件系统的容量限制。
内存则用:
free -h
grep -E '^(MemTotal|MemAvailable|SwapTotal|SwapFree):' /proc/meminfo
cat /sys/fs/cgroup/memory.max 2>/dev/nullfree 中的 available 比单独的 free 列更接近可供新工作使用的内存估计,因为缓存可以在压力下回收。容器还可能受到 cgroup 内存上限限制;因此要把 free 的系统视图与 memory.max 一起读。memory.max 为 max 表示当前 cgroup 没设置额外硬上限,并不表示物理内存无限。

findmnt 解释路径后端,df 解释文件系统容量,free 与 cgroup 文件共同解释内存边界。
当你只得到一个陌生路径或一段模糊故障描述时,可以按下面顺序收集证据。顺序的价值在于:前一步会限制后一步应该使用什么工具。
先保存视角。运行 uname -srmo、hostname 和 id,分别记录内核、环境名称与用户权限。没有这一步,后面很难解释为什么另一个环境输出不同。
确认逻辑位置和对象本身。使用 pwd -P 消除当前路径中的软链接遮蔽,再用 ls -ld PATH 查看目标类型、权限、所有者和时间。
只有普通文件才继续做内容分类。运行 file PATH;若是链接,同时运行 readlink PATH 与 readlink -f PATH,分别保存目标文本和最终路径。
只读路线也有边界。不要因为 cat 通常是阅读命令,就对未知 /dev 节点使用它;不要对 /proc/sys 或 /sys 做重定向;不要为了“看看会怎样”运行 dd、mkfs、mount、chmod、chown 或带 sudo 的修改命令。先分类的原因,正是相同的文件接口背后可能连接完全不同的行为。
下面的实操在一次性 Debian 容器中创建四类对象,观察完就清理。写入只发生在容器的 /tmp/welearn-ch03;--rm 保证容器退出后整个可写层被删除。
复制整段命令:
docker run --rm --name welearn-linux-ch03-lab debian:bookworm-slim bash -lc '
set -eu
export DEBIAN_FRONTEND=noninteractive
apt-get update -qq
apt-get install -y -qq --no-install-recommends file procps util-linux >/dev/null
work=/tmp/welearn-ch03
mkdir -p "$work/demo-dir"
printf "Linux exploration\n" > "$work/note.txt"
ln -s note.txt "$work/note-link"
mkfifo "$work/flow.pipe"
ls -ld "$work/note.txt" "$work/demo-dir" "$work/note-link" "$work/flow.pipe" /dev/null
file "$work/note.txt" "$work/demo-dir" "$work/note-link" "$work/flow.pipe" /bin/ls /dev/null
printf "readlink: "; readlink "$work/note-link"
printf "readlink -f: "; readlink -f "$work/note-link"
printf "proc fs: "; stat -f -c %T /proc
printf "sys fs: "; stat -f -c %T /sys
printf "dev fs: "; stat -f -c %T /dev
for path in / /proc /sys /dev; do findmnt -T "$path" -no TARGET,FSTYPE,SOURCE,OPTIONS; done
这里故意创建普通文件、目录、软链接和 FIFO。它们都能被路径引用,却有不同的类型字符和读取语义。FIFO 只用于让 ls 与 file 分类,不直接读取,因为没有写入端时读取可能等待。
文件部分会出现类似输出:
crw-rw-rw- 1 root root 1, 3 /dev/null
drwxr-xr-x 2 root root 4096 /tmp/welearn-ch03/demo-dir
prw-r--r-- 1 root root 0 /tmp/welearn-ch03/flow.pipe
lrwxrwxrwx 1 root root 8 /tmp/welearn-ch03/note-link -> note.txt
-rw-r--r-- 1 root root 18 /tmp/welearn-ch03/note.txt
/tmp/welearn-ch03/note.txt: ASCII text
/tmp/welearn-ch03/demo-dir: directory
/tmp/welearn-ch03/note-link: symbolic link to note.txt
/tmp/welearn-ch03/flow.pipe: fifo (named pipe)
/dev/null: character special (1/3)这组输出把两条证据对上了:ls 的首字符分别是 c、d、p、l、-,file 给出相应分类。软链接两条输出进一步说明保存文本与解析结果的差别:
readlink: note.txt
readlink -f: /tmp/welearn-ch03/note.txt三个文件系统类型可能是:
proc fs: proc
sys fs: sysfs
dev fs: tmpfs/dev 在这次容器中由 tmpfs 承载,而里面的 /dev/null 是字符设备节点。文件系统类型和目录中对象类型属于两个层次:一个 tmpfs 目录完全可以包含由容器运行时准备的设备节点。
脚本先执行:
rm -rf /tmp/welearn-ch03
test ! -e /tmp/welearn-ch03第一条删除练习对象,第二条用退出状态确认路径已经不存在。随后 Shell 退出,Docker 因 --rm 删除容器的整个可写层。即使脚本中途失败,容器退出后也不会把练习目录留在工作环境里;不过显式 rm 仍有教学价值,因为它验证了清理步骤本身。
容器外可以复查:
docker ps -a --filter name=^/welearn-linux-ch03-lab$没有输出,表示同名容器没有残留。镜像缓存可能仍保留,这是 Docker 的镜像层,不是这次创建的容器可写层。
整套实操的重点不在命令数量,而在证据闭环:我们创建已知对象,预测类型,再用两种工具验证;随后确认接口文件系统与挂载,最后验证清理。
容器不是另一套独立内核。它通常共享宿主内核,同时通过命名空间、cgroup、挂载和设备规则限制用户空间看到的世界。这会让同一命令呈现“真实但有边界”的结果。
本次实操中,/sys 挂载选项包含 ro,/dev 是 64 MiB 的 tmpfs,cgroup v2 的 memory.max 为 max。这些值准确描述了该容器的边界,却不能推广成每台 Linux 的固定配置。换一个运行参数,例如加上内存限额或额外设备映射,输出就会改变。
所以,系统观察最好用两句话收尾:第一句写当前环境明确显示了什么;第二句写由于容器、权限或挂载边界,还不能推出什么。例如:
findmnt显示当前/sys以只读 sysfs 挂载,因此这组命令无法修改 sysfs 属性;这不能证明完整主机环境中的 sysfs 一定使用相同挂载选项。
这就是“探索系统内部”的完成标准:不是看过多少目录,而是能分清层次、对象和边界,并且让别人用同样的命令复查你的判断。
判断路径后端。对 /proc、/sys、/dev 或可疑挂载点,使用 stat -f -c '%T' PATH 与 findmnt -T PATH,确认文件系统类型和读写选项。
选择阅读方式。短而明确的文本可用 cat;长文本用 less;未知设备、FIFO 和 socket 停止在元数据观察,不用 cat 猜语义。
最后看资源。用 df -hT PATH 观察文件系统容量,用 free -h 与 cgroup 限额观察内存,不把两类指标混在一起。
记录命令、时间、关键输出和环境边界。排查结论应写成“命令显示了什么,因此能推出什么;仍不能推出什么”,而不是只贴一屏输出。