vfs
到实际文件系统ext4,最终需要经过通用块设备层,实际文件系统传递过来的IO操作是通过submit_bio
将bio
结构传递给通用块设备层。submit_bio
将bio请求到磁盘request请求的转换(请求的合并和IO优化),并将request请求挂入到磁盘请求的队列中,然后进行处理。noop(先来先处理)
、cfq(按照每个进程的IO请求的公平原则,基于数据量原则)
、deadline(基于最小寻道时间来处理请求,基于时延的方式)
,每一种的IO调度针对不同的应用场景的workload会有不同的效果/**
* submit_bio - 为 I/O 向块设备层提交 bio
* @bio: &struct bio 描述 I/O
*
* submit_bio() 的目的与 generic_make_request() 非常相似,并且
* 使用它功能来完成大部分工作。两者都是相当粗糙的
* 接口;@bio 必须预先设置并准备好进行 I/O。
*
*/
blk_qc_t submit_bio ( struct bio * bio)
{
/*
* 如果是常规读/写或附加数据的屏障,
* 在提交之前先进行正常的会计处理。
*/
if ( bio_has_data (bio)) {
无符号 整数计数;
if (不太可能( bio_op (bio) == REQ_OP_WRITE_SAME))
计数= queue_logical_block_size (bio -> bi_disk -> queue) >> 9 ;
否则
计数= bio_sectors (bio);
if ( op_is_write ( bio_op (bio))) {
count_vm_events (PGPGOUT, count);
} else {
task_io_account_read (bio -> bi_iter.bi_size);
count_vm_events(PGPGIN,计数);
}
if (不太可能(block_dump)) {
char b[BDEVNAME_SIZE];
printk (KERN_DEBUG "%s(%d): %s block %Lu on %s (%u 个扇区)\n" ,
current -> comm, task_pid_nr (current),
op_is_write ( bio_op (bio)) ? "WRITE" : "READ" ,
( unsigned long long )bio -> bi_iter.bi_sector,
bio_devname (bio, b), count);
}
}
返回 generic_make_request (bio);
}
struct block_device
:块设备对于整个内核有三个作用,第一,对物理硬盘进行分割;第二,向上与文件系统进行交互;第三,向下与物理磁盘的枢纽。块设备通过struct block_device->bd_inode
指向类似于/dev/sda1
这样的关联索引节点。在设备驱动层看来,每个struct block_device
代表一个块设备,事实上磁盘才是块设备核心.当struct block_device
是一个分区上的块设备时候,bd_contains
指向包含整个磁盘设备的对应的那个struct block_device
.struct gendisk
:每一个物理磁盘只有唯一个struct gendisk
结构,磁盘上的每一个分区struct hd_struct
对应一个块设备struct block_device
数据结构。struct hd_struct
:用于联系逻辑分区和物理磁盘建立的映射关系,最核心记录是记录该分区中第一个扇区在物理磁盘中的位置和该分区占用的连续的扇区数// 块设备的结构表示
struct block_device {
dev_t bd_dev; /* 不是 kdev_t - 它是搜索键 */
// 打开的次数
int bd_openers;
// 设备的inode,比如/dev/sda1
struct inode * bd_inode;
// 设备所在文件系统的超级块
结构 super_block * bd_super;
// 互斥锁
struct mutex bd_mutex; /* 打开/关闭互斥锁 */
// 申请该设备者
void * bd_claiming;
// 该持有设备者
void * bd_holder;
// 设备持持者;
int bd_holders
// 是否是写持有
布尔 bd_write_holder;
# ifdef CONFIG_SYSFS
struct list_head bd_holder_disks;
# endif
// 如果是空,则指向整个物理磁盘设备的块设备;否则为
struct block_device * bd_contains;
// 块大小,以字节为单位
unsigned bd_block_size;
u8 bd_partno;
//如果是分区则指向的分区
struct hd_struct * bd_part;
// 磁盘引用次数
unsigned bd_part_count;
int bd_invalidated;
// 指向整个磁盘设备
struct gendisk * bd_disk;
// 本设备的UIO请求
struct request_queue * bd_queue;
结构 backing_dev_info * bd_bdi;
结构 list_head bd_list;
/*
* 私有数据。您必须 bd_claim'ed block_device
* 才能使用它。注意:bd_claim 允许所有者
多次声明同一设备,所有者必须特别
注意不要在这种情况下弄乱 bd_private。
*/
无符号 长 bd_private;
/* 冻结进程的计数器 */
int bd_fsfreeze_count;
/* 用于冻结的互斥体 */
struct mutex bd_fsfreeze_mutex;
} __randomize_layout;
// 磁盘物理的标识
struct gendisk{
//主设备号
int major;
// 与磁盘关联的第一个次设备号
int first_minor;
//磁盘标准名称
char disk_name[DISK_NAME_LEN];
// 获取磁盘的devnode函数
char * ( * devnode)( struct gendisk * gd, umode_t * mode);
//磁盘的分区表,可以包含多个struct hd_struct分区
struct disk_part_tbl __rcu * part_tbl;
// 分区表中第0个分区
struct hd_struct part0;
// 块设备的操作指针表
const struct block_device_operations * fops;
// 请求机会
结构 request_queue *队列;
无效 *私有数据;
//磁盘状态
int flags;
结构 rw_semaphore lookup_sem;
结构 kobject * slave_dir;
结构 timer_rand_state *随机;
atomic_t sync_io; /* RAID */
struct disk_events * ev;
# ifdef CONFIG_BLK_DEV_INTEGRITY
struct kobject integrity_kobj;
# endif /* CONFIG_BLK_DEV_INTEGRITY */
int node_id;
结构 坏块 * bb;
结构 lockdep_map lockdep_map;
};
// 一个磁盘分区的磁盘
结构 号{
// 该磁盘分区的结构_ 结构号的
标识
//这个地方有哪些的
第一个sector_sector_t
seqcount_t nr_sects_seq;
扇区_t对齐_偏移;
无符号 整数丢弃对齐;
结构 设备__dev;
结构 kobject * holder_dir;
诠释政策,partno;
结构 partition_meta_info *信息;
# ifdef CONFIG_FAIL_MAKE_REQUEST
intmake_it_fail;
# endif
无符号 长标记;
atomic_t in_flight[ 2 ];
# ifdef CONFIG_SMP
struct disk_stats __percpu * dkstats;
# else
struct disk_stats dkstats;
# endif
struct percpu_ref ref;
结构 rcu_work rcu_work;
};