Linux 基础速览

容器技术建立在 Linux 内核的进程、文件系统和网络机制之上——本篇建立这三块前置认知

阶段一 · 01 · 返回阶段大纲

为什么需要这篇

后续的 Namespace、Cgroups、OverlayFS 都是 Linux 内核特性。如果你不清楚"进程是怎么创建的""/proc 是什么""网络接口为什么能配 IP",后续章节会频繁出现认知断层。本篇目标是用最短篇幅补齐这些前置概念,不求精通,但求理解"是什么、为什么"。

一、进程模型

Linux 中一切工作都由进程完成。你在终端敲的每条命令、运行的每个程序、容器里的每个应用,都是一个进程。理解进程的创建方式是理解容器的第一步。

fork 与 exec — 进程的诞生方式

Linux 创建新进程只有一种途径:fork(分裂)。

操作含义类比
fork() 当前进程复制一份自己,产生一个子进程(内容完全相同) 细胞分裂——分裂后两个细胞一模一样
exec() 用一个新程序替换当前进程的代码和数据 换魂——壳还是那个壳,里面的灵魂换了

典型流程(你在 shell 里输入 ls 时发生的事):

bash 进程(PID 100)
  │
  ├─ fork() → 子进程(PID 101),内容和 bash 完全一样
  │
  └─ 子进程调用 exec("ls") → PID 101 变成了 ls 程序
                            → ls 执行完毕,退出
                            → bash 继续等待下一条命令

为什么不直接"创建"一个全新进程?因为 Linux 的设计哲学是复制比从零构建更简单——先 fork 出一份副本,再按需替换内容(exec)。这个"先复制再替换"的模式贯穿整个系统。

PID — 进程的身份证号

PID(Process ID)是内核分配给每个进程的唯一编号:

# 查看当前 shell 的 PID
echo $$

# 查看进程的父进程
ps -o pid,ppid,comm -p $$

进程树 — 所有进程的族谱

由于每个进程都由 fork 产生,所有进程形成一棵

# 查看进程树
pstree -p

# 输出示例(精简):
# systemd(1)─┬─sshd(800)───sshd(1200)───bash(1201)───pstree(1234)
#             ├─dockerd(900)───containerd(901)
#             └─nginx(1000)─┬─nginx(1001)
#                           └─nginx(1002)

与容器的关系

容器里的进程也在这棵树上——它只是一个普通的子进程。但通过 PID Namespace(第 02 篇),容器内的进程"以为"自己的 PID 是 1,看不到树的其他部分。隔离的是视图,不是进程本身。

二、Linux 文件系统基础

在讨论容器技术之前,必须先把 Linux 文件系统的基本概念理清。很多人对"文件系统"的理解停留在"文件夹和文件"的层面——这在 Windows 的日常使用中够用,但在 Linux 里不够。下面我们从最核心的一条设计哲学讲起,逐层建立认知。

2.1 "一切皆文件":Linux 的核心设计哲学

Linux 有一条贯穿整个系统的设计理念:一切皆文件(Everything is a file)。这句话不是比喻,而是字面意义上的事实。

什么是"文件"?

在 Linux 中,"文件"不是一个狭义概念(只有 Word 文档、图片才叫文件)。文件是一个抽象接口——只要一个东西支持以下四个基本操作,它在 Linux 眼里就是"文件":

系统调用含义类比
open()打开一个文件,获得文件描述符(file descriptor)拿起电话听筒
read()从文件中读取数据听对方说话
write()向文件中写入数据对着话筒说话
close()关闭文件,释放文件描述符挂断电话

关键是:读一个 .txt 文件、读键盘输入、读网络数据、读进程信息,用的都是同一套 open → read → write → close 系统调用。程序员不需要为"不同类型的 IO"写完全不同的代码,这是巨大的简化。

哪些东西在 Linux 里是"文件"?

类型例子你平时就在这里看到它
普通文件(Regular File) 源代码、日志、图片、二进制程序 ~/project/main.go
目录(Directory) 文件夹——本质上是一个存有"文件名→inode 编号"映射关系的特殊文件 任何一个文件夹
设备文件(Device File) 硬盘、键盘、鼠标、终端 /dev/sda(硬盘)、/dev/tty(终端)
管道(Pipe / FIFO) 进程间单向数据流 ls | grep foo|
套接字(Socket) 网络连接端点,也可以是本机进程间通信 /var/run/docker.sock
符号链接(Symlink) 指向另一个文件路径的"快捷方式" /usr/bin/python → python3
虚拟文件(Virtual File) 内核实时生成的"假"文件,不存在于磁盘 /proc/cpuinfo/sys/class/net/eth0/address

