Linux:理解文件系统

1.理解硬件

1.1磁盘

  • 机械磁盘是计算机中的⼀个机械设备
  • 磁盘--- 外设
  • 容量⼤,价格便宜

1.2磁盘物理结构 

 

1.3磁盘的存储结构 

扇区:是磁盘存储数据的基本单位,512字节,块设备 

 

如何定位⼀个扇区呢?
确定磁头要访问哪⼀个柱⾯(磁道)(cylinder)
可以先定位磁头(header)
定位⼀个扇区(sector)
柱⾯(cylinder),磁头(head),扇区(sector),显然可以定位数据了,这就是数据定位(寻址)⽅式之⼀,CHS寻址⽅式。
扇区是从磁盘读出和写⼊信息的最⼩单位,通常⼤⼩为 512 字节。
磁头(head)数:每个盘⽚⼀般有上下两⾯,分别对应1个磁头,共2个磁头
磁道(track)数:磁道是从盘⽚外圈往内圈编号0磁道,1磁道...,靠近主轴的同⼼圆⽤于停靠磁头,不存储数据
柱⾯(cylinder)数:磁道构成柱⾯,数量上等同于磁道个数
扇区(sector)数:每个磁道都被切分成很多扇形区域,每道的扇区数量相同
圆盘(platter)数:就是盘⽚的数量
磁盘容量=磁头数 × 磁道(柱⾯)数 × 每道扇区数 × 每扇区字节数
细节:传动臂上的磁头是共进退的

 1.4磁盘的逻辑结构

细节:传动臂上的磁头是共进退的

 

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

 

所以,磁盘的真实情况是:
磁道:
某⼀盘⾯的某⼀个磁道展开:
即:⼀维数组
柱面:
整个磁盘所有盘⾯的同⼀个磁道,即柱⾯展开:
 
柱⾯上的每个磁道,扇区个数是⼀样的
这不就是⼆维数组吗

整盘: 

整个磁盘不就是多张⼆维的扇区数组表(三维数组?)
所以,寻址⼀个扇区:先找到哪⼀个柱⾯(Cylinder) ,在确定柱⾯内哪⼀个磁道(其实就是磁头位置,Head),在确定扇区(Sector),所以就有了CHS。
我们之前学过C/C++的数组,在我们看来,其实全部都是⼀维数组:

 

所以,每⼀个扇区都有⼀个下标,我们叫做LBA(Logical Block Address)地址,其实就是线性地址。所以怎么计算得到这个LBA地址呢?

 

LBA地址转成CHS地址,CHS如何转换成为LBA地址。 

OS只需要使⽤LBA就可以了!!LBA地址转成CHS地址,CHS如何转换成为LBA地址。谁做?磁盘⾃⼰来做。固件(硬件电路,伺服系统)

1.5 CHS && LBA地址 

CHS转成LBA:
  • 磁头数*每磁道扇区数 = 单个柱⾯的扇区总数
  • LBA = 柱⾯号C*单个柱⾯的扇区总数 + 磁头号H*每磁道扇区数 + 扇区号S - 1
  • 即:LBA = 柱⾯号C*(磁头数*每磁道扇区数) + 磁头号H*每磁道扇区数 + 扇区号S - 1
  • 扇区号通常是从1开始的,⽽在LBA中,地址是从0开始的
  • 柱⾯和磁道都是从0开始编号的
  • 总柱⾯,磁道个数,扇区总数等信息,在磁盘内部会⾃动维护,上层开机的时候,会获取到这些参数。
LBA转成CHS:
  • 柱⾯号C = LBA // (磁头数*每磁道扇区数)【就是单个柱⾯的扇区总数】
  • 磁头号H = (LBA % (磁头数*每磁道扇区数)) // 每磁道扇区数
  • 扇区号S = (LBA % 每磁道扇区数) + 1
  • "//": 表⽰除取整
所以:从此往后,在磁盘使⽤者看来,根本就不关⼼CHS地址,⽽是直接使⽤LBA地址,磁盘内部⾃⼰转换。所以:
从现在开始,磁盘就是⼀个 元素为扇区 的⼀维数组,数组的下标就是每⼀个扇区的LBA地址。OS使⽤磁盘,就可以⽤⼀个数字访问磁盘扇区了。

 2.引入文件系统

2.1引⼊"块"概念 

