Linux 详谈Ext系列⽂件系统(二)

Linux:Ext系列文件系统 提示:以下是本篇文章正文内容,下面案例可供参考本文总结了磁盘的物理结构,并抽象为软件层面上的结构,并将这个结构拆分为若干数据结构进行讲解。 阅读详情

目录

引⼊⽂件系统

引⼊"块"概念

引⼊"分区"概念

引⼊"inode"概念

ext2 ⽂件系统

宏观认识

Block Group

块组内部构成

超级块(Super Block)

GDT(Group Descriptor Table)

块位图(Block Bitmap)

inode位图(Inode Bitmap)

i节点表(Inode Table)

Data Block

inode和datablock映射(弱化)

⽬录与⽂件名

路径解析

路径缓存

挂载分区

⼀个实验:

⼀个结论

⽂件系统总结


引⼊⽂件系统

引⼊"块"概念

其实硬盘是典型的“块”设备,操作系统读取硬盘数据的时候,其实是不会⼀个个扇区地读取,这样效率太低,⽽是⼀次性连续读取多个扇区,即⼀次性读取⼀个”块”(block)。

硬盘的每个分区是被划分为⼀个个的”块”。⼀个”块”的⼤⼩是由格式化的时候确定的,并且不可以更改,最常⻅的是4KB,即连续⼋个扇区组成⼀个 ”块”。”块”是⽂件存取的最⼩单位。

stat main.c

注意:

磁盘就是⼀个三维数组,我们把它看待成为⼀个"⼀维数组",数组下标就是LBA,每个元素都是扇区

每个扇区都有LBA,那么8个扇区⼀个块,每⼀个块的地址我们也能算出来。

知道LBA:块号 = LBA/8

知道块号:LAB=块号*8 + n. (n是块内第⼏个扇区)

引⼊"分区"概念

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

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

注意:
柱⾯⼤⼩⼀致,扇区个位⼀致,那么其实只要知道每个分区的起始和结束柱⾯号,知道每⼀个柱⾯多少个扇区,那么该分区多⼤,其实和解释LBA是多少也就清楚了.

引⼊"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字节
任何⽂件的内容⼤⼩可以不同,但是属性⼤⼩⼀定是相同的

到⽬前为⽌,相信⼤家还有两个问题:

我们已经知道硬盘是典型的“块”设备,操作系统读取硬盘数据的时候,读取的基本单位

是”块”。“块”⼜是硬盘的每个分区下的结构,难道“块”是随意的在分区上排布的吗?那要怎么找到“块”呢?

还有就是上⾯提到的存储⽂件属性的inode,⼜是如何放置的呢?

⽂件系统就是为了组织管理这些的!!

ext2 ⽂件系统

宏观认识

所有的准备⼯作都已经做完,是时候认识下⽂件系统了。我们想要在硬盘上储⽂件,必须先把硬盘格式化为某种格式的⽂件系统,才能存储⽂件。⽂件系统的⽬的就是组织和管理硬盘中的⽂件。在

Linux 系统中,最常⻅的是 ext2 系列的⽂件系统。其早期版本为 ext2,后来⼜发展出 ext3 和 ext4。

ext3 和 ext4 虽然对 ext2 进⾏了增强,但是其核⼼设计并没有发⽣变化,我们仍是以较⽼的 ext2 作为演⽰对象。

ext2⽂件系统将整个分区划分成若⼲个同样⼤⼩的块组 (Block Group),如下图所⽰。只要能管理⼀个分区就能管理所有分区,也就能管理所有磁盘⽂件。

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

Block Group

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

块组内部构成

超级块(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 */
};

GDT(Group Descriptor Table)

块组描述符表,描述块组属性信息,整个分区分成多个块组就对应有多少个块组描述符。每个块组描述符存储⼀个块组 的描述信息,如在这个块组中从哪⾥开始是inode Table,从哪⾥开始是DataBlocks,空闲的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];
};

块位图(Block Bitmap)

Block Bitmap中记录着Data Block中哪个数据块已经被占⽤,哪个数据块没有被占⽤

inode位图(Inode Bitmap)

每个bit表⽰⼀个inode是否空闲可⽤。

i节点表(Inode Table)

存放⽂件属性 如 ⽂件⼤⼩,所有者,最近修改时间等

当前分组所有Inode属性的集合

inode编号以分区为单位,整体划分,不可跨分区

Data Block

数据区:存放⽂件内容,也就是⼀个⼀个的Block。根据不同的⽂件类型有以下⼏种情况:

对于普通⽂件,⽂件的数据存储在数据块中。

对于⽬录,该⽬录下的所有⽂件名和⽬录名存储在所在⽬录的数据块中,除了⽂件名外,ls -l命令看到的其它信息保存在该⽂件的inode中。

Block 号按照分区划分,不可跨分区

inode和datablock映射(弱化)

inode内部存在 __le32 i_block[EXT2_N_BLOCKS];/* Pointers to blocks */ , EXT2_N_BLOCKS =15,就是⽤来进⾏inode和block映射的

这样⽂件=内容+属性,就都能找到了。

思考:
请解释:知道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个操作:

存储属性

内核先找到⼀个空闲的i节点(这⾥是263466)。内核把⽂件信息记录到其中。

存储数据

该⽂件需要存储在三个磁盘块,内核找到了三个空闲块:300,500,800。将内核缓冲区的第⼀块 数据复制到300,下⼀块复制到500,以此类推。

记录分配情况

⽂件内容按顺序300,500,800存放。内核在inode上的磁盘分布区记录了上述块列表。

添加⽂件名到⽬录

新的⽂件名abc。linux如何在当前的⽬录中记录这个⽂件?内核将⼊⼝(263466,abc)添加到⽬录⽂件。⽂件名和inode之间的对应关系将⽂件名和⽂件的内容及属性连接起来。

⽬录与⽂件名

问题:

我们访问⽂件,都是⽤的⽂件名,没⽤过inode号啊?

⽬录是⽂件吗?如何理解?

答案:

⽬录也是⽂件,但是磁盘上没有⽬录的概念,只有⽂件属性+⽂件内容的概念。

⽬录的属性不⽤多说,内容保存的是:⽂件名和Inode号的映射关系

// readdir.c
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <dirent.h>
#include <sys/types.h>
#include <unistd.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) { // 系统调⽤,⾃⾏查阅
        // Skip the "." and ".." directory entries
        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;
}
yzk@bite:~/code/test/test$ ./readdir /
Filename: mnt, Inode: 1048577
Filename: tmp, Inode: 1179650
Filename: sys, Inode: 917506
Filename: libx32, Inode: 17
Filename: srv, Inode: 786434
Filename: lib64, Inode: 16
Filename: sbin, Inode: 18
Filename: dev, Inode: 131073
Filename: swapfile, Inode: 12
Filename: run, Inode: 1048578
Filename: log.txt, Inode: 20
Filename: proc, Inode: 1179649
Filename: lost+found, Inode: 11
Filename: etc, Inode: 262145
Filename: lib, Inode: 14
Filename: opt, Inode: 917505
Filename: usr, Inode: 1179651
Filename: lib32, Inode: 15
Filename: boot, Inode: 655361
Filename: var, Inode: 655365
Filename: product-service-1.0-SNAPSHOT.jar, Inode: 19
Filename: bin, Inode: 13
Filename: media, Inode: 393217
Filename: home, Inode: 786433
Filename: root, Inode: 655362
yzk@bite:~/code/test/test$ ls -li /
total 1014436
13 lrwxrwxrwx 1 root root 7 Sep 14 2020 bin -> usr/bin
655361 drwxr-xr-x 3 root root 4096 May 6 14:34 boot
2 drwxr-xr-x 17 root root 3880 Jul 17 10:39 dev
262145 drwxr-xr-x 98 root root 4096 Oct 27 14:56 etc
786433 drwxr-xr-x 6 root root 4096 Sep 4 14:56 home
14 lrwxrwxrwx 1 root root 7 Sep 14 2020 lib -> usr/lib
15 lrwxrwxrwx 1 root root 9 Sep 14 2020 lib32 -> usr/lib32
16 lrwxrwxrwx 1 root root 9 Sep 14 2020 lib64 -> usr/lib64
17 lrwxrwxrwx 1 root root 10 Sep 14 2020 libx32 -> usr/libx32
20 -rw-r--r-- 1 root root 461 Jul 16 15:51 log.txt
11 drwx------ 2 root root 16384 Sep 14 2020 lost+found
393217 drwxr-xr-x 4 root root 4096 Sep 14 2020 media
1048577 drwxr-xr-x 3 root root 4096 Oct 17 17:47 mnt
917505 drwxr-xr-x 3 root root 4096 May 6 14:37 opt
1 dr-xr-xr-x 169 root root 0 Jul 17 10:26 proc
19 -rw-r--r-- 1 root root 45457485 Feb 3 2024 product-service-1.0-
SNAPSHOT.jar
655362 drwx------ 13 xjh xjh 4096 Oct 27 09:36 root
2 drwxr-xr-x 25 root root 780 Oct 28 20:36 run
18 lrwxrwxrwx 1 root root 8 Sep 14 2020 sbin -> usr/sbin
786434 drwxr-xr-x 2 root root 4096 Apr 23 2020 srv
12 -rw------- 1 root root 993249280 Sep 14 2020 swapfile
1 dr-xr-xr-x 13 root root 0 Jul 17 18:26 sys
1179650 drwxrwxrwt 20 root root 4096 Oct 28 20:39 tmp
1179651 drwxr-xr-x 14 root root 4096 Nov 10 2023 usr
655365 drwxr-xr-x 12 root root 4096 Jun 16 16:40 var