一切皆文件 = 一切都有统一的读写接口。想象一个万能插座:不管你插的是台灯、电视、电风扇、手机充电器,插座提供的都是 220V 交流电。Linux 的"文件接口"就是这个万能插座——不论底层是硬盘、键盘、网卡还是内核数据,上层都用同一套 open/read/write/close 来操作。

一个具体例子来感受

假设你想获取当前系统有几颗 CPU 核心。在 Linux 里,你不需要调用特殊 API,直接"读文件"就行:

# 读取 /proc/cpuinfo —— 就像读一个普通文本文件一样
cat /proc/cpuinfo | grep 'processor' | wc -l

# 和读普通文件完全相同的系统调用链:
# open("/proc/cpuinfo") → read() → write() → close()

再比如,你想限制一个进程的 CPU 使用量(第 03 篇 Cgroups 的核心操作),不需要特殊工具,直接"写文件":

# 往一个"文件"里写个数字,内核就帮你限制了这个 Cgroup 的 CPU
echo 50000 > /sys/fs/cgroup/cpu/mygroup/cpu.cfs_quota_us

读文件 = 查询信息,写文件 = 修改配置。这就是 Linux 把复杂性隐藏在统一接口背后的方式。

关键认知

"一切皆文件"不是 Linux 的专利(Unix 首创),但 Linux 把这条哲学贯彻得最彻底。理解这一点后,你会发现:在 Linux 里,"探索系统"就是"浏览文件"——你用 lscat 就能窥探内核的内部状态,不需要特殊工具。这是 Linux 作为学习平台的巨大优势。

2.2 统一目录树:没有 C 盘 D 盘的世界

Windows 用户习惯了 C:\D:\E:\ 这样的盘符体系——每个分区是一个独立的"根",各自拥有一棵目录树:

Windows(多根):
C:\                D:\                E:\
├─ Windows         ├─ 游戏            ├─ 电影
├─ Program Files   ├─ 照片            └─ 音乐
└─ Users           └─ 文档

Linux 的设计完全不同:整个系统只有一棵目录树,根就是 /。不管你插了几个 U 盘、挂了几块硬盘,它们全部"接入"到这棵树的某个分支上:

Linux(单根):
/
├─ bin/           ← 基本命令(ls, cat, cp...)
├─ etc/           ← 系统配置
├─ home/
│  └─ yutong/     ← 你的个人文件
├─ mnt/
│  ├─ usb/        ← U 盘的内容出现在这里(通过 mount 接入)
│  └─ data/       ← 另一块硬盘的内容出现在这里
├─ proc/          ← 内核进程信息(虚拟,不存在于磁盘)
├─ sys/           ← 内核子系统信息(虚拟)
├─ dev/           ← 设备文件
└─ var/           ← 日志、缓存等可变数据

Windows 盘符 = 独立的岛屿,每个岛有自己的地图系统。C 岛和 D 岛之间没有天然的层次关系。Linux 目录树 = 一棵大树/ 是树干,所有存储设备和文件系统都是长在这棵树上的树枝。你要访问 U 盘?它就在 /mnt/usb 这个树枝上——和访问 /home/yutong 没有任何语法差异。

这种设计为什么更好?

2.3 inode:文件在磁盘上的"身份证"

有了"一切皆文件"和"统一目录树"的认知后,还有一个关键概念需要理解:文件名和文件内容是两个不同的东西,它们通过 inode 关联。

什么是 inode?

inode(Index Node,索引节点)是文件系统内部的一个数据结构,每个文件(或目录)都对应唯一一个 inode。inode 里存的是:

inode 存储的内容说明
文件类型与权限是普通文件、目录还是符号链接?rwx 权限是什么?
所有者信息UID(属主)、GID(属组)
大小文件占多少字节
时间戳最后访问时间(atime)、最后修改时间(mtime)、最后属性变更时间(ctime)
数据块指针文件的实际数据存在磁盘的哪些位置
链接计数有多少个"文件名"指向这个 inode

注意 inode 里不存什么:不存文件名。文件名存在哪里?存在目录里。目录本质上是一个"文件名 → inode 编号"的映射表:

目录 "/home/yutong" 的内部结构(概念示意):

文件名          →  inode 编号
.              →  524288          (指向目录自身)
..             →  262144          (指向父目录 /home)
notes.txt      →  789012          (指向 notes.txt 的数据)
project/       →  890123          (指向 project 子目录)

inode = 房产证,记录着"这块地(数据块)的面积、位置、产权人"。文件名 = 门牌号,只是贴在门口的标签。房产证(inode)只有一份,但一栋房子可以有多个门牌号(多个文件名指向同一个 inode)——这就是硬链接(hard link)的本质。

这解释了哪些日常现象?

为什么 mv(移动/重命名)几乎瞬间完成?因为 mv 只修改了目录里的"文件名 → inode 编号"映射,没有碰实际数据。只要源和目标在同一个文件系统内(inode 编号空间一致),mv 就是改一行记录而已。

# 查看文件的 inode 编号
ls -i notes.txt
# 输出: 789012 notes.txt

# mv 改名后,inode 编号不变
mv notes.txt notes_v2.txt
ls -i notes_v2.txt
# 输出: 789012 notes_v2.txt  ← 还是同一个 inode

为什么删除大文件很快,但删除很多小文件很慢?删除文件本质是减少 inode 的链接计数 + 标记数据块为可用。大文件只有一个 inode 要处理;100 万个小文件有 100 万个 inode 要逐个处理。

为什么硬链接不能跨文件系统?因为 inode 编号只在单个文件系统内有意义。文件系统 A 的 inode 789012 和文件系统 B 的 inode 789012 是完全不同、毫无关系的两个文件。

# 创建硬链接——两个文件名,同一个 inode
ln source.txt hardlink.txt
ls -li source.txt hardlink.txt
# 输出同样的 inode 编号,链接计数 +1

# 符号链接就不一样了——它是一个"指向路径"的特殊文件,有自己的 inode
ln -s source.txt softlink.txt
ls -li source.txt softlink.txt
# 输出不同的 inode 编号

inode 与容器的关系

OverlayFS(第 04 篇)的分层机制本质上是在不同层之间管理 inode 的指向关系。写时复制(Copy-on-Write)就是:当你要修改 lower 层的一个文件时,先把它的 inode 指向的数据块复制到 upper 层,然后修改 upper 层的副本——所有这些操作对容器进程透明。

2.4 深入理解 mount:把文件系统"接入"目录树

现在你理解了"统一目录树"和"inode",挂载就好理解了。在讲命令之前,先建立直觉。

挂载的本质是什么?

一块磁盘(或一个虚拟文件系统)在被格式化后,上面有一个独立的文件系统——它有自己的目录结构、自己的 inode 编号空间。但在挂载之前,这个文件系统和 Linux 的目录树是彼此隔离的,你无法访问它

挂载(mount)就是把一个独立的文件系统"接入"到 Linux 目录树的某个节点上,让那个节点成为访问这个文件系统的入口。

挂载前:
Linux 目录树              独立文件系统(比如 U 盘)
/                          /(U 盘自己的根)
├─ home/                   ├─ 照片/
├─ mnt/  ← 空目录           ├─ 文档/
└─ var/                    └─ 音乐/

挂载后:sudo mount /dev/sdb1 /mnt/usb

Linux 目录树
/
├─ home/
├─ mnt/
│  └─ usb/  ← 现在访问这里,看到的是 U 盘的内容
│     ├─ 照片/      ← U 盘的内容"长"到了这棵树上
│     ├─ 文档/
│     └─ 音乐/
└─ var/

挂载 = 把 U 盘插到目录上。Linux 的目录树是一个插线板,每个目录节点都可以是一个"插座"。当你把 U 盘 mount 到 /mnt/usb,就等于把 U 盘插进了这个插座——之后电流(数据访问)就通了。当你 umount,就是拔掉插头——U 盘还是那个 U 盘,但不再能通过目录树访问到了。

为什么需要挂载?为什么不直接"看到"所有设备?

回到"统一目录树"的设计:因为 Linux 只有一棵树,所以任何存储资源要可访问,就必须成为这棵树的一部分。挂载就是"成为这棵树的一部分"的操作。

Windows 其实也有挂载——只是把挂载点固定在盘符(C:D:)上,你感知不到"挂载"这个动作。Linux 把这个权力完全交给用户:你想把 U 盘挂到哪个目录都行

挂载不只是接 U 盘

