文章目录
- 一、理解硬件
- 二、⽂件系统
- [三、ext2 ⽂件系统](#三、ext2 ⽂件系统)
-
- 1、宏观认识
- [2、Block Group](#2、Block Group)
- 3、块组内部构成
-
- [1)超级块(Super Block)](#1)超级块(Super Block))
- [2)GDT(Group Descriptor Table)](#2)GDT(Group Descriptor Table))
- [3)块位图(Block Bitmap)](#3)块位图(Block Bitmap))
- [4)inode位图(Inode Bitmap)](#4)inode位图(Inode Bitmap))
- [5)i节点表(Inode Table)](#5)i节点表(Inode Table))
- [6)数据块(Data Block)](#6)数据块(Data Block))
- [4、inode 和 data block 映射](#4、inode 和 data block 映射)
- 5、⽬录与⽂件名
- 6、路径解析
- 7、路径缓存
- 8、挂载分区
- 9、⽂件系统总结
- 四、软硬链接
一、理解硬件
1、磁盘、服务器、机柜、机房
-
机械磁盘是计算机中唯⼀的⼀个机械设备(外设)
-
优点容量⼤,价格便宜,缺点慢


2、磁盘的物理结构

3、磁盘的存储结构

-
扇区(sector):磁盘存储数据的基本单位,通常⼤⼩为 512 字节。
-
柱⾯(cylinder):磁道构成柱⾯,数量上等同于磁道个数。
-
磁道(track):磁道是从盘⽚外圈往内圈编号0磁道,1磁道...,靠近主轴的同⼼圆⽤于停靠磁头,不存储数据。每个磁道都被切分成很多扇形区域,每道的扇区数量相同。
-
磁头(head):每个盘⽚⼀般有上下两⾯,每面对应1个磁头。
-
圆盘(platter)数:盘⽚的数量
-
磁盘容量 = 磁头数 × 磁道(柱⾯)数 × 每道扇区数 × 每扇区字节数
如何定位⼀个扇区呢?采用CHS地址定位!
- 先找到柱⾯(磁道)
- 再定位磁头
- 最后定位扇区
CHS寻址对早期的磁盘⾮常有效,知道⽤哪个磁头,读取哪个柱⾯上的第⼏扇区就可以读到数据了。
但是CHS模式⽀持的硬盘容量有限,因为系统⽤ 8bit 来存储磁头地址,⽤ 10bit 来存储柱⾯地址,⽤ 6bit 来存储扇区地址,⽽⼀个扇区共有512Byte,这样使⽤CHS寻址⼀块硬盘最⼤容量为28 * 210 * (26-1) * 512B = 8064 MB(1MB = 1048576B)(若按1MB=1000000B来算就是8.4GB)
4、磁盘的逻辑结构
1)理解过程

磁带上⾯可以存储数据,我们可以把磁带"拉直",形成线性结构

同理磁盘本质上虽然是硬质的,但是逻辑上我们可以把磁盘想象成为卷在⼀起的磁带,那么磁盘的逻辑存储结构我们也可以类似于:

这样每⼀个扇区,就有了⼀个线性地址(也就是数组下标),这种地址叫做LBA 。

2)真实过程

柱⾯是⼀个逻辑上的概念,其实就是每⼀⾯上,相同半径的磁道逻辑上构成柱⾯。所以,磁盘物理上分了很多⾯,但是在我们看来,逻辑上,磁盘整体是由"柱⾯"卷起来的。
所以,磁盘的真实情况是:
磁道:
某⼀盘⾯的某⼀个磁道展开:

即:⼀维数组
柱⾯:
整个磁盘所有盘⾯的同⼀个磁道,即柱⾯展开:

即:二维数组
整盘:
整个磁盘不就是多张⼆维的扇区数组表

即:三维数组
所以寻址⼀个扇区:先找到哪⼀个柱⾯(cylinder),在确定柱⾯内哪⼀个磁道(其实就是磁头(head)位置),在确定扇区(Sector),所以就有了CHS。
我们知道每⼀个扇区都有⼀个下标,我们叫做LBA(Logical Block Address)地址,其实就是线性地址。所以怎么计算得到这个LBA地址呢?

5、CHS && LBA 地址
CHS转成LBA:
-
单个柱⾯的扇区总数 = 磁头数 * 每磁道扇区数
-
LBA = 柱⾯号C * 单个柱⾯的扇区总数 + 磁头号H * 每磁道扇区数 + 扇区号S - 1
注意:
CHS中扇区号从1开始编号,柱⾯和磁道从0开始编号
LBA中地址从0开始编号
LBA转成CHS:
-
柱⾯号C = LBA / 单个柱⾯的扇区总数
-
磁头号H = LBA % 单个柱⾯的扇区总数 / 每磁道扇区数
-
扇区号S = LBA % 每磁道扇区数 + 1
从此往后,在磁盘使⽤者看来,根本就不关⼼CHS地址,⽽是直接使⽤LBA地址,磁盘内部⾃⼰转换。
所以从现在开始,磁盘就是⼀个以扇区为元素的⼀维数组,数组的下标就是每⼀个扇区的LBA地址。OS使⽤磁盘,就可以⽤⼀个数字访问磁盘扇区了。
二、⽂件系统
1、块概念
其实硬盘是典型的 "块" 设备,操作系统读取硬盘数据的时候,其实是不会⼀个个扇区地读取,这样效率太低,⽽是⼀次性连续读取多个扇区,即⼀次性读取⼀个"块"(block)。
硬盘的每个分区是被划分为⼀个个的"块"。⼀个"块"的⼤⼩是由格式化的时候确定的,并且不可以更改,最常⻅的是4KB,即连续⼋个扇区。"块"是⽂件存取的最⼩单位。
注意:
-
块号 = LBA / 8
-
LAB = 块号*8 + n(n是块内第⼏个扇区)

2、分区概念
其实磁盘是可以被分成多个分区(partition)的,以Windows观点来看,你可能会有⼀块磁盘并且将它分区成C,D,E盘,这就是分区。分区从实质上说就是对硬盘的⼀种格式化。但是Linux的设备都是以⽂件形式存在,那是怎么分区的呢?
柱⾯是分区的最⼩单位 ,我们可以利⽤参考柱⾯号码的⽅式来进⾏分区,其本质就是设置每个区的起始柱⾯和结束柱⾯号码。此时我们可以将硬盘上的柱⾯(分区)进⾏平铺,将其想象成⼀个⼤的平⾯,如下图所示:

在柱面大小、扇区数量均一致的前提下,只需知晓每个分区的起始与结束柱面号,以及每个柱面对应的扇区数,即可明确该分区的容量大小,同时也能解读出对应的 LBA 值。
3、inode概念
之前我们说过 ⽂件= 内容 + 属性 ,我们使⽤ ls -l 的时候除了看到⽂件名,还能看到⽂件元数据(属性)。

每⾏包含7列:
- 文件类型 + 权限
- 硬链接数
- 所有者
- 所属组
- 文件⼤⼩
- 最后修改时间
- ⽂件名
除了上述方式,还有⼀个stat命令能够看到更多信息:

我们要思考⼀个问题,⽂件数据都储存在"块"中,那么很显然,我们还必须找到⼀个地⽅储存⽂件的元信息(属性信息),⽐如⽂件的创建者、⽂件的创建⽇期、⽂件的⼤⼩等等。这种储存⽂件元信息的区域就叫做 inode ,中⽂译名为"索引节点"。

注意:
- Linux下⽂件的存储是属性和内容分离存储的。
- Linux下保存⽂件属性的集合叫做inode,⼀个⽂件对应⼀个inode,inode内有⼀个唯⼀的标识符,叫做inode号。
所以⼀个⽂件的属性inode⻓什么样⼦呢?
cpp
struct ext2_inode {
__le16 i_mode; /* File mode */
__le16 i_uid; /* Low 16 bits of Owner Uid */
__le32 i_size; /* Size in bytes */
__le32 i_atime; /* Access time */
__le32 i_ctime; /* Creation time */
__le32 i_mtime; /* Modification time */
__le32 i_dtime; /* Deletion Time */
__le16 i_gid; /* Low 16 bits of Group Id */
__le16 i_links_count; /* Links count */
__le32 i_blocks; /* Blocks count */
__le32 i_flags; /* File flags */
union {
struct {
__le32 l_i_reserved1;
} linux1;
struct {
__le32 h_i_translator;
} hurd1;
struct {
__le32 m_i_reserved1;
} masix1;
} osd1; /* OS dependent 1 */
__le32 i_block[EXT2_N_BLOCKS];/* Pointers to blocks */
__le32 i_generation; /* File version (for NFS) */
__le32 i_file_acl; /* File ACL */
__le32 i_dir_acl; /* Directory ACL */
__le32 i_faddr; /* Fragment address */
union {
struct {
__u8 l_i_frag; /* Fragment number */
__u8 l_i_fsize; /* Fragment size */
__u16 i_pad1;
__le16 l_i_uid_high; /* these 2 fields */
__le16 l_i_gid_high; /* were reserved2[0] */
__u32 l_i_reserved2;
} linux2;
struct {
__u8 h_i_frag; /* Fragment number */
__u8 h_i_fsize; /* Fragment size */
__le16 h_i_mode_high;
__le16 h_i_uid_high;
__le16 h_i_gid_high;
__le32 h_i_author;
} hurd2;
struct {
__u8 m_i_frag; /* Fragment number */
__u8 m_i_fsize; /* Fragment size */
__u16 m_pad1;
__u32 m_i_reserved2[2];
} masix2;
} osd2; /* OS dependent 2 */
};
/*
* Constants relative to the data blocks
*/
#define EXT2_NDIR_BLOCKS 12
#define EXT2_IND_BLOCK EXT2_NDIR_BLOCKS
#define EXT2_DIND_BLOCK (EXT2_IND_BLOCK + 1)
#define EXT2_TIND_BLOCK (EXT2_DIND_BLOCK + 1)
#define EXT2_N_BLOCKS (EXT2_TIND_BLOCK + 1)
备注:EXT2_N_BLOCKS = 15
再次注意:
-
⽂件名并未纳⼊到 inode 数据结构内部。
-
inode的⼤⼩⼀般是128字节或者256字节。
-
任何⽂件的内容⼤⼩可以不同,但是属性⼤⼩⼀定是相同的。
三、ext2 ⽂件系统
1、宏观认识
我们想要在硬盘上存储⽂件,必须先把硬盘格式化为某种格式的⽂件系统,才能存储⽂件。⽂件系统的⽬的就是组织和管理硬盘中的⽂件。
在 Linux 系统中,最常⻅的是 ext2 系列的⽂件系统。其早期版本为 ext2,后来⼜发展出 ext3 和 ext4,ext3 和 ext4 虽然对 ext2 进⾏了增强,但是其核⼼设计并没有发⽣变化。
ext2⽂件系统将整个分区划分成若⼲个同样⼤⼩的块组(Block Group),如下图所示。只要能管理⼀个分区就能管理所有分区,也就能管理所有磁盘⽂件。

上图中启动块(Boot Block/Sector)的⼤⼩是确定的,为1KB,由PC标准规定,⽤来存储磁盘分区信息和启动信息,任何⽂件系统都不能修改启动块。启动块之后才是ext2⽂件系统的开始。
2、Block Group
ext2⽂件系统会根据分区的⼤⼩划分为数个Block Group。⽽每个Block Group都有着相同的结构组成。
3、块组内部构成
1)超级块(Super Block)
存放⽂件系统本⾝的结构信息,描述整个分区的⽂件系统信息。记录的信息主要有:bolck 和 inode的总量,未使⽤的 block 和 inode 的数量,⼀个 block 和 inode 的⼤⼩,最近⼀次挂载的时间,最近⼀次写⼊数据的时间,最近⼀次检验磁盘的时间等等⽂件系统的相关信息。如果Super Block的信息被破坏,可以说整个⽂件系统结构就被破坏了。
超级块在每个块组的开头都有⼀份拷⻉(第⼀个块组必须有,后⾯的块组可以没有)。为了保证⽂件系统在磁盘部分扇区出现物理问题的情况下还能正常⼯作,就必须保证⽂件系统的
super block信息在这种情况下也能正常访问。所以⼀个⽂件系统的super block会在多个block group中进⾏备份,这些super block区域的数据保持⼀致。
cpp
struct ext2_super_block {
__le32 s_inodes_count; /* Inodes count */
__le32 s_blocks_count; /* Blocks count */
__le32 s_r_blocks_count; /* Reserved blocks count */
__le32 s_free_blocks_count; /* Free blocks count */
__le32 s_free_inodes_count; /* Free inodes count */
__le32 s_first_data_block; /* First Data Block */
__le32 s_log_block_size; /* Block size */
__le32 s_log_frag_size; /* Fragment size */
__le32 s_blocks_per_group; /* # Blocks per group */
__le32 s_frags_per_group; /* # Fragments per group */
__le32 s_inodes_per_group; /* # Inodes per group */
__le32 s_mtime; /* Mount time */
__le32 s_wtime; /* Write time */
__le16 s_mnt_count; /* Mount count */
__le16 s_max_mnt_count; /* Maximal mount count */
__le16 s_magic; /* Magic signature */
__le16 s_state; /* File system state */
__le16 s_errors; /* Behaviour when detecting errors */
__le16 s_minor_rev_level; /* minor revision level */
__le32 s_lastcheck; /* time of last check */
__le32 s_checkinterval; /* max. time between checks */
__le32 s_creator_os; /* OS */
__le32 s_rev_level; /* Revision level */
__le16 s_def_resuid; /* Default uid for reserved blocks */
__le16 s_def_resgid; /* Default gid for reserved blocks */
/*
* These fields are for EXT2_DYNAMIC_REV superblocks only.
*
* Note: the difference between the compatible feature set and
* the incompatible feature set is that if there is a bit set
* in the incompatible feature set that the kernel doesn't
* know about, it should refuse to mount the filesystem.
*
* e2fsck's requirements are more strict; if it doesn't know
* about a feature in either the compatible or incompatible
* feature set, it must abort and not try to meddle with
* things it doesn't understand...
*/
__le32 s_first_ino; /* First non-reserved inode */
__le16 s_inode_size; /* size of inode structure */
__le16 s_block_group_nr; /* block group # of this superblock */
__le32 s_feature_compat; /* compatible feature set */
__le32 s_feature_incompat; /* incompatible feature set */
__le32 s_feature_ro_compat; /* readonly-compatible feature set */
__u8 s_uuid[16]; /* 128-bit uuid for volume */
char s_volume_name[16]; /* volume name */
char s_last_mounted[64]; /* directory where last mounted */
__le32 s_algorithm_usage_bitmap; /* For compression */
/*
* Performance hints. Directory preallocation should only
* happen if the EXT2_COMPAT_PREALLOC flag is on.
*/
__u8 s_prealloc_blocks; /* Nr of blocks to try to preallocate*/
__u8 s_prealloc_dir_blocks; /* Nr to preallocate for dirs */
__u16 s_padding1;
/*
* Journaling support valid if EXT3_FEATURE_COMPAT_HAS_JOURNAL set.
*/
__u8 s_journal_uuid[16]; /* uuid of journal superblock */
__u32 s_journal_inum; /* inode number of journal file */
__u32 s_journal_dev; /* device number of journal file */
__u32 s_last_orphan; /* start of list of inodes to delete */
__u32 s_hash_seed[4]; /* HTREE hash seed */
__u8 s_def_hash_version; /* Default hash version to use */
__u8 s_reserved_char_pad;
__u16 s_reserved_word_pad;
__le32 s_default_mount_opts;
__le32 s_first_meta_bg; /* First metablock block group */
__u32 s_reserved[190]; /* Padding to the end of the block */
};
2)GDT(Group Descriptor Table)
块组描述符表,描述块组属性信息,整个分区分成多少个块组就对应有多少个块组描述符。每个块组描述符存储⼀个块组的描述信息,如在这个块组中从哪⾥开始是inode Table,从哪⾥开始是Data Blocks,空闲的inode和数据块还有多少个等等。块组描述符在每个块组的开头都有⼀份拷⻉。
cpp
// 磁盘级blockgroup的数据结构
struct ext2_group_desc
{
__le32 bg_block_bitmap; /* Blocks bitmap block */
__le32 bg_inode_bitmap; /* Inodes bitmap */
__le32 bg_inode_table; /* Inodes table block*/
__le16 bg_free_blocks_count; /* Free blocks count */
__le16 bg_free_inodes_count; /* Free inodes count */
__le16 bg_used_dirs_count; /* Directories count */
__le16 bg_pad;
__le32 bg_reserved[3];
};
3)块位图(Block Bitmap)
- Block Bitmap 中记录着
Data Block中哪个数据块已经被占⽤,哪个数据块没有被占⽤
4)inode位图(Inode Bitmap)
- 每个bit位表示⼀个
inode是否空闲可⽤。
5)i节点表(Inode Table)
- 存放⽂件属性:如⽂件⼤⼩,所有者,最近修改时间等
- 当前分组所有
Inode属性的集合 inode跨组编号,不能跨分区
6)数据块(Data Block)
数据区:存放⽂件内容,也就是⼀个⼀个的块。根据不同的⽂件类型有以下⼏种情况:
-
对于普通⽂件,⽂件的数据存储在数据块中。
-
文件名不会作为属性,保存在文件的inode中,而是保存在当前文件所属目录的数据内容中。
-
对于⽬录,该⽬录下的所有⽂件名和⽬录名存储在所在⽬录的数据块中。
-
Block跨组编号,不能跨分区。所以在同一个分区内部,inode编号和块号都是唯一的。
4、inode 和 data block 映射
- inode内部存在
_ _le32 i_block[EXT2_N_BLOCKS]; EXT2_N_BLOCKS = 15,就是⽤来进⾏inode和block映射的。 - 这样⽂件 = 内容 + 属性,就都能找到了。