所以,访问⽂件,必须打开当前⽬录,根据⽂件名,获得对应的inode号,然后进⾏⽂件访问

所以,访问⽂件必须要知道当前⼯作⽬录,本质是必须能打开当前⼯作⽬录⽂件,查看⽬录⽂件的内容!

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

路径解析

问题:打开当前⼯作⽬录⽂件,查看当前⼯作⽬录⽂件的内容?当前⼯作⽬录不也是⽂件吗?我们访问当前⼯作⽬录不也是只知道当前⼯作⽬录的⽂件名吗?要访问它,不也得知道当前⼯作⽬录的inode吗?

答案1:所以也要打开:当前⼯作⽬录的上级⽬录,额....,上级⽬录不也是⽬录吗??不还是上⾯的问题吗?

答案2:所以类似"递归",需要把路径中所有的⽬录全部解析,出⼝是"/"根⽬录。

最终答案3:⽽实际上,任何⽂件,都有路径,访问⽬标⽂件,⽐如:

/home/yzk/code/test/test/test.c

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

注意:
所以,我们知道了:访问⽂件必须要有⽬录+⽂件名=路径的原因
根⽬录固定⽂件名,inode号,⽆需查找,系统开机之后就必须知道

可是路径谁提供?

你访问⽂件,都是指令/⼯具访问,本质是进程访问,进程有CWD!进程提供路径。

你open⽂件,提供了路径

可是最开始的路径从哪⾥来?

所以Linux为什么要有根⽬录, 根⽬录下为什么要有那么多缺省⽬录?

你为什么要有家⽬录,你⾃⼰可以新建⽬录?

上⾯所有⾏为:本质就是在磁盘⽂件系统中,新建⽬录⽂件。⽽你新建的任何⽂件,都在你或者系统指定的⽬录下新建,这不就是天然就有路径了嘛!

系统+⽤⼾共同构建Linux路径结构

路径缓存

问题1:Linux磁盘中,存在真正的⽬录吗?

答案:不存在,只有⽂件。只保存⽂件属性+⽂件内容

问题2:访问任何⽂件,都要从/⽬录开始进⾏路径解析?

答案:原则上是,但是这样太慢,所以Linux会缓存历史路径结构

问题2: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_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结构,缓存新路径

挂载分区

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

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

⼀个实验:

$ dd if=/dev/zero of=./disk.img bs=1M count=5 #制作⼀个⼤的磁盘块,就当做⼀个分区
$ 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 # 卸载分区
yzk@yzk:/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)为
⽂件系统,就像它们是物理硬盘分区或者外部存储设备⼀样
yzk@yzk:/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

⼀个结论

分区写⼊⽂件系统,⽆法直接使⽤,需要和指定的⽬录关联,进⾏挂载才能使⽤。

所以,可以根据访问⽬标⽂件的"路径前缀"准确判断我在哪⼀个分区。

⽂件系统总结

下⾯⽤⼏张图总结,⼀张是画的,其他都是在⽹上找的.主要想从不同⻆度说明.

Python与西门子PLC数据交互实战:基于Snap7的跨平台工业通信指南 本文详细介绍了如何使用Python和Snap7库实现与西门子PLC的数据交互。内容涵盖跨平台环境搭建、连接参数配置、数据读写实战及性能优化技巧,为工业自动化、数据采集和智能制造场景提供了一套轻量、灵活且高效的通信解决方案。 阅读详情

相关推荐

MATPOWER下载安装教程

将MATPOWER文件复制在C:\Program Files (x86)\MATLAB\R2016b\bin目录下。原来的窗口中多了很多文件路径,点击【保存】,再点击【关闭】选择所要添加的MATPOWER文件夹,点击【选择文件夹】二、将MATPOWER放在bin目录下。找到菜单栏的【设置路径】点击【添加并包含子文件】一、从官网下载后解压。

