LINUX系统-18-EXT系列文件系统(三)

Linux-EXT系列文件系统

1、路径解析

在上一章节的内容中我们可以看到,想要打开目录中的目录,必须先加载目录中目录的内容,也就是先加载目录文件的数据,那张映射表存放着inode与文件名之间的关系。

例如我现在要访问:/home/benjiang/Bite/lesson26/test 这个路径下的test目录下的code.c这个文件,那么我势必需要从根目录 "/"开始进行解析,一层一层加载目录的inode和文件名之间的关系,才能最终找到code.c这个文件,这个过程叫做路径解析。

1、从根目录进行路径解析

具体解析过程:系统会先拿到根目录/的inode,读取根目录的数据块,从映射表中找到home对应的inode编号,接着拿着home路径的inode编号,去读取home目录的数据块,从映射表中找到对应benjiang的inode编号...如此层层递进,类似于递归,最终找到code.c的inode。

2、相对路径进行路径解析

但是现在我没有一层一层递进进入目录中,而是输入了相对路径,怎么办呢?

bash 复制代码
(base) benjiang@BENJIANGLIU:~/Bite/lesson25$ pwd
/home/benjiang/Bite/lesson25

(base) benjiang@BENJIANGLIU:~/Bite/lesson25$ cd ../lesson26
# 此处使用相对路径从lesson25进入了lesson26目录

(base) benjiang@BENJIANGLIU:~/Bite/lesson26$ pwd
/home/benjiang/Bite/lesson26

如果路径不以 / 开头(例如 ./test/code.c),系统则会从当前进程的当前工作目录(Current Working Directory, CWD)的 inode 开始解析,假如在shell中输入进行目录路径解析,从shell的内容可以看到。

bash 复制代码
base) benjiang@BENJIANGLIU:~$ ps -axj | head -1 && ps -axj | grep shell
   PPID     PID    PGID     SID TTY        TPGID STAT   UID   TIME COMMAND
  29141   29228   29228   29228 pts/11     29813 Ss    1000   0:00 /bin/bash --init-file /home/benjiang/.vscode-server/bin/8761a5560cfd65fdd19ce7e2bd18dab5c0a4d84e/out/vs/workbench/contrib/terminal/common/scripts/shellIntegration-bash.sh
  29228   29814   29813   29228 pts/11     29813 S+    1000   0:00 grep --color=auto shell
# 发现shell的进程编号是 29228

(base) benjiang@BENJIANGLIU:~$ ls /proc/29228 -l
total 0
lrwxrwxrwx  1 benjiang benjiang 0 Jun  2 22:12 cwd -> /home/benjiang # 这既是shell的工作路径
-r--------  1 benjiang benjiang 0 Jun  2 22:12 environ
lrwxrwxrwx  1 benjiang benjiang 0 Jun  2 22:15 exe -> /usr/bin/bash
dr-x------  2 benjiang benjiang 4 Jun  2 22:12 fd

(base) benjiang@BENJIANGLIU:~$ pwd # 查看当前工作路径确实是 /home/benjiang
/home/benjiang

所以我们得到结论,相对路径的路径解析是通过当前进程的cwd提供的路径。

3、小总结

从这里可以看出几个问题:

1、为什么需要 / 根目录?

很显然,如果没有根目录,在OS进行路径解析的时候,就不知道从哪里开始了。

就算是相对路径解析时进程提供的cwd,也是从根目录开始的。

2、linux中为什么都会有家目录home ?在这个目录下可以新建目录?

其实本质就是在磁盘文件系统中,新建目录文件 。而你新建的所有文件,都是在你或者系统指定的目录下新建的,这就是天然的路径。而linux文件系统本质上就是一颗多叉树!这个多叉树的根节点就是 根目录 !

4、路径缓存

因为在linux系统中不存在所谓的"目录",目录的本质是一个文件,只保存文件原属性(indoe)和文件内容(数据块)。

这个文件的数据块内容是一张映射表,里面存放着目录内文件名和inode的映射关系。

而我们在访问目录中的文件时,原则上都是从根目录进行解析的,但是这样太慢,因为每次都需要进行磁盘IO,查看数据存放的位置,如果我们把之前打开过的文件或者目录的路径都缓存起来,下次读取的时候,直接读取缓存中的内容,是不是会变快了?

当然!而且linux也是这么做的,会缓存历史路径结构,它在内存中维护了一个被称为 Dcache(Directory Entry Cache,目录项缓存) 的树形结构,这个缓存由一个叫做dentry的结构体进行维护。

dentry结构体代码:

c 复制代码
struct dentry {
	atomic_t d_count;
	unsigned int d_flags;		/* protected by d_lock */
	spinlock_t d_lock;		/* per dentry lock */
	struct inode *d_inode;		/* Where the name belongs to - NULL is
					 * negative */
	/*
	 * The next three fields are touched by __d_lookup.  Place them here
	 * so they all fit in a cache line.
	 */
	struct hlist_node d_hash;	/* lookup hash list */
	struct dentry *d_parent;	/* parent directory */
	struct qstr d_name;

	struct list_head d_lru;		/* LRU list */
	/*
	 * d_child and d_rcu can share memory
	 */
	union {
		struct list_head d_child;	/* child of parent list */
	 	struct rcu_head d_rcu;
	} d_u;
	struct list_head d_subdirs;	/* our children */
	struct list_head d_alias;	/* inode alias list */
	unsigned long d_time;		/* used by d_revalidate */
	struct dentry_operations *d_op;
	struct super_block *d_sb;	/* The root of the dentry tree */
	void *d_fsdata;			/* fs-specific data */
#ifdef CONFIG_PROFILING
	struct dcookie_struct *d_cookie; /* cookie, if any */
#endif
	int d_mounted;
	unsigned char d_iname[DNAME_INLINE_LEN_MIN];	/* small names */
};

每一个被访问过的文件或目录组件(如 /home/user/file.txt 中的 home、user、file.txt),都会在内存中生成一个 struct dentry 对象。

层级关系:每个 dentry 都包含指向其父节点的指针(d_parent)和子节点列表(d_subdirs)。通过这些指针,内核在内存中完美地映射了文件系统的层级结构。

2、挂载分区

抛出一个问题,我现在的linux硬盘上一块有128GB,现在硬盘空间不足了,我需要在linux电脑上再连接一个256GB的硬盘,我直接用IDE、PICE、M2的方式连接到电脑,电脑就能够识别到这块硬盘码?直接就可以读取硬盘上的数据吗?

答案:硬盘连接方式(如M.2、PCIe或SATA)只要正常通电,开机后Linux内核是完全可以识别到这块256GB新硬盘的。但仅仅识别到硬件并不等于能直接读取数据 。这是因为Linux系统与Windows不同,它没有自动分配盘符(如D盘、E盘)的机制。这就引出了Linux存储管理中一个极其重要的核心概念------挂载(Mount)。

**在Linux的单一文件系统树结构中,所有的存储设备都必须通过"挂载"操作,才能与系统目录树中的某个具体目录(即"挂载点",如 /mnt/data)建立关联。**打个比方,新硬盘就像是一个装满文件的U盘,而"挂载点"就像是电脑上的USB接口;只有把U盘插进接口(执行挂载命令),你才能顺着这个接口访问里面的内容。

因此,即使这块硬盘里原本就存有数据,且使用的是Linux支持的文件系统(如EXT4、XFS,甚至是Windows常用的NTFS等),你也必须先手动创建一个空目录作为入口,然后通过 mount 命令将硬盘分区挂载到这个目录下,才能真正读取其中的文件。如果希望每次开机都能自动读取,还需要将挂载信息写入 /etc/fstab 配置文件中。简而言之:硬件识别只是第一步,挂载才是赋予数据访问权限的关键钥匙。

1、一个挂载试验

我们在家目录里面从硬盘中创建一个大的磁盘块,当做一个分区,并挂载。

bash 复制代码
(base) benjiang@BENJIANGLIU:~$ dd if=/dev/zero of=./disk.img bs=1M count=5 # 分割5M大小的磁盘文件
5+0 records in
5+0 records out
5242880 bytes (5.2 MB, 5.0 MiB) copied, 0.00330404 s, 1.6 GB/s
(base) benjiang@BENJIANGLIU:~$ ll
total 164596
drwxr-x--- 19 benjiang benjiang      4096 Jun  3 21:52 ./
drwxr-xr-x  3 root     root          4096 Sep 16  2025 ../
-rw-r--r--  1 benjiang benjiang   5242880 Jun  3 21:52 disk.img  # 查看当前分割出来的文件
drwxr-xr-x 18 benjiang benjiang      4096 May 18 10:22 miniconda3/
-rw-r--r--  1 benjiang benjiang         5 Apr 15 20:37 test.txt

(base) benjiang@BENJIANGLIU:~$ pwd # 当前路径
/home/benjiang

(base) benjiang@BENJIANGLIU:~$ mkfs.ext4 disk.img # 写入ext4文件系统
mke2fs 1.47.0 (5-Feb-2023)

Filesystem too small for a journal
Discarding device blocks: done
Creating filesystem with 1280 4k blocks and 1280 inodes

