文件系统和磁盘的关系

发布于:2024-05-06 ⋅ 阅读:(30) ⋅ 点赞:(0)

ext4文件系统注册

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
通过register_filesystem函数将ext4_fs_type结构体挂载到file_systems全局链表中。

ext4文件系统mout挂载

在这里插入图片描述
1.struct file_system_type *fs_type:指向 file_system_type 结构的指针,表示要挂载的文件系统类型。在这里,它指向 ext4 文件系统类型的结构。
2.int flags:挂载选项标志。这是一个整数,包含了用于控制挂载行为的各种选项。例如,可以指定 MS_RDONLY 标志表示以只读方式挂载文件系统,或者使用 MS_NOATIME 标志表示不更新访问时间等。
3.const char *dev_name:要挂载的设备名称。它是一个指向表示设备路径或设备文件的字符串的指针。例如,“/dev/sdb1” 表示要挂载的设备是 /dev/sdb1。
4.void *data:用于传递挂载选项的数据结构。它可以是一个指向特定类型的结构的指针,用于指定更详细的挂载选项。在 ext4 文件系统中,通常将其设置为 NULL,表示不传递额外的挂载选项。
5.ext4_fill_super:一个函数指针,指向用于填充 ext4 超级块结构的函数。该函数在挂载过程中被调用,用于初始化文件系统的超级块和相关数据结构。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
1.函数首先检查传入的文件系统类型 fstype 是否为空,如果为空则返回 -EINVAL 表示参数无效。
2.接下来,函数调用 get_fs_type 函数获取给定文件系统类型的 file_system_type 结构。如果获取失败(返回 NULL),则返回 -ENODEV 表示文件系统类型无效。
3.如果文件系统类型具有子类型(FS_HAS_SUBTYPE 标志被设置),函数尝试提取子类型字符串。它在 fstype 字符串中查找第一个点号(‘.’)之后的子字符串作为子类型。如果找到子类型并且子类型不为空,则继续后续操作。否则,释放文件系统类型资源并返回 -EINVAL 表示参数无效。
4.函数调用 fs_context_for_mount 函数创建一个文件系统上下文 fs_context,用于进行挂载操作。它使用文件系统类型和超级块标志作为参数。如果创建失败,函数返回相应的错误代码。
5.函数检查当前进程是否具有执行挂载操作的权限。如果没有权限,函数返回 -EPERM 表示操作不允许。
6.如果前面的操作没有错误,函数调用 vfs_get_tree 函数,该函数负责获取文件系统树。
7.如果前面的操作没有错误,函数调用 do_new_mount_fc 函数,该函数执行实际的挂载操作。
8.释放文件系统上下文资源,调用 put_fs_context 函数。

其中
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在alloc_fs_context函数中最终会调用legacy_init_fs_context函数进行初始化,将操作函数集赋值为legacy_fs_context_ops。
在这里插入图片描述
vfs_get_tree函数中会调用ext4文件系统的回调函数ext4_mount。
在这里插入图片描述
在这里插入图片描述
其中blkdev_get_by_path为文件系统与硬件磁盘进行关联的核心函数,用于打开一个块设备。

磁盘探测

在这里插入图片描述
在这里插入图片描述

在drivers/scsi/sd.c的probe探测函数中首先会调用device_add(&sdkp->dev);注册设备,但是由于&sdkp->dev没有设置dev->devt所以在device_add函数中不会调用devtmpfs_create_node,即不会在/dev/目录下创建文件。
在这里插入图片描述
而由于error = sd_format_disk_name(“sd”, index, gd->disk_name, DISK_NAME_LEN);,所以gd->disk_name名字即为常见的sda、sdb。
在这里插入图片描述
在__alloc_disk_node函数即申请struct gendisk结构体中会赋值disk_to_dev(disk)->class = &block_class;。
在这里插入图片描述在这里插入图片描述

在后续device_add_disk(dev, gd, NULL);函数中会进一步调用register_disk函数。
在这里插入图片描述
而register_disk函数中又会进一步调用device_add创建/dev/设备文件。
在这里插入图片描述
在这里插入图片描述
上文说到register_disk函数注册块设备,由于前面申请结构体时已赋值了block_class所以会有req.mode |= S_IFBLK;。即最终会创建/dev/sda设备文件。

设备文件分区

在这里插入图片描述
在这里插入图片描述
应用层通过工具进行磁盘分区进而通过ioctl系统调用最终会调用到add_partition函数。struct hd_struct结构体是用来描述磁盘分区的即常见的/dev/sda1,/dev/sda2…等等。其原理为先通过dev_set_name(pdev, “%s%d”, dname, partno);确定分区名字,再通过err = device_add(pdev);进行/dev/设备文件的注册,由于pdev->class = &block_class;所以创建/dev设备文件时会确认为块设备。
在这里插入图片描述
start_sect:分区的起始扇区。
nr_sects:分区的扇区数量。在 32 位机器上,由于 sector_t 是 64 位类型,更新 nr_sects 可能不是原子操作。因此,通过序列计数器保护该字段。
nr_sects_seq:用于保护 nr_sects 的序列计数器(仅在 BITS_PER_LONG 为 32 且 CONFIG_SMP 定义时存在)。
stamp:时间戳,用于跟踪分区的更新。
dkstats:磁盘统计信息的指针,使用 percpu 变量。
ref:用于引用计数的 percpu 引用计数结构。
__dev:内核设备结构体。
holder_dir:持有者目录的 kobject。
read_only:分区是否为只读。
partno:分区号。
info:分区元信息的指针。
make_it_fail:用于配置请求失败的标志(仅在 CONFIG_FAIL_MAKE_REQUEST 启用时存在)。
rcu_work:用于延迟释放的 RCU 工作项。
stat_time:统计时间(KABI 兼容性保留字段)。
其中partno分区号即/dev/sda1,/dev/sda2等分区。