weixin_47365903的博客 2万+

Linux Ext 文件系统

引言 本文整理了 Linux 内核中 Ext 文件系统的相关知识。 文件系统种类 前面我们讨论了虚拟文件系统,它对所有下层文件系统进行了封装,统一了上层接口并粘合了下层文件系统,这时候应用开发者就接触不到硬盘的数据组织,但是大家一定很好奇,在文件系统这一层是如何组织并有效的利用硬盘的。接下来,我们将介绍 Ext 文件系统的实现方案,同时会顺带着介绍一下无持久存储的文件系统(/proc)。 Ext 文件系统 Ext 文件系统这里主要介绍 Ext2 和 Ext3,它们的特性如下: Ext2 文件系统: 该文

贝克街的流浪猫 1313

批量读取_MCGS触摸屏如何实现批量读取modbus数据的方法?

前两天,在一个QQ群里,有同行的朋友问,在MCGS中如何实现批量读取数据,因为他当时面临设备,通讯协议才是MODBUS RTU,同时厂家说地址不能分开读,必须是连续的。鉴于他提供的消息是能够设置的地方都试过了。为此,我直接告诉通过脚本,批量读取的方案。那么这个方案究竟如何实现?且看我下面慢慢道来。一、工具1.modsim:仿真PLC或者第三方设备。2.MCGS组态软件3.串口软件:在电脑中生成两不...

weixin_32511509的博客 8159

Linux Ext系列文件系统(看这一篇就够了!)

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

酷酷学!!!的博客 1584

手把手教你用STM32单片机控制双向可控硅调光(附完整代码+电路图)

本文提供了一套完整的STM32单片机控制双向可控硅实现灯光无级调光的实战方案。详细讲解了硬件电路设计,包括过零检测与隔离驱动,并剖析了基于定时器中断的软件核心逻辑,通过精准的相位角计算实现平滑的灯光强度控制。附有完整代码与电路图,适合嵌入式开发者实践。

weixin_29194255的博客 951

ArcGIS新手必看:用Landsat 8数据计算植被覆盖度的完整流程(附DEM裁剪技巧)

本文为ArcGIS新手提供了使用Landsat 8数据计算植被覆盖度的完整流程指南。详细讲解了从数据预处理、NDVI计算到基于像元二分模型反演植被覆盖度的核心步骤,并重点分享了利用DEM进行精确裁剪的实用技巧,帮助用户高效完成地表信息提取与分析。

weixin_29279047的博客 237

Ext系列文件系统

磁盘存储、逻辑结构。文件系统。分区、块组、inode、软硬链接。本文系统介绍了Linux文件系统的核心结构与工作原理,重点分析了Ext系列文件系统的演进与实现。首先从磁盘物理结构入手,详细解释了CHS/LBA寻址方式、扇区/块/柱面的组织关系。随后深入剖析了Ext2文件系统的架构设计,包括块组划分、超级块、inode表、数据块等核心组件,并阐述了inode指针的多级映射机制。针对目录管理,文章揭示了Linux通过dentry结构构建路径缓存的优化策略。最后对比了硬链接与软链接的本质区别

wangchen_0的博客 1568

拯救者R7000用户实测:用华为电脑管家11.0版搞定MatePad Pro多屏协同(附驱动与显卡避坑指南)

本文详细介绍了如何在非华为笔记本联想拯救者R7000上安装华为电脑管家11.0版,实现与MatePad Pro的多屏协同功能。从驱动优化到显卡配置,提供了全面的避坑指南和实用技巧,帮助用户解决连接失败等常见问题,提升跨设备生产力体验。

weixin_30788619的博客 398

ext文件系统

ext文件系统从历史到认知到实验

stephen_curry300的博客 3486

YOLOv11 模型轻量化全流程:从剪枝到量化,打造移动端“极速检测利器”

将浮点模型量化为INT8模型,减少内存占用并加速推理。# 加载剪枝后的模型# 转换为TorchScript# 静态量化# 保存量化模型量化后模型推理速度可再提升20%,而mAP损失小于1%。

m0_64661770的博客 465

【DaVinci Configurator专题】-6-将UDS&OBD诊断数据库文件CDD和PDX导入到CFG