其实硬盘是典型的“块”设备,操作系统读取硬盘数据的时候,其实是不会⼀个个扇区地读取,这样
效率太低,⽽是⼀次性连续读取多个扇区,即⼀次性读取⼀个”块”(block)。
硬盘的每个分区是被划分为⼀个个的”块”。⼀个”块”的⼤⼩是由格式化的时候确定的,并且不可
以更改,最常⻅的是4KB,即连续⼋个扇区组成⼀个 ”块”。”块”是⽂件存取的最⼩单位。
注意:
  • 磁盘就是⼀个三维数组,我们把它看待成为⼀个"⼀维数组",数组下标就是LBA,每个元素都是扇
  • 每个扇区都有LBA,那么8个扇区⼀个块,每⼀个块的地址我们也能算出来。
  • 知道LBA:块号 = LBA/8
  • 知道块号:LBA=块号*8 + n. (n是块内第⼏个扇区)

 

2.2引⼊"分区"概念 

其实磁盘是可以被分成多个分区(partition)的,以Windows观点来看,你可能会有⼀块磁盘并且将它分区成C,D,E盘。那个C,D,E就是分区。分区从实质上说就是对硬盘的⼀种格式化。但是Linux的设备都是以⽂件形式存在,那是怎么分区的呢?

 

柱⾯是分区的最小单位,我们可以利⽤参考柱⾯号码的⽅式来进⾏分区,其本质就是设置每个区的起始柱⾯和结束柱⾯号码。 此时我们可以将硬盘上的柱⾯(分区)进⾏平铺,将其想象成⼀个⼤的平⾯,如下图所⽰:

 

2.3引⼊"inode"概念

之前我们说过 ⽂件 = 数据 + 属性 ,我们使⽤ ls -l 的时候看到的除了看到⽂件名,还能看到⽂件元
数据(属性)。

 

[root@localhost linux]# ls -l
总⽤量 12
-rwxr-xr-x. 1 root root 7438 "9⽉ 13 14:56" a.out
-rw-r--r--. 1 root root 654 "9⽉ 13 14:56" test.c
每⾏包含7列:
模式
硬链接数
⽂件所有者
⼤⼩
最后修改时间
⽂件名
ls -l读取存储在磁盘上的⽂件信息,然后显⽰出来

其实这个信息除了通过这种⽅式来读取,还有⼀个stat命令能够看到更多信息 

[root@localhost linux]# stat test.c
File: "test.c"
Size: 654 Blocks: 8 IO Block: 4096 普通⽂件
Device: 802h/2050d Inode: 263715 Links: 1
Access: (0644/-rw-r--r--) Uid: ( 0/ root) Gid: ( 0/ root)
Access: 2017-09-13 14:56:57.059012947 +0800
Modify: 2017-09-13 14:56:40.067012944 +0800
Change: 2017-09-13 14:56:40.069012948 +0800

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

ls -li命令

 

每⼀个⽂件都有对应的inode,⾥⾯包含了与该⽂件有关的⼀些信息。为了能解释清楚inode,我们需要是深⼊了解⼀下⽂件系统。
注意:
Linux下⽂件的存储是属性和内容分离存储的
Linux下,保存⽂件属性的集合叫做inode,⼀个⽂件,⼀个inode,inode内有⼀个唯⼀的标识符,叫做inode号

 所以⼀个⽂件的属性inode⻓什么样⼦呢?

/*
* Structure of an inode on the disk
*/
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,我们后⾯统⼀128字节
任何⽂件的内容⼤⼩可以不同,但是属性⼤⼩⼀定是相同的

两个问题:

1. 我们已经知道硬盘是典型的“块”设备,操作系统读取硬盘数据的时候,读取的基本单位
是”块”。“块”⼜是硬盘的每个分区下的结构,难道“块”是随意的在分区上排布的吗?那要怎
么找到“块”呢?
2. 还有就是上⾯提到的存储⽂件属性的inode,⼜是如何放置的呢?
⽂件系统就是为了组织管理这些的!!

3.ext2 文件系统

3.1宏观认识  

我们想要在硬盘上储⽂件,必须先把硬盘格式化为某种格式的⽂件系统,才能存储⽂件。⽂件系统的⽬的就是组织和管理硬盘中的⽂件。在Linux 系统中,最常⻅的是 ext2 系列的⽂件系统。其早期版本为 ext2,后来⼜发展出 ext3 和 ext4。ext3 和 ext4 虽然对 ext2 进⾏了增强,但是其核⼼设计并没有发⽣变化,我们仍是以较⽼的 ext2 作为演⽰对象。
ext2⽂件系统将整个分区划分成若⼲个同样⼤⼩的块组 (Block Group),如下图所⽰。只要能管理⼀个分区就能管理所有分区,也就能管理所有磁盘⽂件。

 

 

上图中启动块(Boot Block/Sector)的⼤⼩是确定的,为1KB,由PC标准规定,⽤来存储磁盘分区信息和启动信息,任何⽂件系统都不能修改启动块。启动块之后才是ext2⽂件系统的开始。

