Skip to content
📌知识点小结
  • Linux 的核心设计哲学是"一切皆文件":普通数据、目录、磁盘、键盘、网卡、进程信息,在内核眼里统统是一个可以 open/read/write 的文件。
  • FHS(文件系统层次标准)规定了 /bin /etc /var /home /usr 等顶层目录各自放什么,记住这张"地图"就不会在系统里迷路。
  • ls -l 第一列第一个字符标识文件类型:- 普通文件、d 目录、l 软链接、b 块设备、c 字符设备、p 管道、s 套接字。
  • /dev 下是设备文件(sda 磁盘、null 黑洞、zero 零源),/proc 和 /sys 是内核实时生成的"虚拟文件",读它们就是在问内核要信息。
  • 普通文件又按内容分文本文件和二进制文件,Linux 不依赖扩展名判断类型,file 命令看的是文件头魔数。
✅自测一下

3.1 文件系统:一切皆文件 ​

【Linux 操作系统深度】系列 Part 1(免费)。 这一篇讲透 Linux 最根本的设计思想——"一切皆文件":为什么硬盘、键盘、进程都能像文件一样读写,FHS 标准目录各放什么,以及七种文件类型怎么辨认。 Part 2 讲 inode、软硬链接与挂载机制(星球专享),Part 3 讲用户组与 rwx 权限位,Part 4 讲管道重定向与 Shell 脚本。

一、一个问题:Linux 里"文件"到底指什么 ​

刚从 Windows 过来的同学,脑子里"文件"的概念通常是:一个有扩展名、有图标、躺在文件夹里的东西。.txt 是文本,.exe 是程序,C 盘 D 盘是分开的磁盘。

Linux 完全不是这个模型。它有一句贯穿整个系统的设计哲学:

Everything is a file —— 一切皆文件。

这句话的意思是:内核对外部世界的统一抽象方式,就是"文件"。不管你操作的是:

  • 一个存了数据的普通文本;
  • 一个目录(在 Linux 里目录也是一种"文件");
  • 一块硬盘、一个 U 盘;
  • 键盘、终端、打印机;
  • 一张网卡;
  • 甚至一个正在运行的进程的信息;

内核都尽量用同一套接口来对待它们:open() 打开、read() 读、write() 写、close() 关闭。

这不是一句口号,它直接决定了你在 Linux 里的操作方式。下面先建立整体图景,再一层层拆开。

二、一张图看懂"一切皆文件" ​

中间这一层叫 VFS(Virtual File System,虚拟文件系统) ,它是内核里的一个抽象层。VFS 定义了"一个文件应该支持哪些操作",然后让不同的具体文件系统(ext4、procfs、设备驱动……)各自实现这些操作。

对你和上层程序来说,不需要知道背后是磁盘还是设备,统一调 read() 就行;VFS 会把请求转给真正干活的那一方。这就是"一切皆文件"在代码层面的实现。

一个直观的例子 ​

你可以用同一条 cat 命令,读完全不同的东西:

bash
cat /etc/hostname          # 读一个普通文件 → 输出主机名
cat /dev/null              # 读一个字符设备(黑洞)→ 什么都不输出
cat /proc/cpuinfo          # 读内核虚拟文件 → 输出 CPU 信息
cat /dev/zero | head -c 16 # 读字符设备(零源)→ 输出 16 个字节的 0

cat 程序内部只做了一件事:open() → 循环 read() → 把读到的内容写到屏幕。它根本不关心、也不需要知道自己读的是哪种"文件"。这种统一带来的简洁,就是"一切皆文件"的价值。

三、FHS:Linux 的目录地图 ​

Linux 没有 C 盘 D 盘的概念,所有文件——不管在哪块磁盘上——都挂在同一棵从根 / 开始的目录树里。这棵树的顶层布局不是随便长的,它遵循一个叫 FHS(Filesystem Hierarchy Standard,文件系统层次标准) 的规范。

记住下面这张表,你就拿到了 Linux 系统的"地图":