Allocating group tables: done
Writing inode tables: done
Writing superblocks and filesystem accounting information: done

(base) benjiang@BENJIANGLIU:~$ df -h # 查看当前磁盘内容,只有一个ssd,其中挂载了根目录
Filesystem      Size  Used Avail Use% Mounted on
/dev/sdd       1007G  8.4G  948G   1% /
tmpfs           1.6G   20K  1.6G   1% /run/user/1000

(base) benjiang@BENJIANGLIU:~$ sudo mkdir /mnt/mydisk # 创建挂载路径
[sudo] password for benjiang:

# 将新创建的分区挂载在/mnt/mydisk 目录下
(base) benjiang@BENJIANGLIU:~$ sudo mount -t ext4 ./disk.img /mnt/mydisk/ 
(base) benjiang@BENJIANGLIU:~$ df -h
Filesystem      Size  Used Avail Use% Mounted on
/dev/sdd       1007G  8.4G  948G   1% /
tmpfs           1.6G   20K  1.6G   1% /run/user/1000
# 此处发现已经多了个4.7M的磁盘
/dev/loop0      4.7M   24K  4.4M   1% /mnt/mydisk

2、挂载的过程

看过程:

1、首先先创建一个大文件,当做分区使用,此处类似于插入一块刚买来的新的磁盘。

2、然后格式化写入文件系统,此处非常重要!因为格式化的过程本质就是写入文件系统 ,之前已经说了文件系统的写入本质其实是磁盘管理信息的写入 。如果没有写入正确的、linux可识别的文件系统,linux是无法读写硬盘数据的!

3、创建一个或者使用已经存在的文件夹,当做新磁盘挂载的锚点,并挂载磁盘,例如我写了/mnt/mydisk,那么以后访问这个5M大小的空间就是通过/mnt/mydisk这个路径去访问的!

3、挂载结论

分区写入文件系统后,是无法直接使用的,需要与指定的目录进行关联,这个关联的过程就是挂载

所以我就可以通过访问目标文件的前缀来判断我在哪一个分区里面!

3、软硬链接

1、硬连接

在linux系统中,我们找到磁盘中文件的并不是文件名,而是inode,其实在linux中可以让多个文件指向一个inode。

例子:

bash 复制代码
(base) benjiang@BENJIANGLIU:~/Bite/lesson30$ touch abc
# 创建一个文件

(base) benjiang@BENJIANGLIU:~/Bite/lesson30$ ln abc def
# 给这个文件创建一个硬链接

(base) benjiang@BENJIANGLIU:~/Bite/lesson30$ ls -li
total 0
18337 -rw-r--r-- 2 benjiang benjiang 0 Jun  3 22:14 abc
18337 -rw-r--r-- 2 benjiang benjiang 0 Jun  3 22:14 def
# 我们发现这两个文件的inode值是一样的!
# 而且硬链接数为2

在上面的代码中我们可以看到:

ln是创建硬链接的命令

c 复制代码
ln -s [源文件或目录] [链接名]

硬链接可以理解为同一个文件的"多个平等入口"。它通过指向同一个 inode 节点来实现链接。

创建命令:ln 源文件 链接名(默认不加 -s 即为硬链接)

核心特点:

同步变化:硬链接与源文件完全等同,修改任意一个,另一个都会同步改变。

防误删机制:删除源文件或硬链接中的任意一个,文件实体并不会被真正删除,只有当所有的硬链接都被删除(链接数归零)时,文件才会被物理删除。

不跨文件系统:硬链接只能在同一个文件系统(分区)内创建。

不支持目录:不允许对目录创建硬链接(防止文件系统出现死循环)。

共享 Inode:硬链接与源文件共享同一个 inode 编号。

bash 复制代码
(base) benjiang@BENJIANGLIU:~/Bite/lesson30$ pwd
/home/benjiang/Bite/lesson30

(base) benjiang@BENJIANGLIU:~/Bite/lesson30$ echo "测试硬链接" >> abc

(base) benjiang@BENJIANGLIU:~/Bite/lesson30$ cat def
测试硬链接

(base) benjiang@BENJIANGLIU:~/Bite/lesson30$ rm -rf abc

(base) benjiang@BENJIANGLIU:~/Bite/lesson30$ cat def
测试硬链接

从上面这段代码中可以看到,我们想源文件内写入的数据,在硬连接的文件中也可以读取!而且我们删除了源文件后,发现硬连接的文件仍然可以读取的源文件中的内容,为什么?看图

在文件系统中,/home/benjiang/Bite/lesson30 这个目录文件的映射表中,无论是abc还是def,这两个文件名的inode值其实是指向的同一块data block中区域。所以就算你删除了所谓的"源文件",硬连接的文件一样可以指向数据源!

