Ext系列⽂件系统
Ext系列⽂件系统
- 1. 理解硬件
- 1.1 磁盘的物理结构
- 1.2 磁盘的存储结构
- 1.3 磁盘的逻辑结构
- 理解过程
- 实际过程
- 1.4 CHS&&LBA地址
- 2. 引入文件系统
- 块
- 分区
- innode
- 3. Ext2文件系统
- 3.1 宏观认识
- 3.2 block group
- 3.3 块组内部
- 3.3.1 GDT(Group Descriptor Table)
- 3.3.2 超级块(Super Block)
- 3.3.3 块位图(Block Bitmap)
- 3.3.4 inode位图(Inode Bitmap)
- 3.3.5 i节点表(Inode Table)
- 3.3.6 Data Block
- 3.4 inode和datablock映射(弱化)
- 3.5 目录与文件名
- 3.6 路径解析
- 3.7 路径缓存
- 3.8 挂载分区
- 3.9 文件系统总结
- 4. 软硬连接
- 4.1 软链接
- 4.2 硬链接
1. 理解硬件
1.1 磁盘的物理结构

1.2 磁盘的存储结构

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


定位扇区:
- 定位我们的磁头每个磁头有编号每一个盘面有两面分别都有一个磁头。定位了我们的磁头就是相当于确定了我们要访问的扇区在哪一个盘面上。
- 定位我们的柱面(柱面由所有盘面的磁头形成的)就是定位了我们的扇区在哪一个磁道上
- 定位我们的扇区
- CHS地址定位
⽂件 = 内容+属性 都是数据,⽆⾮就是占据那⼏个扇区的问题!能定位⼀个扇区了,能不能定位多个扇区呢?下面我们看一下我们的磁盘

- 扇区是从磁盘读出和写⼊信息的最⼩单位,通常⼤⼩为 512 字节。
- 磁头(head)数:每个盘⽚⼀般有上下两⾯,分别对应1个磁头,共2个磁头
- 磁道(track)数:磁道是从盘⽚外圈往内圈编号0磁道,1磁道…,靠近主轴的同⼼圆⽤于停靠磁头,不存储数据
- 柱⾯(cylinder)数:磁道构成柱⾯,数量上等同于磁道个数
- 扇区(sector)数:每个磁道都被切分成很多扇形区域,每道的扇区数量相同* 圆盘(platter)数:就是盘⽚的数量
- 磁盘容量=磁头数 × 磁道(柱⾯)数 × 每道扇区数 × 每扇区字节数
- 传动臂上的磁头是共进退的
柱⾯(cylinder),磁头(head),扇区(sector),显然可以定位数据了,这就是数据定位(寻址)⽅式之⼀,CHS寻址⽅式。
对早期的磁盘⾮常有效,知道⽤哪个磁头,读取哪个柱⾯上的第⼏扇区就可以读到数据了。但是CHS模式⽀持的硬盘容量有限,因为系统⽤8bit来存储磁头地址,⽤10bit来存储柱⾯地址,⽤6bit来存储扇区地址,⽽⼀个扇区共有512Byte,这样使⽤CHS寻址⼀块硬盘最⼤容量为256 * 1024 * 63 * 512B = 8064 MB(1MB = 1048576B)(若按1MB=1000000B来算就是8.4GB)其中有一个扇区存的是扇区的地址,其余才是存储容量。
1.3 磁盘的逻辑结构
理解过程

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

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

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

实际过程
所以,磁盘的真实情况是:
磁道:
某⼀盘⾯的某⼀个磁道展开:

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

就是一个二维数组;因为我们的柱面个数不唯一因此整个盘的情况如下

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

操作系统只需要使⽤LBA就可以了!! LBA地址转成CHS地址,CHS如何转换成为LBA地址。谁做啊??磁盘⾃⼰来做!固件(硬件电路,伺服系统)
1.4 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(确定在该柱面的列数)
- “//”: 表⽰除取整
2. 引入文件系统
块
其实硬盘是典型的“块”设备,操作系统读取硬盘数据的时候,其实是不会⼀个个扇区地读取,这样效率太低,⽽是⼀次性连续读取多个扇区,即⼀次性读取⼀个”块”(block)。
硬盘的每个分区是被划分为⼀个个的”块”。⼀个”块”的⼤⼩是由格式化的时候确定的,并且不可以更改,最常⻅的是4KB,即连续⼋个扇区组成⼀个 ”块”。”块”是⽂件存取的最⼩单位。