目录名字来源放什么安全/运维相关性
/root根目录,整棵树的起点—
/binbinaries所有用户都能用的基础命令(ls、cat、cp)应急时必须在
/sbinsystem binaries需要 root 的系统管理命令(reboot、fdisk)提权、运维
/etcet cetera系统和服务的配置文件(纯文本)改配置、找口令/密钥
/homehome普通用户的家目录,每人一个子目录用户数据、.ssh 密钥
/rootrootroot 用户的家目录(不在 /home 下)管理员痕迹
/usrunix system resources大部分程序和数据(只读),下分 bin/lib/share安装的软件
/varvariable经常变化的数据:日志、缓存、邮件、数据库/var/log 看日志
/tmptemporary临时文件,重启可能清空临时 payload、提权
/devdevices设备文件(磁盘、null、zero)一切皆文件的体现
/procprocesses进程与内核信息虚拟文件系统看进程、环境变量
/syssystem硬件与内核参数虚拟文件系统设备、驱动信息
/liblibrary/bin 和 /sbin 需要的共享库和内核模块—
/optoptional第三方/可选的附加软件手动装的软件
/bootboot启动引导程序和内核(vmlinuz、grub)启动相关
/mnt /mediamount临时挂载点(额外磁盘、U 盘、光盘)挂载新存储

几个安全学习中最常打交道的目录,单独强调:

  • /etc:服务配置都在这。比如 /etc/passwd(用户列表)、/etc/shadow(密码哈希,root 才能读)、/etc/ssh/sshd_config(SSH 配置)。做安全时,配置文件里的弱口令、硬编码密钥、错误权限都是重点。
  • /var/log:所有日志的家。/var/log/auth.log(登录记录,Debian 系)、/var/log/secure(Red Hat 系)是排查入侵和追踪行为的第一站。
  • /proc:每个运行中的进程在 /proc/<PID>/ 下有一个"目录",里面的 cmdline(启动命令)、environ(环境变量,可能含口令)、fd/(打开的文件)都是信息收集的金矿。
  • /tmp 和 /dev/shm:全局可写,临时文件和内存文件系统常被用来放临时工具;排查时值得留意。

小知识:现代很多发行版(如 Ubuntu)里,/bin /sbin /lib 其实是指向 /usr/bin /usr/sbin /usr/lib 的软链接(合并到 /usr 的方案,usrmerge),所以你 ls -l /bin 会看到它是个链接。这是历史演进,不影响使用。

四、七种文件类型:ls -l 第一列会说话 ​

"一切皆文件"不等于所有文件长得一样。Linux 把文件分成七种类型,用 ls -l 输出的第一个字符就能辨认。

bash
$ ls -l /etc/hostname
-rw-r--r-- 1 root root 12  9月 20 10:00 /etc/hostname
↑
第一个字符就是类型
首字符类型说明典型例子
-普通文件 regular数据的载体,文本或二进制/etc/hostname、脚本、ELF 程序
d目录 directory存放"文件名 → inode"映射的特殊文件/home、/tmp
l符号链接 symbolic link指向另一个路径的快捷方式(软链接)/etc/mtab、/bin→/usr/bin
b块设备 block device按块随机存取,通常是磁盘/存储/dev/sda、/dev/nvme0n1
c字符设备 character device按字节流顺序读写/dev/null、/dev/tty、/dev/zero
p命名管道 named pipe进程间传递数据的 FIFOmkfifo 创建
s套接字 socket进程间/网络通信的端点/run/docker.sock、MySQL socket

4.1 普通文件(-) ​

最常见的一类,里面是实际数据。按内容又分两种:

  • 文本文件:人类可读的字符,配置、脚本、日志都是。
  • 二进制文件:编译后的程序、图片、压缩包等,直接 cat 会看到乱码。

关键区别:Linux 不靠扩展名判断文件类型。一个文件是文本还是程序,看的是内容本身。用 file 命令,它读文件开头的"魔数(magic number)"来判断:

bash
$ file /bin/ls
/bin/ls: ELF 64-bit LSB pie executable, x86-64, ...   # ELF 魔数 = 可执行程序

$ file /etc/hostname
/etc/hostname: ASCII text                              # 纯文本

$ file /dev/null
/dev/null: character special (1/3)                     # 字符设备

$ echo "hello" > fake.png
$ file fake.png
fake.png: ASCII text    # 即使扩展名是 .png,内容是文本,file 照样认出来