3.2Block Group

 ext2⽂件系统会根据分区的⼤⼩划分为数个Block Group。⽽每个Block Group都有着相同的结构组成。

3.3块组内部构成

3.3.1超级块(Super Block) 

存放⽂件系统本⾝的结构信息,描述整个分区的⽂件系统信息。记录的信息主要有:bolck 和 inode的总量,未使⽤的block和inode的数量,⼀个block和inode的⼤⼩,最近⼀次挂载的时间,最近⼀次写⼊数据的时间,最近⼀次检验磁盘的时间等其他⽂件系统的相关信息。Super Block的信息被破坏,可以说整个⽂件系统结构就被破坏了
( 超级块在每个块组的开头都有⼀份拷⻉(第⼀个块组必须有,后⾯的块组可以没有)。 为了保证⽂件系统在磁盘部分扇区出现物理问题的情况下还能正常⼯作,就必须保证⽂件系统的super block信息在这种情况下也能正常访问。所以⼀个⽂件系统的super block会在多个block group中进⾏备份,这些super block区域的数据保持⼀致。 )
/*
* Structure of the super block
*/
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 */
};

3.3.2GDT(Group Descriptor Table)

块组描述符表,描述块组属性信息,整个分区分成多个块组就对应有多少个块组描述符。每个块组描述符存储⼀个块组 的描述信息,如在这个块组中从哪⾥开始是inode Table,从哪⾥开始是Data Blocks,空闲的inode和数据块还有多少个等等。块组描述符在每个块组的开头都有⼀份拷⻉。
// 磁盘级blockgroup的数据结构
/*
* Structure of a blocks group descriptor
*/
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.3.3块位图(Block Bitmap)

  • ​作用​​:标记​​数据块(Data Block)​​的使用状态。
  • 原理​​:
    • 每个比特(bit)对应一个数据块:
      • 1:块已被占用(存储了文件数据)。
      • 0:块空闲可用。
    • 例如:块位图 11100010 表示前3个块已用,第4-6个块空闲,第7个块已用,第8个块空闲。
  • ​示例​​:
    • 若文件系统有 1024 个块,块位图需占用 1024/8 = 128 字节(1字节=8比特)。

3.3.4inode位图(Inode Bitmap)

  • ​作用​​:标记​​Inode 的使用状态​​。
  • ​原理​​:
    • 每个比特对应一个 Inode:
      • 1:Inode 已被占用(关联了文件/目录)。
      • 0:Inode 空闲。
    • 例如:Inode 位图 10100000 表示第1、3个 Inode 已用,其余空闲。
  • ​示例​​:
    • 若块组包含 8192 个 Inode,Inode 位图需占用 8192/8 = 1024 字节
特性块位图(Block Bitmap)Inode 位图(Inode Bitmap)
​管理对象​数据块(Data Blocks)Inode
​标记内容​数据块是否被文件占用Inode 是否关联文件/目录
​大小计算​总块数 ÷ 8(字节)总Inode数 ÷ 8(字节)
​分配策略​优先分配连续空闲块分配第一个空闲 Inode

3.3.5i节点表(Inode Table)

  • 唯一标识文件​​:每个区对应一个唯一的 Inode 编号(如 Inode 1053)。
  • ​存储元数据​​:包括权限、所有者、大小、时间戳等。
  • ​数据块寻址​​:通过指针(直接/间接)定位文件内容所在的磁盘块。
  • 当前分组所有Inode属性的集合
  • inode编号以分区为单位,整体划分,不可跨分区
属性说明
​物理位置​位于文件系统的 ​​块组(Block Group)​​ 内,紧邻 Inode Bitmap。
​存储单元​多个 Inode 连续存储,占用一个或多个磁盘块(Block)。
​大小固定​EXT2/3 中每个 Inode 固定为 ​​128字节​​,EXT4 可扩展(如 256字节)。

示例计算​​:

若磁盘块大小为 4KB,一个块可存储 4096/128 = 32 个 Inode。

3.3.6Data Block 

数据区:存放⽂件内容,也就是⼀个⼀个的Block。根据不同的⽂件类型有以下⼏种情况:
  • 对于普通⽂件,⽂件的数据存储在数据块中。
  • 对于⽬录,该⽬录下的所有⽂件名和⽬录名存储在所在⽬录的数据块中,除了⽂件名外,ls -l命令看到的其它信息保存在该⽂件的inode中。
  • Block 号按照分区划分,不可跨分区
