一、基本概念
磁盘(disk)是指利用磁记录技术存储数据的存储器。磁盘是计算机主要的存储介质, 可以存储大量的二进制数据,并且断电后也能保持数据不丢失。早期计算机使用的磁盘是软磁 盘(Floppy Disk,简称软盘),如今常用的磁盘是硬磁盘(Hard disk,简称硬盘)。
1、磁盘数据结构
一个弧道被划分成多个段,每个段就是一个扇区。
2、磁盘访问
sata硬盘实现的串行ATA协议,ATA下盘命令中记录有LBA(logic block address)起始地址和扇区数;
3、扇区
硬盘的基本访问单位,扇区的大小一般是512B。
4、块
扇区时硬件传输数据的基本单位,硬件一次传输一个扇区的数据到内存中。但是和扇区不同的是,块是虚拟文件系统传输数据的基本单位。Linux中块的大小必须是2的幂次方,但不能超过一个页大小(4K)。
5、段
主要为做scatter/gather DMA操作使用,同一个物理页面中在硬盘存储介质上连续的多个块组成一个段。段大小与块有关,必须是块的整数倍。块通常包含多个扇区。段包含多个块,物理段通常包括多个段;段在内核中由结构struct bio_vec来描述。
6、文件块
大小定义和文件系统一样是相对于文件的一个偏移逻辑块,需要通过具体文件系统中的文件对应inode所记录的间接块信息,换算成对应的文件系统块。
扇区由磁盘的物理特性所决定;块缓冲区由内核代码决定;块由缓冲区决定,是块缓冲区大小的整数倍(但是不能超过一页)
三者之间关系:扇区(512)<=块<=页(4096)块=n*扇区(n为整数)
段由多个块组成,一个段就是一个内存页。Linux系统一次读取磁盘的大小是一个块,页不是一个扇区,块设备驱动由此得名。
6、块设备与文件系统之间的关系
块设备处理过程涉及Linux内核中很多模块
块I/O调度层目的:将请求按照它们对应在块设备上的扇区号进行排列,以减少磁头移动,提升效率。
二、块设备详解
- 块设备I/O操作
块设备只能以块为单位接收输入和返回输出,而字符设备则以字节为单位;
块设备对于I/O请求有对应的缓冲区;
字符设备只能被顺序读写,而块设备可以随机访问。 - 块设备–源码分析(block_device 是伪文件系统bdevfs中对块设备或设备分区抽象,它唯一对应于一个设备号(对分区来讲,主设备号相同,次设备号不同))
struct block_device {
dev_t bd_dev; /* 对应底层设备的设备号 */
int bd_openers; //该设备同时被多少个进程打开
struct inode * bd_inode; /* 块设备的inode,可利用bd_dev通过bdget获得 */
struct super_block * bd_super; // 文件系统的超级块信息
struct mutex bd_mutex; /* open/close mutex */
void * bd_claiming;
void * bd_holder;
int bd_holders;
bool bd_write_holder;
#ifdef CONFIG_SYSFS
struct list_head bd_holder_disks;
#endif
struct block_device * bd_contains;
unsigned bd_block_size; //块大小
struct hd_struct * bd_part; // 指向分区指针,对于gendisk,指向内置的分区
/* number of times partitions within this device have been opened. */
unsigned bd_part_count; // 该设备的所以分区同时被打开的次数
int bd_invalidated;
struct gendisk * bd_disk;
struct request_queue * bd_queue;
struct backing_dev_info *bd_bdi;
struct list_head bd_list;
/*
* Private data. You must have bd_claim'ed the block_device
* to use this. NOTE: bd_claim allows an owner to claim
* the same device multiple times, the owner must take special
* care to not mess up bd_private for that case.
*/
unsigned long bd_private;
/* The counter of freeze processes */
int bd_fsfreeze_count;
/* Mutex for freeze */
struct mutex bd_fsfreeze_mutex;
};
- 通用磁盘–源码分析(gendisk,对于通用磁盘的描述,与真正的底层物理设备相关联)
struct gendisk {
/* major, first_minor and minors are input parameters only,
* don't use directly. Use disk_devt() and disk_max_parts().
*/
int major; /* 主设备号*/
int first_minor; // 第一个次设备号
int minors; /* 表示分区的个数,分区号从1开始,0表示gendisk本身 */
/* 磁盘名称,用于在sysfs和/proc/partitions中表示该磁盘*/
char disk_name[DISK_NAME_LEN]; /* name of major driver */
char *(*devnode)(struct gendisk *gd, umode_t *mode);
unsigned int events; /* supported events */
unsigned int async_events; /* async events, subset of all */
/* Array of pointers to partitions indexed by partno.
* Protected with matching bdev lock but stat and other
* non-critical accesses use RCU. Always access through
* helpers.
*/
struct disk_part_tbl __rcu *part_tbl; //分区表
struct hd_struct part0; // 表示gendisk本身
// 指向底层具体设备的操作函数
const struct block_device_operations *fops;
// 该磁盘关联的请求队列
struct request_queue *queue;
// 私有数据,用于提供用户驱动程序使用
void *private_data;
int flags;
struct kobject *slave_dir;
struct timer_rand_state *random;
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;
struct badblocks *bb;
};
- 磁盘分区–源码分析
struct hd_struct {
sector_t start_sect; // 该分区的起始扇区号
sector_t nr_sects; // 该分区的扇区个数,也就是分区容量
seqcount_t nr_sects_seq;
sector_t alignment_offset;
unsigned int discard_alignment;
struct device __dev;
struct kobject *holder_dir;
int policy, partno;
struct partition_meta_info *info;
#ifdef CONFIG_FAIL_MAKE_REQUEST
int make_it_fail;
#endif
unsigned long stamp;
atomic_t in_flight[2];
#ifdef CONFIG_SMP
struct disk_stats __percpu *dkstats;
#else
struct disk_stats dkstats;
#endif
struct percpu_ref ref;
struct rcu_head rcu_head;
};
- 块设备操作集合–源码分析
struct block_device_operations {
// 打开与释放
int (*open) (struct block_device *, fmode_t);
void (*release) (struct gendisk *, fmode_t);
int (*rw_page)(struct block_device *, sector_t, struct page *, bool);
// I/O 控制
int (*ioctl) (struct block_device *, fmode_t, unsigned, unsigned long);
int (*compat_ioctl) (struct block_device *, fmode_t, unsigned, unsigned long);
unsigned int (*check_events) (struct gendisk *disk,
unsigned int clearing);
/* ->media_changed() is DEPRECATED, use ->check_events() instead */
int (*media_changed) (struct gendisk *);
void (*unlock_native_capacity) (struct gendisk *);
int (*revalidate_disk) (struct gendisk *);
// 获取驱动器信息
int (*getgeo)(struct block_device *, struct hd_geometry *);
/* this callback is with swap_lock and sometimes page table lock held */
void (*swap_slot_free_notify) (struct block_device *, unsigned long);
// 模块指针
struct module *owner;
const struct pr_ops *pr_ops;
};