这和 Windows 靠扩展名关联程序完全不同。在 Linux 上,把脚本改名不影响它的本质;能不能"执行"取决于有没有执行权限 x,以及文件开头的 shebang(#!/bin/bash、#!/usr/bin/env python3)告诉内核用哪个解释器。

4.2 目录(d) ​

目录在磁盘上也是一个文件,只不过它的内容不是给人读的文章,而是一张**"文件名 → inode 编号"的对照表**。你 ls 一个目录,本质上是在读这张表;在目录里新建文件,就是往这张表加一条记录。

这也解释了为什么删除文件需要对所在目录有写权限(要改目录的对照表),而不是对文件本身有写权限。权限和 inode 的深入关系在 Part 2 讲。

4.3 符号链接(l) ​

软链接(symbolic link)是一个内容为"另一个路径"的特殊文件,类似 Windows 的快捷方式,但更彻底。访问软链接时,系统会自动跳到它指向的真实路径。

bash
$ ls -l /usr/bin/python3
lrwxrwxrwx 1 root root 16 ... /usr/bin/python3 -> python3.10

软链接可以跨文件系统、可以指向目录;如果目标被删除,链接就变成"悬空链接(dangling link)",显示为红色。与之相对的"硬链接"本质不同,Part 2 结合 inode 讲透二者区别。

4.4 设备文件(b / c)—— "一切皆文件"最典型的体现 ​

设备文件位于 /dev,它们让你能用读写文件的方式操作硬件。分两类:

块设备(b):以固定大小的"块"为单位,支持随机寻址(跳到任意位置读写),主要是存储设备。

bash
$ ls -l /dev/sda
brw-rw---- 1 root disk 8, 0 ... /dev/sda     # 整块硬盘
$ ls -l /dev/sda1
brw-rw---- 1 root disk 8, 1 ... /dev/sda1    # 第一个分区

字符设备(c):以字节流形式顺序读写,像水管一样,不能跳回开头。键盘、终端、以及几个非常常用的"虚拟设备"都是:

设备行为常见用法
/dev/null数据黑洞:写入的东西全部丢弃,读返回 EOFcommand 2>/dev/null 丢弃错误输出
/dev/zero源源不断提供 \0 字节生成指定大小文件、清空磁盘
/dev/random /dev/urandom提供随机字节生成密钥、随机数
/dev/tty当前终端脚本强制向终端输出

体会一下这套抽象:想丢弃输出,就"把它写进一个叫 null 的文件";想生成 1GB 的全零文件,就"从零源这个文件读 1GB 再写进新文件":

bash
dd if=/dev/zero of=disk.img bs=1M count=1024   # 生成 1GB 全零镜像
head -c 32 /dev/urandom | xxd                  # 生成 32 字节随机数(可当密钥)

4.5 管道(p)和套接字(s) ​

这两类用于进程之间通信,同样被抽象成文件:

  • 命名管道(p,FIFO):一个进程往里写,另一个进程从里读,数据像水流过管子。用 mkfifo mypipe 创建。命令行里的匿名管道 | 是同一思想的临时形态(Part 4 详解)。
  • 套接字(s):网络通信和本机进程间通信的端点。做安全最常遇到的是 /run/docker.sock——Docker 守护进程的 Unix 套接字。谁能读写这个套接字,谁就能控制 Docker(进而等价于 root,容器逃逸的经典路径);MySQL/Nginx 也可能用本机套接字通信。

五、/proc 与 /sys:不存在于磁盘的"文件" ​

除了设备文件,"一切皆文件"还有一个更神奇的延伸:内核把自己的内部状态也做成了文件。

5.1 /proc:进程与内核的窗口 ​

/proc 是 procfs 虚拟文件系统挂载点。这里的文件不占磁盘,在你读取的瞬间由内核动态生成。

bash
$ ls /proc
1    10    cpuinfo    meminfo    version ...
└┬─┘
每个数字 = 一个进程的 PID,对应一个"目录"

常用的信息文件:

路径内容
/proc/cpuinfoCPU 型号、核心数、特性
/proc/meminfo内存使用详情(free 命令的数据来源)
/proc/version内核版本
/proc/mounts当前挂载的文件系统
/proc/net/tcpTCP 连接状态(网络排查)
/proc/sys/...可调整的内核参数(sysctl)

每个进程的 /proc/<PID>/ 目录是信息收集重点:

路径内容安全相关性
/proc/<PID>/cmdline进程启动命令(含参数)还原服务怎么启动的
/proc/<PID>/environ环境变量可能含密码、密钥、令牌
/proc/<PID>/fd/该进程打开的所有文件找它在读什么、日志、socket
/proc/<PID>/maps内存映射分析二进制、找 libc 基址
/proc/<PID>/status运行状态、权限、UID/GID看进程以谁的身份跑
bash
# 例:查看 1 号进程(systemd)的启动命令
cat /proc/1/cmdline | tr '\0' ' '

注意 cmdline 和 environ 里各项用 \0(空字节)分隔,直接 cat 会粘在一起,管道加 tr '\0' '\n' 或 ' ' 转换后更易读。

5.2 /sys:硬件与设备的统一模型 ​

/sys 是 sysfs,比 /proc 更规整,按统一模型暴露硬件设备、驱动、内核子系统的信息。比如 /sys/class/net/ 下是网卡,/sys/block/ 下是块设备。日常排查硬件、设备识别时会用到;安全场景相对 /proc 用得少,知道它也是"内核状态文件化"即可。

5.3 为什么这套设计如此重要 ​

把进程信息、硬件、内核参数都做成文件,带来的直接好处是:你不需要为每种信息学一套专用工具。

  • 想看 CPU?cat /proc/cpuinfo;
  • 想知道某进程的环境变量?cat /proc/1234/environ;
  • 想改内核参数(如开启 IP 转发)?往 /proc/sys/net/ipv4/ip_forward 写个 1:echo 1 > /proc/sys/net/ipv4/ip_forward。

"读文件 = 查状态,写文件 = 改配置",一套读写语义通吃。这正是 Unix/Linux "机制统一、组合简单"的工程美学,也是后面学命令注入、提权、容器逃逸时反复用到的底层认知。

六、动手:在系统里辨认这些概念 ​

打开任意一台 Linux(云服务器、WSL、或靶场机),按下面顺序敲一遍,比只读文章有效得多:

bash
# 1. 看整棵目录树的顶层布局,对照 FHS 表
ls -l /

# 2. 辨认七种文件类型(注意第一列字符)
ls -l /etc/hostname          # - 普通文件
ls -ld /etc                  # d 目录
ls -l /bin                   # l 软链接(usrmerge 系统)
ls -l /dev/sda               # b 块设备(没有磁盘就看 /dev/loop0)
ls -l /dev/null              # c 字符设备
ls -l /run/docker.sock       # s 套接字(装了 docker 才有)

# 3. 用 file 看真实类型,验证"不靠扩展名"
file /bin/ls /etc/hostname /dev/null
echo "x" > /tmp/test.png && file /tmp/test.png   # 扩展名骗人

# 4. 体验 /proc 虚拟文件
cat /proc/cpuinfo | head
cat /proc/meminfo | head
ls /proc/self/               # self = 当前正在运行的命令自己
cat /proc/self/cmdline | tr '\0' ' '; echo

# 5. 体验设备文件
dd if=/dev/zero of=/tmp/zeros.bin bs=1M count=1  # 从零源造 1MB 文件
ls -lh /tmp/zeros.bin
command 2>/dev/null; echo "错误已被 /dev/null 吞掉"

做完这一遍,你应该能:对着 ls -l 输出脱口说出文件类型、解释 /dev 和 /proc 为什么也是"文件"、以及为什么改扩展名骗不了 Linux。

七、本篇小结 ​

  • "一切皆文件"是 Linux 的统一抽象:普通数据、目录、设备、进程状态、通信端点,内核都通过 VFS 用 open/read/write 一套接口对待;具体差异由各文件系统和驱动实现。
  • FHS 规定了目录地图:配置在 /etc、日志在 /var/log、用户在 /home、设备在 /dev、进程信息在 /proc、程序大多在 /usr。
  • 七种文件类型看 ls -l 首字符:- 普通、d 目录、l 软链接、b 块设备、c 字符设备、p 管道、s 套接字。
  • Linux 不靠扩展名判断类型,file 命令读文件头魔数;能否执行看 x 权限和 shebang/ELF 头。
  • /proc 和 /sys 是内核实时生成的虚拟文件,读它就是在查内核状态,写它就是在改配置,不占磁盘。

理解了"文件"这个统一抽象,下一篇我们钻进文件系统的底层:一个文件在磁盘上到底是怎么存的、inode 是什么、硬链接和软链接有什么本质区别、一块新磁盘又是怎么"挂"进这棵目录树的。