属性说明
​作用​存储文件内容或目录条目(文件名 + Inode编号映射)。
​大小​通常为 ​​1KB、2KB、4KB​​(EXT4默认4KB),格式化时确定,不可动态调整。
​分配单位​文件系统以块为单位分配空间,即使文件只有1字节也占用整个块。
​寻址方式​通过Inode的​​直接/间接指针​​定位数据块。

Data Block 太大可以跨组保存 

 3.4inode和datablock映射

inode内部存在 __le32 i_block[EXT2_N_BLOCKS];/* Pointers to blocks */ , EXT2_N_BLOCKS =15,就是⽤来进⾏inode和block映射的
这样⽂件=内容+属性,就都能找到了。

 

索引级别​​管理能力​​(假设块大小=4KB,指针4字节)​图中对应部分​
​直接块​12×4KB = ​​48KB​图中左侧"12个直接块"区域
​一级间接​1024指针×4KB = ​​4MB​"一级间接块索引表指针"指向的表格
​二级间接​1024×1024×4KB = ​​4GB​二级索引表的多层结构
​三级间接​1024³×4KB = ​​4TB​图中最右侧的三级指针链

 

知道inode号的情况下,在指定分区,请解释:对⽂件进⾏增、删、查、改是在做什么?
  • 分区之后的格式化操作,就是对分区进⾏分组,在每个分组中写⼊SB、GDT、Block Bitmap、Inode Bitmap等管理信息,这些管理信息统称: ⽂件系统
  • 只要知道⽂件的inode号,就能在指定分区中确定是哪⼀个分组,进⽽在哪⼀个分组确定 是哪⼀个inode
  • 拿到inode⽂件属性和内容就全部都有了

 通过touch⼀个新⽂件来看看如何⼯作。

[root@localhost linux]# touch abc
[root@localhost linux]# ls -i abc
263466 abc

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

 

创建⼀个新⽂件主要有以下4个操作:
1.存储属性
内核先找到⼀个空闲的i节点(这⾥是263466)。内核把⽂件信息记录到其中。
2.存储数据
该⽂件需要存储在三个磁盘块,内核找到了三个空闲块:300,500,800。将内核缓冲区的第⼀块数据复制到300,下⼀块复制到500,以此类推。
3.记录分配情况
⽂件内容按顺序300,500,800存放。内核在inode上的磁盘分布区记录了上述块列表。

4.添加⽂件名到⽬录

新的⽂件名abc。linux如何在当前的⽬录中记录这个⽂件?内核将⼊⼝(263466,abc)添加到⽬录⽂件。⽂件名和inode之间的对应关系将⽂件名和⽂件的内容及属性连接起来。
abc → 263466 → 块300/500/800
操作步骤图片中的标注证据
​1. 分配Inode​i节点表中的263466条目
​2. 分配数据块​数据区的块300, 500, 800
​3. 记录块列表​文件所使用的数据块列表:300,500,800
​4. 添加目录项​增加到目录的入口:(263466, abc)

 3.5⽬录与⽂件名 

问题:
我们访问⽂件,都是⽤的⽂件名,没⽤过inode号啊?
⽬录是⽂件吗?如何理解?
答案:
⽬录也是⽂件,但是磁盘上没有⽬录的概念,只有⽂件属性+⽂件内容的概念。
⽬录的属性不⽤多说,内容保存的是:⽂件名和Inode号的映射关系

 

所以,访问⽂件,必须打开当前⽬录,根据⽂件名,获得对应的inode号,然后进⾏⽂件访问
所以,访问⽂件必须要知道当前⼯作⽬录,本质是必须能打开当前⼯作⽬录⽂件,查看⽬录⽂件的
内容!

 

local@bite:~/code/test/test$ pwd
/home/local/code/test/test
local@bite:~/code/test/test$ ls -li
total 24
1596260 -rw-rw-r-- 1 local local 814 Oct 28 20:32 test.c
⽐如:要访问test.c, 就必须打开test(当前⼯作⽬录),然后才能获取test.c对应的inode进⽽对⽂
件进⾏访问。

3.6路径解析

问题:打开当前⼯作⽬录⽂件,查看当前⼯作⽬录⽂件的内容?当前⼯作⽬录不也是⽂件吗?我们访问当前⼯作⽬录不也是只知道当前⼯作⽬录的⽂件名吗?要访问它,不也得知道当前⼯作⽬录的inode吗?
答案1:所以也要打开:当前⼯作⽬录的上级⽬录,上级⽬录不也是⽬录吗??不还是上⾯的问
题吗?
答案2:所以类似"递归",需要把路径中所有的⽬录全部解析,出⼝是"/"根⽬录。
最终答案3:⽽实际上,任何⽂件,都有路径,访问⽬标⽂件,⽐如:

 /home/local/code/test/test/test.c