- 磁盘就是⼀个三维数组,我们把它看待成为⼀个"⼀维数组",数组下标就是LBA,每个元素都是扇区
- 每个扇区都有LBA,那么8个扇区⼀个块,每⼀个块的地址我们也能算出来。
- 知道LBA:块号 = LBA/8
- 知道块号:LAB=块号*8 + n. (n是块内第⼏个扇区)

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

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

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

每⾏包含7列:
- 模式
- 硬链接数
- ⽂件所有者
- 组
- ⼤⼩
- 最后修改时间
- ⽂件名

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

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

每⼀个⽂件都有对应的inode就是一个编号可以通过我们的ls -li查看,⾥⾯包含了与该⽂件有关的⼀些信息。为了能解释清楚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,⼜是如何放置的呢?
⽂件系统就是为了组织管理这些的!!下面我们就进入我们文件系统的讲解!!!
3. Ext2文件系统
3.1 宏观认识
所有的准备⼯作都已经做完,是时候认识下⽂件系统了。我们想要在硬盘上储⽂件,必须先把硬盘格式化为某种格式的⽂件系统,才能存储⽂件。⽂件系统的⽬的就是组织和管理硬盘中的⽂件。在Linux 系统中,最常⻅的是 ext2 系列的⽂件系统。其早期版本为 ext2,后来⼜发展出 ext3 和 ext4。ext3 和 ext4 虽然对 ext2 进⾏了增强,但是其核⼼设计并没有发⽣变化,我们仍是以较⽼的 ext2 作为演⽰对象。ext2⽂件系统将整个分区划分成若⼲个同样⼤⼩的块组 (Block Group),如下图所⽰。只要能管理⼀个分区就能管理所有分区,也就能管理所有磁盘⽂件。

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

3.2 block group
ext2⽂件系统会根据分区的⼤⼩划分为数个Block Group。⽽每个Block Group都有着相同的结构组成因此我们是站在分区的角度上宏观分配我们的大小因此inode编号是可以跨分组的但是不可以跨分区的。
3.3 块组内部
3.3.1 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];
};
3.3.2 超级块(Super Block)
存放⽂件系统本⾝(就是我们的分区)的结构信息,描述整个分区的⽂件系统信息。记录的信息主要有:bolck 和 inode的总量,未使⽤的block和inode的数量,⼀个block和inode的⼤⼩,最近⼀次挂载的时间,最近⼀次写⼊数据的时间,最近⼀次检验磁盘的时间等其他⽂件系统的相关信息。Super Block的信息被破坏,可以说整个⽂件系统结构就被破坏了(因为Super Block是一个分区的管理信息)。超级块在每个块组的开头都有⼀份拷⻉(第⼀个块组必须有,后⾯的块组可以没有)。 为了保证⽂件系统在磁盘部分扇区出现物理问题的情况下还能正常⼯作,就必须保证⽂件系统的super block信息在这种情况下也能正常访问。所以⼀个⽂件系统的super block会在多个block group中进⾏备份,这些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.3 块位图(Block Bitmap)
- Block Bitmap中记录着Data Block中哪个数据块已经被占⽤,哪个数据块没有被占⽤。也就是快位图每一个比特位的值代表数据块是否被使用过了。
3.3.4 inode位图(Inode Bitmap)
- 每个bit表⽰⼀个inode是否空闲可⽤。
3.3.5 i节点表(Inode Table)

- 存放⽂件属性 如 ⽂件⼤⼩,所有者,最近修改时间等
- 当前分组所有Inode属性的集合
- inode编号以分区为单位,整体划分,不可跨分区
3.3.6 Data Block
数据区:存放⽂件内容,也就是⼀个⼀个的Block。根据不同的⽂件类型有以下⼏种情况:
- 对于普通⽂件,⽂件的数据存储在数据块中。
- 对于⽬录,该⽬录下的所有⽂件名和⽬录名存储在所在⽬录的数据块中,除了⽂件名外,ls -l命令看到的其它信息保存在该⽂件的inode中。
- Block 号按照分区划分,不可跨分区
3.4 inode和datablock映射(弱化)

