首先实现块设备驱动需理解gendisk与request_queue的作用,1. gendisk描述设备信息并注册到系统;2. request_queue管理I/O请求并通过make_request处理bio;3. 每个bio包含多个段,驱动需遍历并完成数据拷贝;4. 模块卸载时按顺序释放资源;5. 编译后通过insmod加载并用mkfs、mount测试。

编写Linux块设备驱动需要理解内核中块I/O子系统的基本架构和数据流。块设备与字符设备不同,它以固定大小的数据块为单位进行读写,并支持随机访问。典型的块设备包括硬盘、SSD、U盘等。本文将带你一步步实现一个简单的内存模拟块设备驱动(RAM Disk),帮助你掌握Linux块设备驱动开发的核心要点。
Linux块设备驱动主要依赖于struct gendisk和struct request_queue两个核心结构体:
块设备不直接处理read/write系统调用,而是通过请求队列接收bio(block I/O)结构,由驱动完成数据搬运。
在模块初始化函数中,需分配并初始化请求队列和gendisk:
static struct request_queue *queue;
static struct gendisk *disk;
<p>static int __init my_blk_init(void)
{
// 分配请求队列
queue = blk_alloc_queue(GFP_KERNEL);
if (!queue)
return -ENOMEM;</p><pre class='brush:php;toolbar:false;'>// 设置请求处理函数
blk_queue_make_request(queue, my_make_request);
// 分配gendisk结构(1个分区)
disk = alloc_disk(1);
if (!disk) {
blk_cleanup_queue(queue);
return -ENOMEM;
}
disk->major = MY_BLK_MAJOR; // 主设备号
disk->first_minor = 0;
disk->fops = &my_blk_fops; // 文件操作(通常为空或仅占位)
disk->queue = queue;
strcpy(disk->disk_name, "myblk");
// 设置容量(以512字节扇区为单位)
set_capacity(disk, MY_BLK_SIZE / 512);
// 注册设备
add_disk(disk);
return 0;}
传统方式使用make_request函数逐个处理bio。每个bio代表一次I/O操作,可能包含多个段(segment):
static int my_make_request(struct request_queue *q, struct bio *bio)
{
struct bio_vec bvec;
sector_t sector = bio->bi_iter.bi_sector;
void *mem = myblk_data + (sector * 512); // 内存偏移
bool is_write = op_is_write(bio_op(&bio));
<pre class='brush:php;toolbar:false;'>if (sector * 512 + bio->bi_iter.bi_size > MY_BLK_SIZE) {
bio_endio(bio, -EIO);
return 0;
}
bio_for_each_segment(bvec, bio, iter) {
char *bdata = kmap_atomic(bvec.bv_page) + bvec.bv_offset;
if (is_write)
memcpy(mem, bdata, bvec.bv_len);
else
memcpy(bdata, mem, bvec.bv_len);
kunmap_atomic(bdata - bvec.bv_offset);
mem += bvec.bv_len;
}
bio_endio(bio, 0);
return 0;}
注意:现代内核推荐使用blk_mq_make_request配合多队列机制,但简单驱动仍可用传统方式。
模块卸载时必须释放申请的资源,顺序不能错:
static void __exit my_blk_exit(void)
{
del_gendisk(disk);
put_disk(disk);
blk_cleanup_queue(queue);
}
确保在del_gendisk后不再有新的I/O进入,避免空指针访问。
编写Makefile:
obj-m += myblk.o <p>KDIR := /lib/modules/$(shell uname -r)/build</p><p>all: $(MAKE) -C $(KDIR) M=$(PWD) modules</p><p>clean: $(MAKE) -C $(KDIR) M=$(PWD) clean</p>
编译后加载模块:
sudo insmod myblk.ko dmesg | tail # 查看设备号 lsblk # 应看到 myblk 设备 sudo mkfs.ext4 /dev/myblk sudo mount /dev/myblk /mnt echo "hello" > /mnt/test.txt
基本上就这些。从零写块设备驱动的关键是理解bio的处理流程和内存映射方式。虽然真实硬件驱动还需处理DMA、中断等,但内存模拟设备是学习的良好起点。调试时多用dmesg观察内核输出,逐步验证读写正确性。
以上就是Linux如何编写块设备驱动_LinuxBlockDriver开发教程的详细内容,更多请关注php中文网其它相关文章!
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号