都要从根⽬录开始,依次打开每⼀个⽬录,根据⽬录名,依次访问每个⽬录下指定的⽬录,直到访问到test.c。这个过程叫做Linux路径解析。

所以,我们知道了:访问⽂件必须要有⽬录+⽂件名=路径的原因
根⽬录固定⽂件名,inode号,⽆需查找,系统开机之后就必须知道
可是路径谁提供?
你访问⽂件,都是指令/⼯具访问,本质是进程访问,进程有CWD!进程提供路径。
你open⽂件,提供了路径
可是最开始的路径从哪⾥来?
所以Linux为什么要有根⽬录, 根⽬录下为什么要有那么多缺省⽬录?
你为什么要有家⽬录,你⾃⼰可以新建⽬录?
上⾯所有⾏为:本质就是在磁盘⽂件系统中,新建⽬录⽂件。⽽你新建的任何⽂件,都在你或者系统指定的⽬录下新建,这不就是天然就有路径了嘛!
系统+⽤⼾共同构建Linux路径结构.

3.7路径缓存

问题1:Linux磁盘中,存在真正的⽬录吗?
答案:不存在,只有⽂件。只保存⽂件属性+⽂件内容
问题2:访问任何⽂件,都要从/⽬录开始进⾏路径解析?
答案:原则上是,但是这样太慢,所以Linux会缓存历史路径结构
问题3:Linux⽬录的概念,怎么产⽣的?
答案:打开的⽂件是⽬录的话,由OS⾃⼰在内存中进⾏路径维护

 

Linux中,在内核中维护树状路径结构的内核结构体叫做: struct dentry 

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_PROFILINGstruct dcookie_struct* d_cookie; /* cookie, if any */
#endifint d_mounted;unsigned char d_iname[DNAME_INLINE_LEN_MIN]; /* small names */
};

 dentry找到了,inode就找到了 

注意:
每个⽂件其实都要有对应的dentry结构,包括普通⽂件。这样所有被打开的⽂件,就可以在内存中形成整个树形结构
整个树形节点也同时会⾪属于LRU(Least Recently Used,最近最少使⽤)结构中,进⾏节点淘汰 整个树形节点也同时会⾪属于Hash,⽅便快速查找
更重要的是,这个树形结构,整体构成了Linux的路径缓存结构,打开访问任何⽂件,都在先在这
棵树下根据路径进⾏查找,找到就返回属性inode和内容,没找到就从磁盘加载路径,添加dentry
结构,缓存新路径

操作​​无缓存​​有缓存​
​首次访问 /path/abc需读取图中所有目录块和Inode表。仅第一次需访问磁盘。
​重复访问​每次重复解析路径。直接从缓存返回Inode,零磁盘I/O。
​删除/重命名文件​需同步更新缓存和图中磁盘结构。缓存失效,触发重新解析。

3.8挂载分区

我们已经能够根据inode号在指定分区找⽂件了,也已经能根据⽬录⽂件内容,找指定的inode了,在指定的分区内,我们可以为所欲为了。可是:

问题:inode不是不能跨分区吗?Linux不是可以有多个分区吗?我怎么知道我在哪⼀个分区???

 3.8.1⼀个实验

$ dd if=/dev/zero of=./disk.img bs=1M count=5 #制作⼀个⼤的磁盘块,就当做⼀个分区#创建5MB的空白镜像#通过dd命令创建的disk.img就是典型的​​磁盘镜像文件​​
$ mkfs.ext4 disk.img # 格式化写⼊⽂件系统
$ mkdir /mnt/mydisk # 建⽴空⽬录
$ df -h # 查看可以使⽤的分区
Filesystem Size Used Avail Use% Mounted on
udev 956M 0 956M 0% /dev
tmpfs 198M 724K 197M 1% /run
/dev/vda1 50G 20G 28G 42% /
tmpfs 986M 0 986M 0% /dev/shm
tmpfs 5.0M 0 5.0M 0% /run/lock
tmpfs 986M 0 986M 0% /sys/fs/cgroup
tmpfs 198M 0 198M 0% /run/user/0
tmpfs 198M 0 198M 0% /run/user/1002
$ sudo mount -t ext4 ./disk.img /mnt/mydisk/ # 将分区挂载到指定的⽬录$ df -h
Filesystem Size Used Avail Use% Mounted on
udev 956M 0 956M 0% /dev
tmpfs 198M 724K 197M 1% /run
/dev/vda1 50G 20G 28G 42% /
tmpfs 986M 0 986M 0% /dev/shm
tmpfs 5.0M 0 5.0M 0% /run/lock
tmpfs 986M 0 986M 0% /sys/fs/cgroup
tmpfs 198M 0 198M 0% /run/user/0
tmpfs 198M 0 198M 0% /run/user/1002
/dev/loop0 4.9M 24K 4.5M 1% /mnt/mydisk$ sudo umount /mnt/mydisk # 卸载分区
local@bite:/mnt$ df -h
Filesystem Size Used Avail Use% Mounted on
udev 956M 0 956M 0% /dev
tmpfs 198M 724K 197M 1% /run
/dev/vda1 50G 20G 28G 42% /
tmpfs 986M 0 986M 0% /dev/shm
tmpfs 5.0M 0 5.0M 0% /run/lock
tmpfs 986M 0 986M 0% /sys/fs/cgroup
tmpfs 198M 0 198M 0% /run/user/0
tmpfs 198M 0 198M 0% /run/user/1002

 注意:

/dev/loop0 在Linux系统中代表第⼀个循环设备(loop device)。循环设备,也被称为
回环设备或者loopback设备,是⼀种伪设备(pseudo-device),它允许将⽂件作为块设备
(block device)来使⽤。这种机制使得可以将⽂件(⽐如ISO镜像⽂件)挂载(mount)为
⽂件系统,就像它们是物理硬盘分区或者外部存储设备⼀样
最多可以同时挂载8个不同的磁盘镜像文件​​(每个镜像需要独占一个loop设备)
local@bite:/mnt$ ls /dev/loop* -l
brw-rw---- 1 root disk 7, 0 Oct 17 18:24 /dev/loop0
brw-rw---- 1 root disk 7, 1 Jul 17 10:26 /dev/loop1
brw-rw---- 1 root disk 7, 2 Jul 17 10:26 /dev/loop2
brw-rw---- 1 root disk 7, 3 Jul 17 10:26 /dev/loop3
brw-rw---- 1 root disk 7, 4 Jul 17 10:26 /dev/loop4
brw-rw---- 1 root disk 7, 5 Jul 17 10:26 /dev/loop5
brw-rw---- 1 root disk 7, 6 Jul 17 10:26 /dev/loop6
brw-rw---- 1 root disk 7, 7 Jul 17 10:26 /dev/loop7
crw-rw---- 1 root disk 10, 237 Jul 17 10:26 /dev/loop-control

 df 命令(最直接)快速判断当前所在分区

. 表示当前目录​​,输出显示其所在设备(如 /dev/vda1)和挂载点(/home)。 

$ df -h .
Filesystem      Size  Used Avail Use% Mounted on
/dev/vda1        50G   20G   28G  42% /home

 

3.8.2一个结论

分区写⼊⽂件系统,⽆法直接使⽤,需要和指定的⽬录关联,进⾏挂载才能使⽤。
所以,可以根据访问⽬标⽂件的"路径前缀"准确判断我在哪⼀个分区。

 4.软硬链接

4.1硬链接​

​基本概念​

  • ​硬链接​​ 是文件系统中​​指向同一个 inode 的多个文件名​​,即多个路径指向同一份数据。
  • 所有硬链接​​地位平等​​,没有“原始文件”和“链接文件”之分。
  • 删除任意一个硬链接,只要还有其他链接存在,文件数据就不会被释放。

特点 

 ​1.共享 inode​

  • 所有硬链接指向同一个 inode,因此:
    • 修改任一硬链接的内容,其他硬链接也会同步变化。
    • 它们的文件属性(权限、所有者、时间戳等)完全相同。

2.不跨文件系统​

  • 硬链接必须在​​同一个文件系统​​内创建(因为 inode 是文件系统局部的)。

3.不能链接目录​​(超级用户除外)

  • 普通用户无法用 ln 创建目录的硬链接,防止形成循环引用(但内核允许 . 和 .. 这样的特殊硬链接)。

4.​​删除不影响数据​

  • 只有当​​所有硬链接都被删除​​,且没有进程占用文件时,数据才会真正释放。

 创建硬链接​

ln 源文件 硬链接名
ln file.txt hardlink_to_file.txt

此时 file.txt 和 hardlink_to_file.txt 指向同一个 inode,修改其中一个会影响另一个。

查看硬链接​ 

ls -i​ 查看 inode 号(相同 inode 表示是硬链接): 

ls -i file.txt hardlink_to_file.txt
//输出示例:
123456 file.txt
123456 hardlink_to_file.txt  # inode 相同

 

263563 - rw - r--r--. 2 root root 0 9⽉ 15 17:45 abc
263563 - rw - r--r--. 2 root root 0 9⽉ 15 17:45 abc-hard//2是多个新的文件名指向目标文件,硬连接数//增加inode引用计数的数字  

 创建目录后发现是2,是因为目录里.也是硬链接