引用计数(Link Count):注意看 ls -li 输出中权限位后面的那个数字 2。这个数字叫做"链接数"。当创建 def 时,这个计数从 1 变成了 2。只有当这个计数归零时,操作系统才会真正释放数据块的占用。

在删除文件的时候,我们干了2件事:第一将目录中的对应关系删除,第二将硬链接数-1,如果硬连接数为0,则将对应的磁盘空间释放。

硬连接的意义:一个轻量化的备份,不用cp源文件就可以做到删除源文件而备份不丢失。

为什么新建目录的硬连接数2???

bash 复制代码
(base) benjiang@BENJIANGLIU:~/Bite/lesson30$ mkdir new
(base) benjiang@BENJIANGLIU:~/Bite/lesson30$ ll
total 16
drwxr-xr-x  3 benjiang benjiang 4096 Jun  3 22:40 ./   # 指向本级目录lesson30
drwxr-xr-x 24 benjiang benjiang 4096 Jun  3 22:14 ../  # 指向上级目录Bite
-rw-r--r--  1 benjiang benjiang   16 Jun  3 22:20 def
drwxr-xr-x  2 benjiang benjiang 4096 Jun  3 22:40 new/ # 新建目录的连接数是2

因为 ... 的存在,当我们cd进new这个目录的时候,ls发现目录中存在 . 和 ... 这两个文件。

这2个文件的本质就是,本级目录和上级目录的硬连接!

2、软连接

硬连接是通过inode引用另外一个文件,软链接是是通过名字引用另外一个文件,单实际上,新的文件和被引用的文件的inode不同!应用常见上可以理解为一个快捷方式。

bash 复制代码
(base) benjiang@BENJIANGLIU:~/Bite/lesson30$ ls -li
total 12
18337 -rw-r--r-- 2 benjiang benjiang   16 Jun  3 22:20 abc
18337 -rw-r--r-- 2 benjiang benjiang   16 Jun  3 22:20 def
 1763 drwxr-xr-x 2 benjiang benjiang 4096 Jun  3 22:40 new
 
(base) benjiang@BENJIANGLIU:~/Bite/lesson30$ ln -s abc abc.s
# 建立一个软连接

(base) benjiang@BENJIANGLIU:~/Bite/lesson30$ ls -li
total 12
18337 -rw-r--r-- 2 benjiang benjiang   16 Jun  3 22:20 abc
 1860 lrwxrwxrwx 1 benjiang benjiang    3 Jun  3 22:47 abc.s -> abc
 # 软连接 abc.s 指向了 abc
18337 -rw-r--r-- 2 benjiang benjiang   16 Jun  3 22:20 def
 1763 drwxr-xr-x 2 benjiang benjiang 4096 Jun  3 22:40 new

(base) benjiang@BENJIANGLIU:~/Bite/lesson30$ cat abc.s
测试硬链接

(base) benjiang@BENJIANGLIU:~/Bite/lesson30$ readlink abc.s # 读取软链接的内部存储的数据
abc

我们readlink 发现abs.s这个软连接内部存储的是abc,这个它连接的文件名。

继续

bash 复制代码
(base) benjiang@BENJIANGLIU:~/Bite/lesson30$ cd new/
(base) benjiang@BENJIANGLIU:~/Bite/lesson30/new$ ll
total 8
drwxr-xr-x 2 benjiang benjiang 4096 Jun  3 23:16 ./
drwxr-xr-x 3 benjiang benjiang 4096 Jun  3 23:16 ../
lrwxrwxrwx 1 benjiang benjiang    3 Jun  3 22:47 abc.s -> abc

(base) benjiang@BENJIANGLIU:~/Bite/lesson30/new$ cat abc.s
cat: abc.s: No such file or directory 
# 发现因为使用的相对路径,abc.s在移动到new目录后无法继续使用了

(base) benjiang@BENJIANGLIU:~/Bite/lesson30/new$ ln -s ../abc newabc.s
(base) benjiang@BENJIANGLIU:~/Bite/lesson30/new$ ll
total 8
drwxr-xr-x 2 benjiang benjiang 4096 Jun  3 23:22 ./
drwxr-xr-x 3 benjiang benjiang 4096 Jun  3 23:16 ../
lrwxrwxrwx 1 benjiang benjiang    3 Jun  3 22:47 abc.s -> abc
lrwxrwxrwx 1 benjiang benjiang    6 Jun  3 23:22 newabc.s -> ../abc
(base) benjiang@BENJIANGLIU:~/Bite/lesson30/new$ readlink newabc.s
../abc # 确定默认使用的是相对路径

