Linux内核中的块设备驱动详解

张开发
2026/4/11 6:41:14 15 分钟阅读

分享文章

Linux内核中的块设备驱动详解
Linux内核中的块设备驱动详解引言块设备是Linux系统中用于存储和访问数据的重要设备类型与字符设备不同块设备以固定大小的数据块为单位进行数据交换。本文将深入探讨Linux内核块设备驱动的架构、编写方法和核心机制。块设备驱动架构1. 层次结构用户进程 ↓ 文件系统VFS ↓ 通用块层bio、request ↓ 块设备驱动 ↓ 硬件设备2. 核心组件gendisk通用磁盘结构表示一个磁盘设备request_queue请求队列管理IO请求bio块IO描述符描述单个IO操作requestIO请求由多个bio组成gendisk结构1. 结构定义#include linux/genhd.h struct gendisk { int major; // 主设备号 int first_minor; // 首个次设备号 int minors; // 次设备号数量 char disk_name[DISK_NAME_LEN]; // 磁盘名称 struct disk_part_tbl __rcu *part_tbl; // 分区表 struct hd_struct part0; const struct block_device_operations *fops; // 块设备操作 struct request_queue *queue; // 请求队列 void *private_data; // 私有数据 sector_t capacity; // 扇区数 int flags; struct device *driverfs_dev; // 其他字段... };2. block_device_operations#include linux/blkdev.h struct block_device_operations { int (*open)(struct block_device *, fmode_t); void (*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 *); int (*revalidate_disk)(struct gendisk *); int (*getgeo)(struct block_device *, struct hd_geometry *); // 其他字段... };请求队列1. 请求队列操作#include linux/blkdev.h // 分配请求队列 struct request_queue *blk_alloc_queue(int flags); struct request_queue *blk_mq_init_queue(struct blk_mq_tag_set *tag_set); // 设置请求处理函数 void blk_queue_make_request(struct request_queue *q, make_request_fn *mfn); // 设置队列参数 void blk_queue_logical_block_size(struct request_queue *q, unsigned short size); void blk_queue_physical_block_size(struct request_queue *q, unsigned short size); void blk_queue_max_hw_sectors(struct request_queue *q, unsigned int max); void blk_queue_max_segments(struct request_queue *q, unsigned short max);2. 请求处理函数typedef void (make_request_fn)(struct request_queue *q, struct bio *bio); void my_make_request(struct request_queue *q, struct bio *bio) { struct block_device *bdev bio-bi_bdev; struct gendisk *disk bdev-bd_disk; // 处理bio bio_endio(bio); }bio结构1. bio定义#include linux/bio.h struct bio { struct bio *bi_next; // 下一个bio struct block_device *bi_bdev; // 块设备 unsigned int bi_opf; // 操作标志 unsigned int bi_vcnt; // bio_vec数量 unsigned int bi_max_vecs; // 最大bio_vec数量 unsigned int bi_comp_cpu; atomic_t bi_cnt; struct bio_vec *bi_io_vec; // bio_vec数组 bio_end_io_t *bi_end_io; // 完成回调 void *bi_private; struct bvec_iter bi_iter; sector_t bi_sector; // 起始扇区 unsigned int bi_size; // 大小 // 其他字段... };2. bio_vecstruct bio_vec { struct page *bv_page; // 页面 unsigned int bv_len; // 长度 unsigned int bv_offset; // 偏移 };3. bio操作#include linux/bio.h // 创建bio struct bio *bio_alloc(gfp_t gfp_mask, unsigned int nr_vecs); // 添加page到bio int bio_add_page(struct bio *bio, struct page *page, unsigned int len, unsigned int offset); // 提交bio void submit_bio(struct bio *bio); // 获取bio信息 struct page *bio_page(struct bio *bio); unsigned int bio_offset(struct bio *bio); sector_t bio_sector(struct bio *bio);请求处理1. 请求结构#include linux/blkdev.h struct request { struct request_queue *q; struct list_head queuelist; struct call_single_data csd; sector_t __sector; // 起始扇区 unsigned int __data_len; // 数据长度 struct bio *bio; struct bio *biotail; unsigned short nr_phys_segments; unsigned long atomic_write_count; struct request_list *rl; // 其他字段... };2. 请求完成// bio完成回调 void my_bio_endio(struct bio *bio) { if (bio-bi_status) { printk(KERN_ERR IO error: %d\n, bio-bi_status); } bio_put(bio); } // request完成 void blk_mq_end_request(struct request *rq, blk_status_t status);简单块设备驱动示例1. 完整驱动代码#include linux/module.h #include linux/kernel.h #include linux/init.h #include linux/fs.h #include linux/blkdev.h #include linux/bio.h #include linux/hdreg.h #define SECTOR_SIZE 512 #define NSECTORS 1024 #define DEV_NAME myblock static int major; static struct gendisk *my_disk; static struct request_queue *my_queue; static spinlock_t lock; static char *my_storage; static void my_request(struct request_queue *q) { struct request *req; while ((req blk_fetch_request(q)) ! NULL) { struct bio *bio; __rq_for_each_bio(bio, req) { struct bio_vec bvec; struct bvec_iter iter; bio_for_each_segment(bvec, bio, iter) { void *buffer kmap_atomic(bvec.bv_page) bvec.bv_offset; if (rq_data_dir(req) READ) { memcpy(buffer, my_storage iter.bi_sector * SECTOR_SIZE, bvec.bv_len); } else { memcpy(my_storage iter.bi_sector * SECTOR_SIZE, buffer, bvec.bv_len); } kunmap_atomic(buffer); } } blk_end_request_all(req, 0); } } static int my_open(struct block_device *bdev, fmode_t mode) { printk(KERN_INFO %s: opened\n, DEV_NAME); return 0; } static void my_release(struct gendisk *disk, fmode_t mode) { printk(KERN_INFO %s: released\n, DEV_NAME); } static int my_ioctl(struct block_device *bdev, fmode_t mode, unsigned int cmd, unsigned long arg) { return 0; } static int my_getgeo(struct block_device *bdev, struct hd_geometry *geo) { geo-heads 16; geo-sectors 63; geo-cylinders NSECTORS / (16 * 63); return 0; } static struct block_device_operations my_fops { .open my_open, .release my_release, .ioctl my_ioctl, .getgeo my_getgeo, }; static int __init my_block_init(void) { int ret; my_storage kzalloc(NSECTORS * SECTOR_SIZE, GFP_KERNEL); if (!my_storage) return -ENOMEM; spin_lock_init(lock); my_queue blk_alloc_queue(GFP_KERNEL); if (!my_queue) { ret -ENOMEM; goto out_free_storage; } blk_queue_make_request(my_queue, NULL); blk_queue_logical_block_size(my_queue, SECTOR_SIZE); queue_flag_set_unlocked(QUEUE_FLAG_NONROT, my_queue); major register_blkdev(0, DEV_NAME); if (major 0) { ret major; goto out_free_queue; } my_disk alloc_disk(1); if (!my_disk) { ret -ENOMEM; goto out_unregister_blkdev; } my_disk-major major; my_disk-first_minor 0; my_disk-fops my_fops; my_disk-queue my_queue; my_disk-private_data NULL; strcpy(my_disk-disk_name, DEV_NAME); my_disk-capacity NSECTORS; add_disk(my_disk); printk(KERN_INFO %s: registered with major %d\n, DEV_NAME, major); return 0; out_unregister_blkdev: unregister_blkdev(major, DEV_NAME); out_free_queue: blk_cleanup_queue(my_queue); out_free_storage: kfree(my_storage); return ret; } static void __exit my_block_exit(void) { del_gendisk(my_disk); put_disk(my_disk); blk_cleanup_queue(my_queue); unregister_blkdev(major, DEV_NAME); kfree(my_storage); printk(KERN_INFO %s: unregistered\n, DEV_NAME); } module_init(my_block_init); module_exit(my_block_exit); MODULE_LICENSE(GPL); MODULE_AUTHOR(Demo); MODULE_DESCRIPTION(Simple block device driver);分区管理1. 分区结构#include linux/genhd.h struct hd_struct { sector_t start_sect; sector_t nr_sects; sector_t alignment_offset; struct device __dev; struct kobject *holder_dir; int policy, partno; struct partition_meta_info *info; // 其他字段... };2. 添加分区#include linux/genhd.h int add_partition(struct gendisk *disk, int partno, sector_t start, sector_t len, int state, struct partition_meta_info *info);IO调度器1. 调度器类型noop最简单的调度器不做任何排序cfq完全公平队列调度器deadline最后期限调度器优化延迟mq-deadline多队列版本的deadlinebfq预算公平队列调度器2. 设置调度器# 查看可用调度器 cat /sys/block/sda/queue/scheduler # 设置调度器 echo deadline /sys/block/sda/queue/scheduler3. 调度器API#include linux/blkdev.h // 设置调度器 int elv_register_queue(struct request_queue *q, struct elevator_type *e); // 请求排序 void blk_insert_cloned_request(struct request_queue *q, struct request *rq);设备注册1. 主设备号#include linux/blkdev.h // 静态分配 #define DEV_MAJOR 200 // 动态分配 int major; major register_blkdev(0, DEV_NAME); // 动态分配 unregister_blkdev(major, DEV_NAME);2. 块设备注册#include linux/blkdev.h // gendisk分配 struct gendisk *alloc_disk(int minors); struct gendisk *blk_mq_alloc_disk(struct blk_mq_tag_set *tag_set, void *queuedata); // 注册/注销 void add_disk(struct gendisk *disk); void del_gendisk(struct gendisk *disk);错误处理1. bio错误static void my_bio_endio(struct bio *bio) { if (bio-bi_status) { printk(KERN_ERR Bio failed: %d\n, bio-bi_status); } bio_put(bio); }2. 请求错误blk_status_t status BLK_STS_OK; if (error) status BLK_STS_IOERR; blk_end_request_all(req, status);缓存和同步1. 缓存管理// 刷新缓存 blk_sync_queue(q); // 请求同步 blk_execute_rq(q, NULL, rq, 0);2. barrier请求// 添加barrier blk_queue_ordered(q, QUEUE_ORDERED_DRAIN, NULL);结论Linux块设备驱动开发涉及gendisk、request_queue、bio等核心结构需要理解块设备层次结构和IO处理流程。本文档介绍了块设备驱动的基础架构和编写方法开发者可以据此开发自己的块设备驱动。实际开发中还需要考虑性能优化、错误处理、电源管理等方面的内容。

更多文章