在前面的"一切皆文件"小节你已经看到:硬盘上的 ext4/xfs 文件系统需要挂载,虚拟文件系统同样需要挂载。实际上,Linux 系统启动时,内核会先挂载一个临时 rootfs(在内存中),然后再把真正的根文件系统挂载到 /

# 查看当前所有挂载点——你会看到各种类型
mount | head -20

# 典型输出解读:
# /dev/sda1 on / type ext4        ← 根文件系统,磁盘上的 ext4,挂载到 /
# proc on /proc type proc         ← /proc 是虚拟文件系统,挂载到 /proc
# sys on /sys type sysfs          ← /sys 是虚拟文件系统,挂载到 /sys
# tmpfs on /run type tmpfs        ← 内存文件系统,挂载到 /run
# /dev/sdb1 on /mnt/usb type vfat ← U 盘(FAT32),挂载到 /mnt/usb
文件系统类型存储在哪里典型挂载点容器场景
ext4 / xfs磁盘//home容器镜像的底层存储
tmpfs内存(掉电丢失)/tmp/run容器的 tmpfs 挂载
proc内核虚拟生成/proc每个容器需要独立的 /proc
sysfs内核虚拟生成/sysCgroup 路径依赖 /sys 挂载
overlay基于多个下层目录构建容器的 rootfsDocker 镜像分层(第 04 篇)
bind mount把现有目录"映射"到另一个位置任意docker run -v /host/path:/container/path

常用挂载命令

# 查看所有挂载点
mount
# 也可以用 findmnt 获得更清晰的树状输出
findmnt

# 挂载磁盘分区到目录
sudo mount /dev/sdb1 /mnt/usb

# 挂载时指定文件系统类型
sudo mount -t ext4 /dev/sdc1 /mnt/data

# 挂载虚拟文件系统(proc、tmpfs 等)
sudo mount -t proc proc /some/dir/proc
sudo mount -t tmpfs tmpfs /tmp

# 绑定挂载(bind mount)——让一个目录同时出现在两个位置
sudo mount --bind /original/path /another/path

# 卸载
sudo umount /mnt/usb
# 如果提示 "target is busy",说明有进程在使用该挂载点
# 用 lsof /mnt/usb 查看是谁,关闭后就能卸载

# 强制卸载(当普通卸载失败时)
sudo umount -l /mnt/usb  # lazy unmount,等没人用了再真正卸载

挂载与容器的关系——这是理解容器文件系统的关键

容器的文件系统隔离通过 Mount Namespace(第 02 篇)实现。每个容器拥有独立的挂载表(mount table):

  • 宿主机看不到容器内部的挂载操作
  • 容器看不到宿主机的挂载表全貌(除非显式共享)
  • Docker 的 -v /host:/container 本质上就是一个 bind mount,把宿主机的目录"映射"到容器内部
  • Docker 镜像的分层文件系统(OverlayFS)也是通过 mount 实现——把多个层合并挂载为容器的 /

一个帮助你记住的直觉

你可以把 Linux 的文件系统想成一棵树 + 很多个"挂件"。树干和主要树枝是根文件系统(ext4/xfs 在磁盘上),而 /proc/sys、U 盘、OverlayFS 都是"挂件"——它们通过 mount 挂在树的某个节点上。内核负责让这一切看起来像是一棵完整的、无缝的树,这就是 VFS(Virtual File System)层的工作。

2.5 虚拟文件系统的实例:/proc 与 /sys

前面讲了很多抽象概念,现在来看两个最重要的虚拟文件系统实例。它们完美体现了"一切皆文件"哲学。

/proc — 进程信息的窗口

/proc 是一个虚拟文件系统——它不占磁盘空间,内容由内核实时生成。每次你读取 /proc 下的文件,内核都会现场计算并返回当前状态。

路径内容谁在用
/proc/[PID]/某个进程的详细信息pstophtop
/proc/[PID]/ns/进程所属的各个 Namespacensenterdocker exec
/proc/[PID]/cgroup进程所属的 CgroupCgroup 管理工具
/proc/meminfo系统内存使用情况free 命令
/proc/cpuinfoCPU 信息lscpu
# /proc 中每个数字目录就是一个进程
ls /proc | grep '^[0-9]' | head -5

# 查看当前 bash 进程的信息
ls /proc/$$/
# 输出: cmdline cwd environ exe fd maps mem ns status ...