blkdev_get_by_path打开磁盘设备

在这里插入图片描述
在这里插入图片描述
在blkdev_get_by_path->lookup_bdev->bd_acquire->bdget函数中如果/dev/sda1分区对应的dev_t dev设备号没有对应的struct block_device块设备描述符则会通过bdget->iget5_locked函数创建该描述符并在bdev_set函数中将BDEV_I(inode)->bdev.bd_dev设置为/dev/sda1分区对应的dev_t dev设备号。
在这里插入图片描述
在blkdev_get->__blkdev_get->bdev_get_gendisk函数中即通过上文所述的dev_t dev设备号找到对应的磁盘描述符struct gendisk *disk。
在这里插入图片描述
major:驱动程序的主设备号。主设备号用于标识设备所使用的驱动程序。不同的驱动程序可以使用不同的主设备号。

first_minor:块设备的第一个次设备号。次设备号用于标识同一驱动程序下的不同设备。
minors:次设备号的数量。对于可以被分区的磁盘,minors 表示可用于分区的次设备号的数量。对于不可被分区的磁盘,如只有一个分区的磁盘,minors 的值为 1。
disk_name:主驱动程序的名称。这是一个字符数组,用于存储驱动程序的名称。例如,对于硬盘驱动程序,该字段可能包含 “sda”。
events:支持的事件标志位。这是一个无符号短整型(unsigned short),用于表示驱动程序支持的事件。
event_flags:与事件处理相关的标志位。这是一个无符号短整型(unsigned short),用于表示与事件处理相关的标志。
part_tbl:分区表的指针数组。分区表是一个 struct disk_part_tbl 结构体的指针数组,通过 partno 索引访问。分区表存储了块设备的分区信息。
part0:第一个分区的 hd_struct 结构体。hd_struct 是用于表示硬盘分区的数据结构。
user_ro_bitmap:用户只读位图。这是一个位图(bitmap),用于表示哪些分区是以只读方式挂载的。位图的长度由 DISK_MAX_PARTS 定义。
fops:指向 block_device_operations 结构体的指针。block_device_operations 结构体包含了与块设备相关的操作函数,例如读取、写入等操作。
queue:请求队列的指针。请求队列用于管理块设备的 I/O 请求。它维护了一个请求的队列,并负责协调和调度这些请求的处理。
private_data:私有数据指针。这是一个指针,供驱动程序使用,用于存储驱动程序需要的任何私有数据。
flags:标志位。这是一个整型变量,用于存储与块设备相关的标志信息。
state:状态位。这是一个无符号长整型(unsigned long),用于存储块设备的状态信息。
lookup_sem:查找信号量。这是一个读写信号量(rw_semaphore),用于保护查找操作的并发访问。
slave_dir:从设备目录的指针。这是一个指向从设备目录的指针,用于存储块设备的从设备目录信息。
random:随机数生成器状态指针。这是一个指向随机数生成器状态的指针,用于生成磁盘操作中需要的随机数。
sync_io_sectors:同步 I/O 扇区数。这是一个 atomic64_t 类型的变量,用于表示同步 I/O 操作期间传输的扇区数。通常在 RAID 系统中使用。
ev:磁盘事件指针。这是一个指向磁盘事件的指针,用于存储和处理与磁盘相关的事件。
integrity_kobj:完整性相关的内核对象指针。这是一个指向完整性相关内核对象的指针,用于存储与磁盘完整性检查相关的信息。
cdi:CD-ROM 设备信息结构体指针。这是一个指向 CD-ROM 设备信息结构体的指针,用于存储与 CD-ROM 设备相关的信息。
node_id:节点 ID。用于标识设备所在的节点。
bb:坏块信息结构体指针。这是一个指向坏块信息结构体的指针,用于存储与磁盘坏块相关的信息。
lockdep_map:用于锁依赖性的映射。这是一个 lockdep_map 结构体,用于存储与锁依赖性检查相关的信息。

创建超级块super_block

在这里插入图片描述
在这里插入图片描述

回到上文的mount_bdev函数通过 s = sget(fs_type, test_bdev_super, set_bdev_super, flags | SB_NOSEC,bdev);创建ext4文件系统对应的超级块struct super_block。并最终返回该超级快root根节点对应的目录项struct dentry。
在这里插入图片描述
然后在legacy_get_tree函数中赋值sb = root->d_sb;和fc->root = root;
在这里插入图片描述最后通过vfs_create_mount函数创建挂载描述符vfsmount并赋值超级块mnt_sb和根节点目录项mnt_root。
在这里插入图片描述
最后在do_add_mount->graft_tree函数中,将新的挂载点struct mount *newmnt添加到文件系统层次结构中,并建立与父挂载点和挂载点对象的关联。