CDD和ODX是两种诊断数据库文件。机器可读的诊断数据库其实就是将传统的Excel调查表等信息编辑成CDD或ODX文件。CDD是Vector私有的一种诊断数据库格式;ODX是国际标准的诊断数据交互格式。CANdelaStudio编辑CDD文件,以及也可以导出ODX文件;ODX Studio用于编辑ODX文件,诊断数据库的编辑其实就是诊断需求的定义。 下面以PDX为例,将其导入到DaVinci Configurator。 目录 1 打开DaVinci Configurator,导入PDX文件 2 解.

「汽车电子助手」的博客 6750

ext系列文件系统

如果当该块组的数据区已满但仍有数据需要存储,那么就会使用其他块组的数据区,因为一个分区中所有数据区里的块的编号是唯一的。如 bolck 和 inode 的总量,未使用的 block 和 inode 的数量,一个 block 和 inode 的大小,最近一次挂载的时间,最近一次写入数据的时间等。在寻找某一扇区时,会先通过移动磁臂(会带动所有磁头同步沿径向移动)找到该扇区所在的柱面,也就是找到对应的磁道,然后通过激活特定编号的磁头来找到具体盘片和该盘片的正反面,最后通过转轴来找到扇面,这种方式被称为CHS。

2301_80662872的博客 967

数学建模模型总结

这是本人在两年内准备参加数学建模竞赛搜集到的模型资料,已经经过了本人的筛选和整理,希望能够对准备参加数学建模竞赛的学弟学妹们提供一些帮助!

Linux:Ext系列⽂件系统(二)

用 “块” 存储文件内容,提高读写效率;用 “分区” 划分磁盘功能,方便管理;用 “inode” 存储文件属性,作为文件的唯一标识;用 “目录” 维护文件名与 inode 的映射,让我们能通过文件名访问文件。

hello world/linux 937

Outlook Express 6 (OE6) for Win7/Win8/Win10

英文版 Outlook Express6.0, 兼容Win7/Win10/WinXP,解压就可用。

jre9下载 jdk9

(Java SE Runtime Environment 9.0) 9.0u175 官方最新版 【x86|x64】

Linux 详谈Ext系列⽂件系统(一)

本文介绍了磁盘的物理和逻辑结构。磁盘通过CHS(柱面、磁头、扇区)寻址定位数据,但其容量受限。逻辑上,磁盘可视为线性结构,采用LBA(逻辑块地址)编址方式。LBA与CHS可相互转换:LBA=柱面号×单个柱面扇区总数+磁头号×每磁道扇区数+扇区号-1。操作系统只需使用LBA地址,磁盘内部会自动完成地址转换。这种机制使磁盘在逻辑上成为一个以扇区为元素的一维数组,简化了数据访问过程。

yuzhaokangcode的博客 1030

温度PID控制PLC程序

温度PID稳定控制程序,适用于欧姆龙PLC,对其他PLC也具有很大参考价值

Linux系统学习【掌握Ext系列⽂件系统的相关内容】

本文系统介绍了Linux文件系统的核心知识,重点解析Ext系列文件系统(Ext2/3/4)的结构与原理。内容涵盖磁盘硬件架构、服务器部署层级(磁盘→服务器→机柜→机房),以及磁盘的物理存储结构(柱面/磁道/扇区)。深入讲解文件系统核心组件:超级块、inode、数据块、位图、目录项等关键概念,并对比Ext各版本的演进特点(如日志机制)。此外,还涉及文件访问流程、磁盘挂载、容量查看、文件系统检查等实用操作,为理解Linux存储管理机制和性能优化奠定基础。

承于所学,探技术之渊;以代码求政,寻成长之道。 1万+

[Linux文件系统——Lesson16.Ext系列⽂件系统]

本文系统介绍了Linux文件系统的核心机制,重点分析了ext系列文件系统的设计原理。主要内容包括: 磁盘物理结构:详细讲解了CHS和LBA地址的转换原理,以及磁盘的分区、格式化过程。 文件系统架构:深入剖析了ext2/ext3/ext4的块组(BlockGroup)设计,包括超级块(SuperBlock)、组描述表(GDT)、块位图(BlockBitmap)、inode位图(InodeBitmap)、inode表和数据块(DataBlock)等核心组件。 文件管理机制:阐述了inode如何通过直接/间接指针

2401_83386596的博客 1440

Ext系列⽂件系统

本文系统介绍了磁盘硬件结构和文件系统的基本原理。首先阐述了磁盘的物理结构(磁头、磁道、扇区等)和逻辑寻址方式(CHS和LBA地址转换)。然后重点讲解了Linux ext2文件系统的核心概念,包括块组结构、inode机制和数据块管理。特别说明了目录文件的存储方式(文件名与inode的映射关系),以及创建文件和写入数据的具体过程。通过分析文件系统的分层设计,揭示了操作系统如何将物理磁盘抽象为逻辑文件存储,为理解计算机存储系统提供了理论基础。