# 查看某进程的命令行
cat /proc/$$/cmdline | tr '\0' ' '

# 查看系统内存(free 命令的数据来源就是这个文件)
cat /proc/meminfo | head -5

关键认知

ps 命令不是"直接问内核有哪些进程",而是遍历 /proc 目录,读取每个数字子目录的信息拼装输出。所以在容器中如果 /proc 没有重新挂载(还是宿主机的),ps 就会显示宿主机所有进程——这就是 Namespace 篇中 --mount-proc 参数的意义。

/sys — 内核子系统的控制面板

/sys(sysfs)也是虚拟文件系统,主要暴露内核子系统和设备的信息:

# 查看所有网络接口
ls /sys/class/net/
# 输出: eth0  lo  docker0  veth1234

# 通过 sysfs 查看某网卡的 MAC 地址
cat /sys/class/net/eth0/address

/proc 偏向"进程视角"(每个进程的状态),/sys 偏向"设备/子系统视角"(硬件和内核模块的状态)。两者都是内核暴露信息的窗口,容器技术大量依赖它们——Cgroup 通过写文件限制资源,Namespace 通过读文件查看隔离状态。

三、网络基础

网络接口(Network Interface)

网络接口是 Linux 收发网络数据的端点,可以是物理的也可以是虚拟的:

接口类型用途
eth0物理网卡连接外部网络
lo回环(loopback)本机自己跟自己通信(127.0.0.1)
docker0虚拟网桥Docker 容器的默认网关
vethXXXX虚拟以太网连接容器和网桥

对内核来说,物理网卡和虚拟网卡没有本质区别——都是"网络接口"这个抽象的实例,都能配 IP、收发数据包。这是容器网络的基础。

# 查看所有网络接口及其 IP
ip addr show

# 简写
ip a

IP 地址与子网

私有地址(内网地址)

RFC 1918 规定了三段永远不会出现在公网的地址,任何人都可以在内网随意使用:

网段范围常见用途
10.0.0.0/810.0.0.0 ~ 10.255.255.255大型企业内网、云厂商 VPC、容器网络
172.16.0.0/12172.16.0.0 ~ 172.31.255.255Docker 默认网段(172.17.0.0/16)
192.168.0.0/16192.168.0.0 ~ 192.168.255.255家庭路由器、小型办公网络

CIDR 表示法

IP 地址 32 位,/数字 表示前多少位是网络号,剩余是主机号

10.0.0.1/24

网络号:10.0.0.___   (前 24 位,标识"哪个子网")
主机号:_______.1     (后 8 位,标识"子网中第几台")

该子网范围:10.0.0.0 ~ 10.0.0.255(可用主机地址 .1 ~ .254)

类比邮政地址:网络号 = 街道名,主机号 = 门牌号。同一条街(同一子网)的住户可以直接喊话通信,不同街道需要通过邮局(路由器)转发。/24 就是说"街道名占了地址的前 24 位"。

快速记忆:

veth pair — 虚拟网线

veth = Virtual Ethernet。veth pair 是成对出现的虚拟网络接口——数据从一端进,必定从另一端出:

┌─────────────────┐              ┌─────────────────┐
│   Namespace A    │              │   Namespace B    │
│                  │              │                  │
│     [veth-a] ●━━━━━━ 虚拟网线 ━━━━━━● [veth-b]     │
│                  │              │                  │
└─────────────────┘              └─────────────────┘

从 veth-a 发出的数据 → 立刻出现在 veth-b
从 veth-b 发出的数据 → 立刻出现在 veth-a

物理世界里,两台电脑用一根网线直连就能通信。veth pair 就是这根网线的虚拟版。Network Namespace 隔离后两个空间完全不通(像两个密封的房间),veth pair 就是在墙上打一个洞穿一根线,让两边能互通数据。

# 创建一对 veth
sudo ip link add veth-a type veth peer name veth-b

# 查看——两个接口同时出现
ip link show type veth

# 给 veth-a 配 IP 并启动
sudo ip addr add 10.0.0.1/24 dev veth-a
sudo ip link set veth-a up

# 给 veth-b 配 IP 并启动
sudo ip addr add 10.0.0.2/24 dev veth-b
sudo ip link set veth-b up

# 测试连通
ping -c 2 10.0.0.2

# 清理
sudo ip link del veth-a  # 删一端,另一端自动消失

网桥(Bridge)— 虚拟交换机