上面2个例子能够看出软连接是一个独立的文件,他有自己的inode和data block,而他的data block中存储的是它指向文件的相对路径!

那么我们如何避免在移动软连接文件后导致的相对路径失效问题呢?使用绝对路径

bash 复制代码
(base) benjiang@BENJIANGLIU:~/Bite/lesson30/new$ pwd
/home/benjiang/Bite/lesson30/new

(base) benjiang@BENJIANGLIU:~/Bite/lesson30/new$ ln -s /home/benjiang/Bite/lesson30/abc ./abs.s
# 使用绝对路径创建软连接
(base) benjiang@BENJIANGLIU:~/Bite/lesson30/new$ ll
total 8
drwxr-xr-x 2 benjiang benjiang 4096 Jun  3 23:26 ./
drwxr-xr-x 3 benjiang benjiang 4096 Jun  3 23:16 ../
lrwxrwxrwx 1 benjiang benjiang    3 Jun  3 22:47 abc.s -> abc # 已经因为移动文件导致相对路径失效的软连接
lrwxrwxrwx 1 benjiang benjiang   32 Jun  3 23:26 abs.s -> /home/benjiang/Bite/lesson30/abc # 使用绝对路径的软连接
lrwxrwxrwx 1 benjiang benjiang    6 Jun  3 23:22 newabc.s -> ../abc

(base) benjiang@BENJIANGLIU:~/Bite/lesson30/new$ mkdir new # 创建新目录

(base) benjiang@BENJIANGLIU:~/Bite/lesson30/new$ mv abs.s ./new # 将软连接移动到new目录
(base) benjiang@BENJIANGLIU:~/Bite/lesson30/new$ cd new
(base) benjiang@BENJIANGLIU:~/Bite/lesson30/new/new$ ll
total 8
drwxr-xr-x 2 benjiang benjiang 4096 Jun  3 23:27 ./
drwxr-xr-x 3 benjiang benjiang 4096 Jun  3 23:27 ../
lrwxrwxrwx 1 benjiang benjiang   32 Jun  3 23:26 abs.s -> /home/benjiang/Bite/lesson30/abc
(base) benjiang@BENJIANGLIU:~/Bite/lesson30/new/new$ cat abs.s
测试硬链接
# 发现就算移动了软链接的,但是因为使用了绝对路径,软连接依然有效

3、软硬连接对比

特性 软连接(Symbolic Link) 硬链接(Hard Link)
底层本质 是一个独立的特殊文件,拥有自己独立的 inode,其内容仅存储目标文件的路径字符串(类似于 Windows 快捷方式)。 是原始文件的"别名",与原始文件共享同一个 inode 和数据块,在系统看来它们就是同一个文件。
跨文件系统 支持。可以跨越不同的磁盘分区或文件系统创建链接。 不支持。只能在同一个文件系统(同一分区)内创建。
链接对象 支持目录。可以对普通文件或目录创建软连接。 不支持目录。只能对普通文件创建硬链接(Linux 为防止目录树循环引用,默认禁止对目录创建硬链接)。
删除原文件后 链接失效。如果目标文件被删除或移动,软连接会变成"断链"(悬空链接),无法访问。 不受影响。只要还有一个硬链接存在,文件数据就不会被真正删除,依然可以正常访问。
权限与属性 软连接自身权限通常显示为 lrwxrwxrwx,但实际访问权限由它指向的目标文件决定。 与原始文件完全共享权限、所有者、大小和修改时间,修改任何一个,另一个都会同步变化。
创建命令 ln -s <目标路径> <链接名> ln <目标文件> <链接名>
相关推荐
无足鸟ICT1 小时前
【RHCA+】~_.vimrc
linux
zhangfeng11331 小时前
,在slurm中也能安装ubundu了,Singularity(现叫 Apptainer)不需要root权限的容器方案,对比docker
运维·人工智能·机器学习·docker·容器
皆圥忈1 小时前
深入理解进程虚拟地址空间
linux
一只鹿鹿鹿1 小时前
网络安全和安防建设方案(doc文件)
大数据·运维·网络·物联网·安全
LJianK11 小时前
服务器高 CPU 排查方法
linux·运维·服务器
liu-yonggang1 小时前
Linux vs QNX 深度对比
linux·qnx
皆圥忈1 小时前
Linux 进程控制(上):创建、终止与等待
linux·运维·服务器
one优雅的猫1 小时前
Linux 常用命令
linux·运维·服务器
安静轨迹1 小时前
服务器性能指标:TPS、CPS、QPS 全解
运维·服务器