目录里..是3是因为上级目录和上级目录里的.和当前目录里的..是同一inode

 4.2软链接

基本概念​

软链接(Symbolic Link,又称符号链接)是 Linux/Unix 系统中的一种特殊文件类型,它​​存储的是另一个文件或目录的路径​​,类似于 Windows 的“快捷方式”。

创建软链接​ 

ln -s 目标文件或目录 软链接名称
ln -s /var/log/syslog my_log_link  # 创建指向系统日志的软链接

 此时 my_log_link 是一个独立文件,内容为路径 /var/log/syslog

识别软链接​ 

ls -l​ 显示软链接及其指向的目标: 

ls -l my_log_link
//输出示例:
lrwxrwxrwx 1 user group 13 Jan 1 12:00 my_log_link -> /var/log/syslog
//首字母 l 表示软链接。
//-> 后是目标路径。

 

gcc myapp.c -o /.../myapp  # 编译并输出到目标路径
ln -s /.../myapp exe  # 创建软链接 exe

现在目录下有一个名为 exe 的软链接,指向 /.../myapp

直接运行 exe

./exe ​

  1. Shell 会先解析 ./exe 的路径:
    •  ./ 表示当前目录,所以 Shell 查找当前目录下的 exe
  2. 发现 exe 是一个软链接,指向 /.../myapp
  3. Shell 最终执行 /.../myapp

4.3软硬连接对⽐

软连接是独⽴⽂件
硬链接只是⽂件名和⽬标⽂件inode的映射关系

 

4.4软硬连接的⽤途: 

硬链接
. .. 就是硬链接
⽂件备份
软连接
类似快捷⽅式

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/83167.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

用 openssl 测试 tls 连接

以 baidu 为例,命令行为: openssl s_client -tlsextdebug -connect baidu.com:443 得到的输出为: CONNECTED(00000003) TLS server extension "renegotiation info" (id65281), len1 0000 - 00 …

今日行情明日机会——20250515

上证指数缩量收阴线,个股跌多涨少,上涨波段4月9日以来已有24个交易日,时间周期上处于上涨末端,注意风险。 深证指数缩量收阴线,日线上涨结束的概率在增大,注意风险。 2025年5月15日涨停股主要行业方向分…

Axure RP9:列表新增

文章目录 列表新增思路新增按钮操作说明保存新增交互设置列表新增 思路 利用中继器新增行实现列表新增功能 新增按钮操作说明 工具栏中添加新增图标及标签,在图标标签基础上添加热区;对热区添加鼠标单击时交互事件,同步插入如下动作:显示/隐藏动作,设置目标元件为新增窗…

ArcGIS Pro调用多期历史影像

一、访问World Imagery Wayback,基本在我国范围 如下图: 二、 放大到您感兴趣的区域 三、 查看影像版本信息 点击第二步的按钮后,便可跳转至World Imagery (Wayback 2025-04-24)的相关信息。 四 、点击上图影像版本信息,页面跳转…

提高成功率!课题中的立项依据深度写作