知道inode号的情况下,对⽂件进⾏增、删、查、改是在做什么?
-
只要知道⽂件的 inode号,就能在指定分区中确定是哪⼀个分组,进⽽在那⼀个分组确定是哪⼀个inode。
-
拿到inode后,⽂件的属性和内容就全部都有了
下⾯,通过touch⼀个新⽂件来看看如何⼯作。

为了说明问题,我们将上图简化:

创建⼀个新⽂件主要有以下4个操作:
1. 存储属性
内核先找到⼀个空闲的i节点(这⾥是1312034)。内核把⽂件信息记录到其中。
2. 存储数据该⽂件需要存储在三个磁盘块,内核找到了三个空闲块:300,500,800。将内核缓冲区的第⼀块数据复制到300,下⼀块复制到500,以此类推。
3. 记录分配情况⽂件内容按顺序300,500,800存放。内核在inode上的磁盘分布区记录了上述块列表。
4. 添加⽂件名到⽬录新的⽂件名abc。Linux内核将⼊⼝(1312034,abc)添加到⽬录⽂件。⽂件名和inode之间的对应关系将⽂件名和⽂件的内容及属性连接起来。
5、⽬录与⽂件名
⽬录是⽂件吗?
- ⽬录也是⽂件,但是磁盘上没有⽬录的概念,只有⽂件属性+⽂件内容的概念。
- ⽬录的属性不⽤多说,内容保存的是:⽂件名和Inode号的映射关系
示例:
cpp
#include<stdio.h>
#include<sys/types.h>
#include<dirent.h>
#include<unistd.h>
#include<string.h>
#include<stdlib.h>
int main(int argc, char* argv[])
{
if(argc != 2)
{
fprintf(stderr, "Usage: %s <directory>\n", argv[0]);
exit(EXIT_FAILURE);
}
DIR* dir = opendir(argv[1]);
if(!dir)
{
perror("opendir");
exit(EXIT_FAILURE);
}
struct dirent* entry;
while((entry = readdir(dir)) != NULL)
{
if(strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
{
continue;
}
printf("Filename: %s, Inode: %lu\n", entry->d_name, (unsigned long)entry->d_ino);
}
closedir(dir);
return 0;
}

我们访问⽂件,都是⽤的⽂件名,没⽤过inode号啊?
- 访问⽂件,必须打开当前⽬录,根据⽂件名,获得对应的inode号,然后进⾏⽂件访问

⽐如:要访问 test.c,就必须打开test_12_13(当前⼯作⽬录),然后才能获取 test.c 对应的 inode 进⽽对⽂件进⾏访问。
6、路径解析
我们访问任何文件都要从根⽬录( / )开始,依次打开每⼀个⽬录,根据⽬录名,依次访问每个⽬录下指定的⽬录,直到访问到指定文件。这个过程就叫做Linux路径解析。
所以这也就是为什么访问⽂件必须要有路径+文件名的原因。
路径是谁提供?
- 访问⽂件都是指令/⼯具访问,本质是进程访问,进程自身有CWD!所以是进程提供路径。
- open⽂件,也就等于提供了路径。
7、路径缓存
我们访问任何⽂件,每次都要从 / ⽬录开始进⾏路径解析的话,就相当于一直在做磁盘IO,导致效率太低。因此OS在进行路径解析的时候,会把我们历史访问的所有的目录(路径),形成一颗多叉树进行保存。
Linux中,在内核中维护树状目录结构的内核结构体叫做: struct dentry
cpp
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 */
};
注意:
-
每个⽂件其实都要有对应的dentry结构,包括普通⽂件。这样所有被打开的⽂件,就可以在内存中形成整个树形结构。
-
整个树形节点也同时会⾪属于LRU(Least Recently Used,最近最少使⽤)结构中,进⾏节点淘汰。
-
整个树形节点也同时会⾪属于Hash,⽅便快速查找。
-
更重要的是,这个树形结构,整体构成了Linux的路径缓存结构,打开访问任何⽂件,都在先在这棵树下根据路径进⾏查找,找到就返回属性inode和内容,没找到就从磁盘加载路径,添加dentry结构,缓存新路径
8、挂载分区
我们已经能够根据inode号在指定分区找⽂件了,也已经能根据⽬录⽂件内容,找指定的inode了,在指定的分区内,我们可以为所欲为了。
可是:我怎么知道我在哪⼀个分区?
实验:
powershell
dd if=/dev/zero of=./disk.img bs=1M count=5 #制作一个大的磁盘块,就当做一个分区
mkfs.ext4 disk.img # 格式化写入文件系统
sudo mount -t ext4 ./disk.img ./dir # 将分区挂载到指定的目录
sudo umount ./dir # 卸载分区