qq65653232的博客 992

LINUX-Ext系列⽂件系统

那个C,D,E就是分区。柱⾯是分区的最⼩单位,我们可以利⽤参考柱⾯号码的⽅式来进⾏分区,其本质就是设置每个区的起始柱⾯和结束柱⾯号码。记录的信息主要有:bolck 和 inode的总量,未使⽤的block和inode的数量,⼀个block和inode的⼤⼩,最近⼀次挂载的时间,最近⼀次写⼊数据的时间,最近⼀次检验磁盘的时间等其他⽂件系统的相关信息。硬链接是通过inode引⽤另外⼀个⽂件,软链接是通过名字引⽤另外⼀个⽂件,但实际上,新的⽂件和被引⽤的⽂件的inode不同,应⽤常⻅上可以想象成⼀个快捷⽅式。

2301_80337275的博客 589

初识Linux(14)Ext系列⽂件系统

之前谈论的都是已打开文件在操作系统的中的管理,但是还有更多的文件没有被打开,被存在磁盘中,如何管理这些磁盘中的文件,就是本篇的学习目标。

2301_79501467的博客 924

Linux:Ext系列⽂件系统(三)

路径解析是文件访问的 “导航逻辑”,从根目录逐级查找,核心是目录的映射表挂载是分区的 “接入方式”,让多个分区融入统一的目录树软硬连接是文件的 “复用技巧”,根据是否跨分区、是否依赖原文件选择使用理解了这些内容,你就能更灵活地管理磁盘和文件,甚至解决一些常见的系统问题(比如死链接清理、挂载失败排查)。文件系统的底层逻辑看似复杂,但只要抓住 “inode 是核心、目录是映射、挂载是连接” 这三个关键点,就能一通百通。

hello world/linux 1198

6. Ext系列⽂件系统

本文主要介绍了Ext系列文件系统的关键概念和磁盘存储基础,重点内容包括: 磁盘物理结构:阐述了机械磁盘的硬件组成,包括磁头、磁道、柱面和扇区等基本单元,以及CHS寻址方式的原理和局限性。 逻辑存储结构:介绍了磁盘的逻辑线性化过程,将三维物理结构转化为LBA(逻辑块地址)的一维寻址方式,并说明了CHS与LBA地址的相互转换机制。 块设备概念:解释了操作系统以"块"为单位访问磁盘的原因(典型块大小为4KB,由8个连续扇区组成),这是文件存取的最小单位。 文件系统基础:强调文件本质是存储在扇区中的数据(内容+属性

2402_88143778的博客 360

Linux:Ext系列⽂件系统(一)

对我们(以及 OS)来说,磁盘就是一个 “元素为扇区的一维数组”,数组下标是 LBA 地址 —— 我们不用关心磁头怎么移动、盘片怎么旋转,只需要告诉磁盘 “我要访问 LBA=XXX 的扇区”,剩下的底层转换全由磁盘固件完成理解这些底层逻辑,不仅能搞懂 “磁盘为什么慢”(磁头移动、盘片旋转需要时间),也为后续学习文件系统(比如 EXT4、NTFS 怎么管理扇区)打下基础 —— 毕竟所有文件的内容和属性,最终都是存在一个个扇区里的。

hello world/linux 1114

Linux Ext系列⽂件系统

我们常说的磁盘,分为 机械硬盘(HDD,就是下面图里的这种)

Zengwengang的博客 380

【Linux】Ext系列文件系统

【Linux】Ext系列文件系统

tan_run的博客 1987

linux--------------Ext系列⽂件系统(下)

└── symlink.txt -> source.txt # 软链接(inode=1002,数据块存储路径"source.txt")| Inode号(新) | 目录项长度 | 文件名长度 | 文件类型(符号链接) | 文件名(如symlink.txt) || Inode号(与源相同) | 目录项长度 | 文件名长度 | 文件类型 | 文件名(如hardlink.txt) || Inode号(4B) | 目录项长度(2B) | 文件名长度(1B) | 文件类型(1B) | 文件名(变长) |

qwer55588的博客 1032
上一篇: Linux 详谈Ext系列⽂件系统(一)
下一篇: Linux 详谈软硬链接
万我.
博客等级 码龄1年 460粉丝 93原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值