1. 块设备与字符设备的的不同:
a. 块设备以块为单位进行输入和返回输出,字符设备以字节为单位。
b. 块设备对于I/O请求 有对应的缓冲区,字符设备无须缓冲,可以直接读写。
c. 字符设备只能被顺序读写,块设备可以随机访问。
2. block_device_operation, 类似于char设备的file_operations 结构体,它是对块设备操作的集合。
include/linux/blkdev.h
struct block_device_operations {
int (*open) (struct block_device *, fmode_t);
int (*release) (struct gendisk *, fmode_t); //打开和关闭块设备
int (*ioctl) (struct block_device *, fmode_t, unsigned, unsigned long);
int (*compat_ioctl) (struct block_device *, fmode_t, unsigned, unsigned long);
int (*direct_access) (struct block_device *, sector_t,
void **, unsigned long *);
int (*media_changed) (struct gendisk *); //检查设备中内容是否已经改变。
void (*unlock_native_capacity) (struct gendisk *);
int (*revalidate_disk) (struct gendisk *); //如果块设备内容改变了,发生响应,以使新内容准备好。
int (*getgeo)(struct block_device *, struct hd_geometry *); //根据块设备的几何信息,填充一个hd_geomoetry结构体,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; //指向拥有该结构体的模块,通常被初始化为“THIS_MODULE”
};
-
3. 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; /* major number of driver */ int first_minor; //第一个次设备号 int minors; /* maximum number of minors, =1 for * disks that can't be partitioned. */ char disk_name[DISK_NAME_LEN]; /* name of major driver */ //设备名称 char *(*devnode)(struct gendisk *gd, mode_t *mode); /* 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; const struct block_device_operations *fops; //块设备操作集合 struct request_queue *queue; //内核管理这个设备的I/O请求队列的指针 void *private_data; //可用于指向磁盘的任何私有数据,用法与字符设备驱动file结构体的private_data类似 int flags; struct device *driverfs_dev; // FIXME: remove struct kobject *slave_dir; //可被引用计数的结构体 struct timer_rand_state *random; atomic_t sync_io; /* RAID */ struct work_struct async_notify; #ifdef CONFIG_BLK_DEV_INTEGRITY struct blk_integrity *integrity; #endif int node_id; };
- linux内核提供了一组函数来操作gendisk。
1. 分配gendisk.
struct gendisk *alloc_disk(int minors);
gendisk结构体是一个动态分配的结构体,需要特别的内核操作来初始化,驱动不能自己分配这个结构体。
struct gendisk *alloc_disk(int minors) //minor就是这个磁盘使用的次设备号的数量,一般也就是磁盘分区的数量,此后minors不能修改。
{
return alloc_disk_node(minors, -1); //实际调用了这个
}
EXPORT_SYMBOL(alloc_disk);
struct gendisk *alloc_disk_node(int minors, int node_id)
{
struct gendisk *disk;
disk = kmalloc_node(sizeof(struct gendisk),
GFP_KERNEL | __GFP_ZERO, node_id);
if (disk) {
if (!init_part_stats(&disk->part0)) {
kfree(disk);
return NULL;
}
disk->node_id = node_id;
if (disk_expand_part_tbl(disk, 0)) {
free_part_stats(&disk->part0);
kfree(disk);
return NULL;
}
disk->part_tbl->part[0] = &disk->part0;
disk->minors = minors;
rand_initialize_disk(disk);
disk_to_dev(disk)->class = &block_class;
disk_to_dev(disk)->type = &disk_type;
device_initialize(disk_to_dev(disk));
INIT_WORK(&disk->async_notify,
media_change_notify_thread);
}
return disk;
}
-
2.增加gendisk
void add_disk(struct gendisk *gd);
gendisk结构体被分配以后,系统还不能使用这个磁盘,需要调用add_disk()来注册这个磁盘设备。add_disk()的调用必须发生在驱动程序初始化工作完成,并能响应磁盘的请求之后。
/**
* add_disk - add partitioning information to kernel list
* @disk: per-device partitioning information
*
* This function registers the partitioning information in @disk
* with the kernel.
*
* FIXME: error handling
*/
void add_disk(struct gendisk *disk)
{
struct backing_dev_info *bdi;
dev_t devt;
int retval;
/* minors == 0 indicates to use ext devt from part0 and should
* be accompanied with EXT_DEVT flag. Make sure all
* parameters make sense.
*/
WARN_ON(disk->minors && !(disk->major || disk->first_minor));
WARN_ON(!disk->minors && !(disk->flags & GENHD_FL_EXT_DEVT));
disk->flags |= GENHD_FL_UP;
retval = blk_alloc_devt(&disk->part0, &devt);
if (retval) {
WARN_ON(1);
return;
}
disk_to_dev(disk)->devt = devt;
/* ->major and ->first_minor aren't supposed to be
* dereferenced from here on, but set them just in case.
*/
disk->major = MAJOR(devt);
disk->first_minor = MINOR(devt);
/* Register BDI before referencing it from bdev */
bdi = &disk->queue->backing_dev_info;
bdi_register_dev(bdi, disk_devt(disk));
blk_register_region(disk_devt(disk), disk->minors, NULL,
exact_match, exact_lock, disk);
register_disk(disk);
blk_register_queue(disk);
retval = sysfs_create_link(&disk_to_dev(disk)->kobj, &bdi->dev->kobj,
"bdi");
WARN_ON(retval);
}
EXPORT_SYMBOL(add_disk);
EXPORT_SYMBOL(del_gendisk); /* in partitions/check.c */
-
3.释放gendisk
void del_gendisk(struct gendisk *gd); void del_gendisk(struct gendisk *disk) { struct disk_part_iter piter; struct hd_struct *part; /* invalidate stuff */ disk_part_iter_init(&piter, disk, DISK_PITER_INCL_EMPTY | DISK_PITER_REVERSE); while ((part = disk_part_iter_next(&piter))) { invalidate_partition(disk, part->partno); delete_partition(disk, part->partno); } disk_part_iter_exit(&piter); invalidate_partition(disk, 0); blk_free_devt(disk_to_dev(disk)->devt); set_capacity(disk, 0); disk->flags &= ~GENHD_FL_UP; unlink_gendisk(disk); part_stat_set_all(&disk->part0, 0); disk->part0.stamp = 0; kobject_put(disk->part0.holder_dir); kobject_put(disk->slave_dir); disk->driverfs_dev = NULL; if (!sysfs_deprecated) sysfs_remove_link(block_depr, dev_name(disk_to_dev(disk))); device_del(disk_to_dev(disk)); }
4. gendisk的引用计数。
gendisk中的kobject成员,它是一个可被引用计数的结构体,通过get_disk()和put_disk()函数可用来操作引用计数。
struct kobject *get_disk(struct gendisk *disk) { struct module *owner; struct kobject *kobj; if (!disk->fops) return NULL; owner = disk->fops->owner; if (owner && !try_module_get(owner)) return NULL; kobj = kobject_get(&disk_to_dev(disk)->kobj); if (kobj == NULL) { module_put(owner); return NULL; } return kobj; }
EXPORT_SYMBOL(get_disk); void put_disk(struct gendisk *disk) { if (disk) kobject_put(&disk_to_dev(disk)->kobj); } EXPORT_SYMBOL(put_disk);
- 5. 设置gendisk容量。
void set_capacity(struct gendisk *disk, sector_t size);
块设备中最小的可寻址单元是扇区,扇区大小一般是2的整数倍,最常见的大小是512字节.
扇区的大小是设备的物理属性,扇区是所有块设备的基本单元,块设备无法对比扇区还小的单元进行寻址和操作。
内核与块设备驱动交互的扇区都以512字节为单位,setc_capacity也是。
static inline sector_t get_capacity(struct gendisk *disk) { return disk->part0.nr_sects; } static inline void set_capacity(struct gendisk *disk, sector_t size) { disk->part0.nr_sects = size; }