1. 战略定位:在宏观愿景中界定课题坐标 立项依据的第一重任务,是回答“为什么要做”——但这一问并非局限于学术好奇,而要升维到国家战略、行业痛点与学科前沿的交汇点。教师在申报时,应先扫描上位政策(如国家中长期科…

【FileZilla】Client端的线程模型 (一)

CMainFrame构造---》CFileZillaEngineContex构造--》引起其成员变量lmpl构造--》引起fz::event_loop的构造,其中创建了两个线程(指针) task_和 timer_task_。 // In event_loop.cpp event_loop::event_loop(thread_pool & pool): sync_(false) {task_ std::ma…

什么是Agentic AI(代理型人工智能)?

什么是Agentic AI(代理型人工智能)? 一、概述 Agentic AI(代理型人工智能)是一类具备自主决策、目标导向性与持续行动能力的人工智能系统。与传统AI系统依赖外部输入和显式命令不同,Agentic AI在设定目标…

Windows平台OpenManus部署及WebUI远程访问实现

前言:继DeepSeek引发行业震动后,Monica.im团队最新推出的Manus AI 产品正席卷科技圈。这款具备自主思维能力的全能型AI代理,不仅能精准解析复杂指令并直接产出成果,更颠覆了传统人机交互模式。尽管目前仍处于封闭测试阶段&#xf…

Springboot3自定义starter笔记

场景&#xff1a;抽取聊天机器人场景&#xff0c;它可以打招呼。 效果&#xff1a;任何项目导入此 starter 都具有打招呼功能&#xff0c;并且问候语中的人名需要可以在配置文件中修改。 创建自定义 starter 项目&#xff0c;引入 spring-boot-starter 基础依赖。 <dependen…

Nginx与Tomcat负载均衡集群配置指南

目录 一、资源清单 二、基础环境 三、安装配置Tomcat 四、安装配置Nginx 一、资源清单 主机 操作系统 IP地址 tomcat1 OpenEuler24.03 192.168.16.142 tomcat2 OpenEuler24.03 192.168.16.143 Nginx OpenEuler24.03 192.168.16.144 二、基础环境 hostnamectl …

【数据处理】xarray 数据处理教程:从入门到精通

目录 xarray 数据处理教程&#xff1a;从入门到精通一、简介**核心优势** 二、安装与导入1. 安装2. 导入库 三、数据结构&#xff08;一&#xff09;DataArray&#xff08;二&#xff09; Dataset&#xff08;三&#xff09;关键说明 四、数据操作&#xff08;一&#xff09;索…

网站漏洞存在哪些危害,该如何做好预防?

网站漏洞的危害及预防措施 一、网站漏洞的主要危害 网站漏洞是攻击者利用系统、应用或配置中的缺陷&#xff0c;实施恶意行为的入口。其危害包括但不限于以下方面&#xff1a; 数据泄露与隐私侵犯 危害&#xff1a;攻击者通过SQL注入、XSS等漏洞窃取用户数据&#xff08;如密…

WEB攻防-PHP漏洞解析

Web攻防之PHP漏洞解析 目录结构 引言 1.1 PHP在CTF Web方向的核心地位1.2 报告目标与结构说明1.3 PHP安全研究的方法论代码执行漏洞 2.1 漏洞原理与历史演进2.2 危险函数全解析与利用链2.3 绕过过滤的20种高级技巧2.4 实战案例&#xff1a;从CVE到CTF赛题复现2.5 防御方案与安…

YOLO11解决方案之距离计算探索

概述 Ultralytics提供了一系列的解决方案&#xff0c;利用YOLO11解决现实世界的问题&#xff0c;包括物体计数、模糊处理、热力图、安防系统、速度估计、物体追踪等多个方面的应用。 测量两个物体之间的间距被称为特定空间内的距离计算&#xff0c;YOLO11使用两个边界框的中心…

mongodb处理时区转换问题

1. 程序查询直接使用&#xff08;java&#xff09;Date即可, 因为直接支持 2. 若方便查看日期需要进行格式和时区转换 db.task.aggregate([{ $match: {userId: 113633}},{ $project: {userId: 1,endTime: 1,formattedDate: {$dateToString: {format: "%Y-%m-%d %H:%M:%S&…

基于中心点预测的视觉评估与可视化流程

基于中心点预测的视觉评估与可视化流程 基于中心点预测的视觉评估与可视化流程一、脚本功能概览二、可视化与评分机制详解1. 真实框解析2. 调用模型处理帧3. 预测中心点与真实值的对比4. 打分策略5. 图像可视化三、目录结构要求四、运行方式五、应用场景与拓展思路六、总结七,…

Comparator 比较器

在Java中&#xff0c;Comparator.comparingInt(String::length) 是一个用于创建比较器&#xff08;Comparator&#xff09;的静态方法调用&#xff0c;它的核心作用是定义一个比较规则&#xff0c;使对象按照特定属性&#xff08;这里是字符串长度&#xff09;进行比较。下面从…

无人设备遥控器之无线通讯技术篇

无人设备遥控器的无线通讯技术是确保遥控操作准确、稳定、高效进行的关键。以下是对无人设备遥控器无线通讯技术的详细解析&#xff1a; 一、主要无线通讯技术类型 Wi-Fi通讯技术 原理&#xff1a;基于IEEE 802.11标准&#xff0c;通过无线接入点&#xff08;AP&#xff09;…

Android Coli 3 ImageView load two suit Bitmap thumb and formal,Kotlin(七)

Android Coli 3 ImageView load two suit Bitmap thumb and formal&#xff0c;Kotlin&#xff08;七&#xff09; 在 Android Coli 3 ImageView load two suit Bitmap thumb and formal&#xff0c;Kotlin&#xff08;六&#xff09;-CSDN博客 的基础上改进&#xff0c;主要是…

ET EntityRef EntityWeakRef 类分析

EntityRef EntityWeakRef 相同点 也是这两个的作用&#xff1a;这两个都是用来表示一个实体引用。一般来说使用一个对象&#xff0c;直接持有对象就可以&#xff0c;但是如果对象来自于对象池&#xff0c;这个时候直接持有对象不合适&#xff0c;对象可能已经被对象池回收&…