块设备
输入输出系统的设备包括字符设备和块设备。块设备也和字符设备类似,需要注册、打开、读写操作。
硬盘是最常见的块设备。块设备一般需要被格式化为文件系统,然后通过文件系统接口调用操作。
块设备也需要mknod到/dev/xxx下面,/dev 路径下面是 devtmpfs 文件系统。和字符设备一样,需要为/dev/xxx分配一个inode,inode 里面的 i_rdev 被设置成了块设备的设备号 dev_t。
1 |
|
这个inode是不能操作文件系统的, 我们需要把他mount到一个文件夹下面。如果这个块设备原来被格式化为一种文件系统的格式,例如 ext4,那我们调用的就是 ext4 相应的 mount 操作。ext4文件系统就是调用的ext4_mount函数,然后调用mount_bdev。mount_bdev主要就是做了2个工作:
- 调用blkdev_get_by_path 根据 /dev/xxx 这个名字,找到相应的设备并打开它;块设备是在这里打开的,不是调用的file_ops的函数.
- sget 根据打开的设备文件,填充 ext4 文件系统的 super_block
如果块设备分区了,分别格式化成不同的文件系统, 就有下面这张复杂的关系图了。
gendisk 只有一个实例,指向 /dev/sda。
1 |
|
struct disk_part_tbl 结构里是一个 struct hd_struct 的数组,用于表示各个分区。struct block_device_operations fops 指向对于这个块设备的各种操作。struct request_queue queue 是表示在这个块设备上的请求队列。struct hd_struct 是用来表示某个分区的,在上面的例子中,有两个 hd_struct 的实例,分别指向 /dev/sda1、 /dev/sda2。
1 |
|
在 hd_struct 中,比较重要的成员变量保存了如下的信息:从磁盘的哪个扇区开始,到哪个扇区结束。而 block_device 既可以表示整个块设备,也可以表示某个分区,所以对于上面的例子,block_device 有三个实例,分别指向 /dev/sda1、/dev/sda2、/dev/sda。
block_device 的成员变量 bd_disk,指向的 gendisk 就是整个块设备。这三个实例都指向同一个 gendisk。bd_part 指向的某个分区的 hd_struct,bd_contains 指向的是整个块设备的 block_device。
这个流程挺复杂的,但是归根到底都逃不出增删改查的操作,相互关联通过指针来关联,先注册回调函数,然后操作时调用回调。
行动,才不会被动!
欢迎关注个人公众号 微信 -> 搜索 -> fishmwei,沟通交流。