3.5 目录与文件名
问题:
- 我们访问⽂件,都是⽤的⽂件名,没⽤过inode号啊?
- ⽬录是⽂件吗?如何理解?
答案: - ⽬录也是⽂件,但是磁盘上没有⽬录的概念,只有⽂件属性+⽂件内容的概念。
- ⽬录的属性不⽤多说,内容保存的是:⽂件名和Inode号的映射关系
- 所以,访问⽂件,必须打开当前⽬录,根据⽂件名,获得对应的inode号(因为操作系统只认我们inode编号),然后进⾏⽂件访问
- 所以,访问⽂件必须要知道当前⼯作⽬录,本质是必须能打开当前⼯作⽬录⽂件,查看⽬录⽂件的内容!(路径是我们的进程提供的因为进程的当前目录来自bash,bash的路径来自我们的系统)。
3.6 路径解析
问题:打开当前⼯作⽬录⽂件,查看当前⼯作⽬录⽂件的内容?当前⼯作⽬录不也是⽂件吗?我们访问当前⼯作⽬录不也是只知道当前⼯作⽬录的⽂件名吗?要访问它,不也得知道当前⼯作⽬录的inode吗?
答案1:所以也要打开:当前⼯作⽬录的上级⽬录,额…,上级⽬录不也是⽬录吗??不还是上⾯的问题吗?
答案2:所以类似"递归",需要把路径中所有的⽬录全部解析,出⼝是"/"根⽬录。
最终答案3:⽽实际上,任何⽂件,都有路径,访问⽬标⽂件,⽐如:
/home/whb/code/test/test/test.c
都要从根⽬录开始,依次打开每⼀个⽬录,根据⽬录名,依次访问每个⽬录下指定的⽬录,直到访问到test.c。这个过程叫Linux路径解析。
可是路径谁提供?
- 你访问⽂件,都是指令/⼯具访问,本质是进程访问,进程有CWD!进程提供路径。
- 你open⽂件,提供了路径
可是最开始的路径从哪⾥来? - 所以Linux为什么要有根⽬录, 根⽬录下为什么要有那么多缺省⽬录?
- 你为什么要有家⽬录,你⾃⼰可以新建⽬录?
- 上⾯所有⾏为:本质就是在磁盘⽂件系统中,新建⽬录⽂件。⽽你新建的任何⽂件,都在你或者系统指定的⽬录下新建,这不就是天然就有路径了嘛!
- 系统+⽤⼾共同构建Linux路径结构.
3.7 路径缓存
问题1:Linux磁盘中,存在真正的⽬录吗?
答案:不存在,只有⽂件。只保存⽂件属性+⽂件内容
问题2:访问任何⽂件,都要从/⽬录开始进⾏路径解析?
答案:原则上是,但是这样太慢,所以Linux会缓存历史路径结构
问题2:Linux⽬录的概念,怎么产⽣的?
答案:打开的⽂件是⽬录的话,由OS⾃⼰在内存中进⾏路径维护
Linux中,在内核中维护树状路径结构的内核结构体叫做:struct dentry
struct dentry {// 1. 目录项的引用计数atomic_t d_count; // 引用计数,为 0 时释放unsigned int d_flags; // 状态标志(如是否被删除)// 2. 文件名与父目录项struct qstr d_name; // 文件名(字符串 + 哈希值)struct dentry *d_parent; // 父目录的 dentry(形成目录树)// 3. 关联的 inode 和文件系统struct inode *d_inode; // 对应的 inode(文件元数据)struct super_block *d_sb; // 所属的超级块(文件系统)// 4. 子目录项链表(用于构建目录树)struct list_head d_subdirs; // 子目录项链表struct list_head d_child; // 在父目录的 d_subdirs 链表中的位置// 5. 目录项缓存(dcache)的哈希表struct hlist_node d_hash; // 用于哈希表快速查找// 6. 文件操作接口(例如挂载点的特殊处理)const struct dentry_operations *d_op;
};
};
注意:
- 每个⽂件其实都要有对应的dentry结构,包括普通⽂件。这样所有被打开的⽂件,就可以在内存中形成整个树形结构
- 整个树形节点也同时会⾪属于LRU(Least Recently Used,最近最少使⽤)结构中,进⾏节点淘汰
- 整个树形节点也同时会⾪属于Hash,⽅便快速查找
- 更重要的是,这个树形结构,整体构成了Linux的路径缓存结构,打开访问任何⽂件,都在先在这棵树下根据路径进⾏查找,找到就返回属性inode和内容,没找到就从磁盘加载路径,添加dentry结构,缓存新路径