可以看到此时我们已经将分区挂载到目录dir,往目录里面创建文件,就相当于往分区里面创建:

注意:
/dev/loop0在Linux系统中代表第⼀个循环设备(loop device)。循环设备,也被称为回环设备或者loopback设备,是⼀种伪设备(pseudo-device),它允许将⽂件作为块设备(block device)来使⽤。这种机制使得可以将⽂件(⽐如ISO镜像⽂件)挂载(mount)为⽂件系统,就像它们是物理硬盘分区或者外部存储设备⼀样
结论:
- 分区写⼊⽂件系统,⽆法直接使⽤,需要和指定的⽬录关联,进⾏挂载才能使⽤。
- 所以,可以根据访问⽬标⽂件的"路径前缀"准确判断我在哪⼀个分区。
9、⽂件系统总结
- 下⾯⽤⼏张图总结


四、软硬链接
1、硬链接
我们看到真正找到磁盘上⽂件的并不是⽂件名,⽽是inode。其实在linux中可以让多个⽂件名对应于同⼀个inode。
举例:给已存在的文件 abc 新增一个硬链接 def。
powershell
ln abc def

- abc和def的链接状态完全相同,他们被称为指向⽂件的硬链接。内核记录了这个连接数,inode 1312068 的硬连接数为2。
- 二者共享同一份文件数据(inode 相同),可同步访问 / 修改,且删除任一不影响另一使用。
- 我们在删除⽂件时⼲了两件事情:1.在⽬录中将对应的记录删除,2.将硬连接数-1,如果为0,则将对应的磁盘释放。
2、软链接
硬链接是通过inode引⽤另外⼀个⽂件,软链接是通过名字引⽤另外⼀个⽂件,但实际上,新的⽂件和被引⽤的⽂件的inode不同,应⽤上可以想象成⼀个快捷⽅式。
举例:为文件 abc 创建一个软链接 abc.s
powershell
ln -s abc abc.s

- abc.s 作为指向 abc 的快捷方式(inode 不同),可间接访问 abc,但依赖原文件存在。
3、软硬链接对⽐
- 软链接是一个独⽴的⽂件,因为有独立的 inode号。
- 硬链接不是一个独立的文件,因为它没有独立的 inode号,本质上是一组新的⽂件名和⽬标 inode号的映射关系。
4、软硬链接⽤途
硬链接
.和..就是硬链接- 对文件进行备份
软连接
- 类似windows下的快捷⽅式