veth pair 是一对一的连接。当有多个容器需要互通时,不可能两两直连(N 个容器需要 N*(N-1)/2 根线)。这时需要网桥(Bridge)

┌──────────┐  ┌──────────┐  ┌──────────┐
│  容器 A   │  │  容器 B   │  │  容器 C   │
│ [veth-a]  │  │ [veth-b]  │  │ [veth-c]  │
└─────┬─────┘  └─────┬─────┘  └─────┬─────┘
      │              │              │
━━━━━━┷━━━━━━━━━━━━━━┷━━━━━━━━━━━━━━┷━━━━━━━━  docker0 网桥
                     │
                  [宿主机网络]
物理世界虚拟世界
交换机(Switch)Linux Bridge(如 docker0
网线插到交换机端口veth 一端连到 Bridge
交换机转发数据帧给目标端口Bridge 按 MAC 地址转发给目标 veth
# 创建网桥
sudo ip link add br0 type bridge
sudo ip link set br0 up

# 创建两对 veth,一端接入网桥
sudo ip link add veth1 type veth peer name veth1-br
sudo ip link add veth2 type veth peer name veth2-br

# 把 "网桥侧" 的端接入 br0
sudo ip link set veth1-br master br0
sudo ip link set veth2-br master br0
sudo ip link set veth1-br up
sudo ip link set veth2-br up

# 给 "容器侧" 配 IP
sudo ip addr add 10.0.0.1/24 dev veth1
sudo ip addr add 10.0.0.2/24 dev veth2
sudo ip link set veth1 up
sudo ip link set veth2 up

# veth1 和 veth2 现在可以通过 br0 互通
ping -c 2 -I veth1 10.0.0.2

Docker 网络的本质

安装 Docker 后系统多出一个 docker0 网桥。每创建一个容器,Docker 就生成一对 veth——一端放进容器的 Network Namespace(容器里看到的 eth0),另一端接到 docker0 网桥。所有容器通过这个网桥互通,再通过 NAT/iptables 规则与外网通信。

小结

本篇要点回顾

模块核心认知后续关联
进程模型 fork 复制 + exec 替换;PID 是进程身份证;所有进程形成树 PID Namespace 让容器看到独立的进程树(第 02 篇)
文件系统基础 一切皆文件(统一 open/read/write/close 接口);统一目录树(单根 /);inode 是文件身份证(文件名≠文件内容) 容器 rootfs 就是一棵独立的目录树;OverlayFS 的分层本质是 inode 指向的切换(第 04 篇)
mount 挂载 所有文件系统必须通过 mount 接入目录树才能访问;不只是接 U 盘,proc/sys/overlay 等虚拟文件系统也通过 mount 接入 Mount Namespace 隔离挂载表;Docker -v 本质是 bind mount;OverlayFS 通过 mount 实现分层合并(第 02、04 篇)
/proc 与 /sys 虚拟文件系统,不占磁盘,内核实时生成;ps 命令读的是 /proc 而非直接问内核 Namespace 通过 /proc/PID/ns/ 暴露;Cgroups 通过 /sys/fs/cgroup/ 控制资源(第 03 篇)
网络接口 物理网卡和虚拟网卡对内核无区别,都能配 IP 收发数据 Network Namespace 给容器一套独立的网络接口(第 02 篇)
IP/子网/CIDR 10.x / 172.16~31.x / 192.168.x 是私有地址;/24 表示子网大小 容器网络全部使用私有地址
veth pair 成对的虚拟网线,数据从一端进另一端出 连接容器 NS 与宿主机网桥(第 02 篇 Network NS)
网桥 虚拟交换机,多个 veth 接入后可互通 docker0 就是一个网桥,所有容器通过它互联

动手练习

  1. 运行 pstree -p 观察你的系统进程树,找到当前 shell 的位置
  2. 创建一个临时文件,用 ls -li 查看它的 inode 编号;mv 重命名后再次查看,验证 inode 不变;创建一个硬链接,观察两个文件名是否共享同一个 inode
  3. ls /proc/$$/ 探索你当前 bash 进程的信息,尝试读取 cmdlinestatusns/
  4. 运行 mount | grep -E 'proc|sys|cgroup',看看系统中有哪些虚拟文件系统被挂载
  5. ip addr 查看你系统上有哪些网络接口,哪些是物理的、哪些是虚拟的
  6. 创建一对 veth,配上 IP,用 ping 验证连通性后删除