dentry 是内核为了加速路径解析而维护的缓存结构,它记录了文件名到 inode 的映射关系。即使文件被删除,dentry 可能仍存在于缓存中(直到被回收)。
3.8 挂载分区
我们已经能够根据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 # 卸载分区
whb@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

- 分区写⼊⽂件系统,⽆法直接使⽤,需要和指定的⽬录关联,进⾏挂载才能使⽤。
- 所以,可以根据访问⽬标⽂件的"路径前缀"准确判断我在哪⼀个分区。
到此为止我们就应该理解了我们在调用fopen的时候我们的OS做些什么?
用户调用 fopen()
→ glibc 调用 open() 系统调用
→ 内核路径解析
→ 遍历目录项,加载 dentry 和 inode(ext2 实现)
→ 权限检查
→ 创建文件描述符和 struct file
→ 返回 fd 给用户态
→ glibc 封装为 FILE 结构体
进程的打开文件表(struct files_struct)
|
|— struct file (每个打开的文件实例)
|
|— f_path (文件路径信息)
|
|— struct dentry (目录项)
| |
| |— d_inode (关联的 inode)
|
|— struct vfsmount (挂载点信息)
3.9 文件系统总结




4. 软硬连接
4.1 软链接
ln -s <源文件或目录> <目标链接名>
rm <链接名>
unlink <链接名>

软链接是通过名字引⽤另外⼀个⽂件,但实际上,新的⽂件和
被引⽤的⽂件的inode不同,应⽤常⻅上可以想象成⼀个快捷⽅式
4.2 硬链接
ln <源文件> <目标链接名>
rm <链接名>
unlink <链接名>
作用:
- 可以来进行备份
- 本质是我们新建立了一个新文件只不过新文件的inode是我们的源文件的innode
下面我们看下

这里的数字1和2就是我们的硬链接数代表有1个或两个文件名指向同一个inode编号。因此我们新建一个目录硬链接数就是2一位目录下面有一个.指向当前目录。如果我们在新建的目录下在新建一个目录,此时第一次建立的目录的硬链接数就是3此时多了一个…指向上级目录。
相关文章:
Ext系列⽂件系统
Ext系列⽂件系统 1. 理解硬件1.1 磁盘的物理结构1.2 磁盘的存储结构1.3 磁盘的逻辑结构理解过程实际过程 1.4 CHS&&LBA地址 2. 引入文件系统块分区innode 3. Ext2文件系统3.1 宏观认识3.2 block group3.3 块组内部3.3.1 GDT(Group Descriptor Table…...
JavaScript 对象复制:浅拷贝与深拷贝
JavaScript 对象复制:浅拷贝与深拷贝使用说明 在 JavaScript 中,对象复制分为 浅拷贝 和 深拷贝,两者的核心区别在于是否递归复制嵌套的引用类型属性。以下是详细说明和示例: 一、浅拷贝(Shallow Copy) 特…...
MTK-Android12 13 屏蔽掉Viewing full screen
去掉ROOM 开机第一次提示全屏弹框 文章目录 需求参考资料修改文件实现方案 解决思路grep 源码查找信息grep 查找 grep -rn "Viewing full screen" 找string 字段grep 查找 grep -rn immersive_cling_title 布局grep 查找 grep -rn layout.immersive_mode_cling 对应的…...
mysql数据库的线程连接数、状态 、最大并发数、缓存等参数配置
mysql数据库的线程连接数、状态 、最大并发数、缓存等参数配置 https://www.modb.pro/db/1784385883449397248 mysql数据库的线程连接数、状态 、最大并发数、缓存等参数配置 SQL命令行临时设置操作 #查看mysql数据库的线程连接数: mysql> show global statu…...
OpenGauss 数据库介绍
OpenGauss 数据库介绍 OpenGauss 是华为基于 PostgreSQL 开发的企业级开源关系型数据库,现已成为开放原子开源基金会的项目。以下是 OpenGauss 的详细介绍: 一 核心特性 1.1 架构设计亮点 特性说明优势多核并行NUMA感知架构充分利用现代CPU多核性能行…...
GitHub创建远程仓库
使用GitHub创建远程仓库:从零开始实现代码托管与协作 前言 在当今软件开发领域,版本控制系统已成为开发者必备的核心工具。作为分布式版本控制系统的代表,Git凭借其强大的分支管理和高效的协作能力,已成为行业标准。而GitHub作为…...
【AI部署】腾讯云GPU -—SadTalker的AI数字人访问web服务—未来之窗超算中心
访问部署在Cloud Studio上的web服务 当你把该项目部署在本地时,访问该服务的请求地址为http://localhost:8080/hello;当你把该项目部署在Cloud Studio工作台启动时,要想访问到该服务,需要先在工作台右侧打开访问链接面板ÿ…...
基于spring boot 集成 deepseek 流式输出 的vue3使用指南
本文使用deepseek API接口流式输出的文章。 环境要求 jdk17 spring boot 3.4 代码如下: package com.example.controller;import jakarta.annotation.PostConstruct; import org.springframework.ai.chat.messages.AssistantMessage; import org.springframework.ai.chat.mes…...
fastdds:传输层SHM和DATA-SHARING的区别
下图是fastdds官方的图,清晰地展示了dds支持的传输层: 根据通信双方的相对位置(跨机器、同机器跨进程、同进程)的不同选择合适的传输层,是通信中间件必须要考虑的事情。 跨机器:udp、tcp 跨机器通信,只能通过网络, f…...
堆栈溢出 StackOverflowError 排查
报错: exHandler dispatch failed; nested exception is java.lang.StackOverflowError org.springframework.web.util.NestedServletException: Handler dispatch failed; nested exception is java.lang.StackOverflowError at org.springframework.web.servlet.D…...
树莓派_利用Ubuntu搭建gitlab
树莓派_利用Ubuntu搭建gitlab 一、给树莓派3A搭建基本系统 1、下载系统镜像 https://cdimage.ubuntu.com/ubuntu/releases/18.04/release/ 2、准备系统SD卡 二、给树莓派设备联网 1、串口后台登录 使用串口登录后台是最便捷的,因为前期网络可能不好直接成功 默…...
ARINC818协议(三)
源特定参数 源特定参数被定义,用于在源和目的之间进行传输 源特定参数包括初始化,合适的解释,周期性的验证。 gamma or palette tables:伽马或者调色板 color format:颜色格式 Brightness and backlight control :亮度…...
得佳胜哲讯科技 SAP项目启动会:胶带智造新起点 数字转型新征程
在全球制造业加速向数字化、智能化转型的浪潮中,胶带制造行业正迎来以“自动化生产、数据化运营、智能化决策”为核心的新变革。工业互联网、大数据分析与智能装备的深度融合,正推动胶带制造从传统生产模式向“柔性化生产精准质量控制全链路追溯”的智慧…...
万字解析TCP
通过学习视频加博客的组合形式,整理了一些关于TCP协议的知识。 *图源:临界~的csdn博客。 一、TCP建立连接 TCP的建立连接,大致可以分为面向连接、TCP报文结构、TCP的三次握手、TCP的建立状态、SYN泛洪攻击。 1.1、面向连接 面向连接 --- …...
PyTorch 浮点数精度全景:从 float16/bfloat16 到 float64 及混合精度实战
PyTorch 在深度学习中提供了多种 IEEE 754 二进制浮点格式的支持,包括半精度(float16)、Brain‑float(bfloat16)、单精度(float32)和双精度(float64),并通过统…...
2025年大数据实训室建设及大数据实训平台解决方案
一、引言 在数字化浪潮中,大数据技术已成为推动各行业创新发展的核心驱动力。从金融领域的风险预测到医疗行业的精准诊断,从电商平台的个性化推荐到交通系统的智能调度,大数据的应用无处不在。据权威机构预测,到 2025 年…...
我的机器学习之路(初稿)
文章目录 一、机器学习定义二、核心三要素三、算法类型详解1. 监督学习(带标签数据)2. 无监督学习(无标签数据)3. 强化学习(决策优化)(我之后主攻的方向) 四、典型应用场景五、学习路线图六、常见误区警示七…...
Python 高阶函数:日志的高级用法
日志装饰器的 **7 个高阶优化方案**,结合了生产环境最佳实践和调试深度需求: --- ### 一、**智能动态采样装饰器** 解决高频函数日志过多问题,自动根据错误率调整日志频率 python from collections import defaultdict import time cla…...
贪心、动态规划、其它算法基本原理和步骤
目录 1. 贪心1.1 贪心算法的基本步骤1.2 贪心算法实战1.2.1 贪心的经典问题1.2.2 贪心解决数组与子序列问题1.2.3 贪心解决区间调度问题1.2.4 贪心解决动态决策问题1.2.5 贪心解决一些复杂场景应用 2. 动态规划2.1 动态规划的基本步骤和一些优化2.2 动态规划实战2.2.1 斐波那契…...
python-各种文件(txt,xls,csv,sql,二进制文件)读写操作、文件类型转换、数据分析代码讲解
1.文件txt读写标准用法 1.1写入文件 要读取文件,首先得使用 open() 函数打开文件。 file open(file_path, moder, encodingNone) file_path:文件的路径,可以是绝对路径或者相对路径。mode:文件打开模式,r 代表以…...
[250418] 智谱 AI 发布新一代模型,同时推出新域名 Z.ai
目录 智谱开源 GLM-4-32B-0414 系列 AI 模型开源赋能,加速 AI 应用落地性能卓越,比肩顶尖模型应用广泛,赋能各行各业 智谱开源 GLM-4-32B-0414 系列 AI 模型 国内人工智能领军企业智谱华章正式开源新一代 GLM-4-32B-0414 系列大语言模型&…...
ctfshow-大赛原题-web702
因为该题没有理解到位,导致看wp也一直出错,特此反思一下。 参考yu22x师傅的文章 :CTFSHOW大赛原题篇(web696-web710)_ctfshow 大赛原题-CSDN博客 首先拿到题目: // www.zip 下载源码 我们的思路就是包含一个css文件,…...
Triton(2)——Triton源码接结构
1 triton 3.0.0 源码结构 triton docs/:项目文档 cmake/:构建配置相关 bin/:工具、脚本 CmakeLists.txt:cmake 配置文件 LSCENSE README.md Pyproject.toml:python 项目配置文件 utils/:项目配置文…...
容器docker入门学习
这里写目录标题 容器容器的软件厂商 dockerdocker引擎 虚拟化虚拟化技术 docker安装详解1、安装检查2、安装yum相关的工具3、安装docker-ce软件4、查看docker版本5、启动docker服务6、设置docker开机启动7、查看有哪些docker容器运行进程8、查看容器里有哪些镜像9、下载nginx软…...
Android——动画
帧动画 帧动画就是很多张图片,一帧一帧的播放,形成的一个动画效果。 frame.xml <?xml version"1.0" encoding"utf-8"?> <animation-list xmlns:android"http://schemas.android.com/apk/res/android">&l…...
HarmonyOS NEXT开发教程:全局悬浮窗
今天跟大家分享一下HarmonyOS开发中的悬浮窗。 对于悬浮窗,可能有的同学会想到使用层叠布局是否可以实现,将悬浮窗叠在导航栏组件Tabs上,像这样: Stack({alignContent:Alignment.BottomEnd}){Tabs({barPosition:BarPosition.End…...
守护进程及gdb调试(新手简略版)
一、守护进程 守护进程(Daemon Process)是一种在后台运行的特殊进程,它独立于控制终端,并且在系统启动时自动运行,通常用于执行一些系统级的任务或提供特定的服务。以下是关于守护进程的详细定义和特点: …...
解锁元生代:ComfyUI工作流与云原生后端的深度融合
目录 蓝耘元生代:智算新势力崛起 ComfyUI 工作流创建详解 ComfyUI 初印象 蓝耘平台上搭建 ComfyUI 工作流 构建基础工作流实操 代码示例与原理剖析 云原生后端技术全景 云原生后端概念解析 核心技术深度解读 蓝耘元生代中两者的紧密联系…...
STM32 基本GPIO控制
目录 GPIO基础知识 编辑IO八种工作模式 固件库实现LED点灯 蜂鸣器 按键基础知识 编辑继电器 震动传感器 433M无线模块 GPIO基础知识 GPIO(General-Purpose input/output,通用输入/输出接口) 用于感知外部信号(输入模式)和控制外部设备&…...
汽车免拆诊断案例 | 2019款大众途观L车鼓风机偶尔不工作
故障现象 一辆2019款大众途观L车,搭载DKV发动机和0DE双离合变速器,累计行驶里程约为8万km。车主进厂反映,鼓风机偶尔不工作。 故障诊断 接车后试车,鼓风机各挡位均工作正常。用故障检测仪检测,空调控制单元&#x…...
