怎样实现一个 Linux 块设备驱动?涉及到哪些关键的数据结构和函数?

weixin_32310361 2025-04-17 10:35:56

怎样实现一个 Linux 块设备驱动?涉及到哪些关键的数据结构和函数?

...全文
296 1 打赏 收藏 转发到动态 举报
AI 作业
写回复
用AI写文章
1 条回复
切换为时间正序
请发表友善的回复…
发表回复
  • 打赏
  • 举报
回复

在 Linux 内核中实现一个块设备驱动是一个相对复杂的过程,下面为你详细介绍实现步骤、涉及的关键数据结构和函数。

实现步骤

1. 初始化模块

编写模块的初始化和退出函数,在初始化函数里进行块设备驱动的注册等操作,在退出函数中执行注销操作。

2. 分配设备号

使用 alloc_chrdev_regionregister_blkdev 来分配块设备的主设备号。

3. 定义块设备操作结构体

该结构体包含了一系列函数指针,这些函数定义了对块设备的各种操作,如打开、释放、请求处理等。

4. 注册块设备

调用 register_blkdev 函数注册块设备,将块设备操作结构体与设备号关联起来。

5. 实现请求处理函数

这个函数负责处理对块设备的读写请求,通常会涉及到与底层存储介质的交互。

6. 卸载模块

在模块退出时,注销块设备并释放设备号。

关键数据结构

1. struct gendisk

这是一个核心的数据结构,用于表示一个通用的磁盘设备。它包含了设备的各种信息,如设备号、分区表、块设备操作结构体等。

struct gendisk {
    int major;           // 主设备号
    int first_minor;     // 第一个次设备号
    int minors;          // 次设备号的数量
    char disk_name[32];  // 设备名称
    struct hd_struct **part;  // 分区表
    struct block_device_operations *fops;  // 块设备操作结构体
    // 其他成员...
};

2. struct block_device_operations

该结构体定义了对块设备的各种操作函数,如 openreleaseioctl 等。

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);
    // 其他操作函数...
};

3. struct request_queue

它代表了一个请求队列,用于存储对块设备的读写请求。驱动程序需要从这个队列中取出请求并进行处理。

struct request_queue {
    struct elevator_queue *elevator;  // 电梯调度器
    void (*request_fn)(struct request_queue *q);  // 请求处理函数
    // 其他成员...
};

4. struct request

该结构体表示一个对块设备的读写请求,包含了请求的起始扇区、扇区数量、数据缓冲区等信息。

struct request {
    sector_t sector;         // 起始扇区
    unsigned int nr_sectors; // 扇区数量
    struct bio *bio;         // 块 I/O 描述符
    // 其他成员...
};

关键函数

1. alloc_disk

用于分配一个 struct gendisk 结构体实例。

struct gendisk *alloc_disk(int minors);

2. add_disk

将一个 struct gendisk 结构体实例添加到内核中,使其成为一个可用的块设备。

void add_disk(struct gendisk *disk);

3. blk_init_queue

初始化一个请求队列,并指定请求处理函数。

struct request_queue *blk_init_queue(request_fn_proc *rfn, spinlock_t *lock);

4. blk_queue_make_request

设置请求队列的 make_request_fn 函数,用于处理块 I/O 请求。

void blk_queue_make_request(struct request_queue *q, make_request_fn *mfn);

5. register_blkdev

注册一个块设备,返回分配的主设备号。

int register_blkdev(unsigned int major, const char *name);

6. unregister_blkdev

注销一个块设备,释放主设备号。

int unregister_blkdev(unsigned int major, const char *name);

示例代码

以下是一个简单的块设备驱动示例:

#include <linux/module.h>
#include <linux/init.h>
#include <linux/blkdev.h>

#define MY_BLOCK_MAJOR 0
#define MY_BLOCK_NAME "my_block_dev"
#define MY_BLOCK_SIZE (1024 * 1024)

static struct gendisk *my_disk;
static struct request_queue *my_queue;
static spinlock_t my_lock;
static char *my_data;

static void my_request(struct request_queue *q) {
    struct request *req;

    while ((req = blk_fetch_request(q)) != NULL) {
        if (!blk_fs_request(req)) {
            __blk_end_request_all(req, -EIO);
            continue;
        }

        // 处理请求,这里简单打印信息
        printk(KERN_INFO "Processing request: sector %llu, nr_sectors %u\n",
               (unsigned long long)blk_rq_pos(req), blk_rq_sectors(req));

        __blk_end_request_all(req, 0);
    }
}

static struct block_device_operations my_fops = {
    .owner = THIS_MODULE,
};

static int __init my_block_init(void) {
    int ret;

    spin_lock_init(&my_lock);
    my_queue = blk_init_queue(my_request, &my_lock);
    if (!my_queue) {
        printk(KERN_ERR "Failed to initialize request queue\n");
        return -ENOMEM;
    }

    my_disk = alloc_disk(1);
    if (!my_disk) {
        printk(KERN_ERR "Failed to allocate gendisk\n");
        blk_cleanup_queue(my_queue);
        return -ENOMEM;
    }

    my_data = vmalloc(MY_BLOCK_SIZE);
    if (!my_data) {
        printk(KERN_ERR "Failed to allocate data buffer\n");
        put_disk(my_disk);
        blk_cleanup_queue(my_queue);
        return -ENOMEM;
    }

    my_disk->major = register_blkdev(MY_BLOCK_MAJOR, MY_BLOCK_NAME);
    if (my_disk->major < 0) {
        printk(KERN_ERR "Failed to register block device\n");
        vfree(my_data);
        put_disk(my_disk);
        blk_cleanup_queue(my_queue);
        return my_disk->major;
    }

    my_disk->first_minor = 0;
    my_disk->fops = &my_fops;
    my_disk->queue = my_queue;
    sprintf(my_disk->disk_name, MY_BLOCK_NAME);
    set_capacity(my_disk, MY_BLOCK_SIZE / 512);

    add_disk(my_disk);

    printk(KERN_INFO "My block device driver initialized\n");
    return 0;
}

static void __exit my_block_exit(void) {
    del_gendisk(my_disk);
    put_disk(my_disk);
    unregister_blkdev(my_disk->major, MY_BLOCK_NAME);
    vfree(my_data);
    blk_cleanup_queue(my_queue);
    printk(KERN_INFO "My block device driver exited\n");
}

module_init(my_block_init);
module_exit(my_block_exit);
MODULE_LICENSE("GPL");

这个示例代码展示了一个简单的块设备驱动的实现,包括设备的初始化、请求处理和模块的退出操作。在实际应用中,你需要根据具体的需求对请求处理函数进行更详细的实现,以完成对块设备的读写操作。

4,662

社区成员

发帖
与我相关
我的任务
社区描述
本论坛以AI、WoS 、XR、IoT、Auto、生成式AI等核心板块组成,为开发者提供便捷及高效的学习和交流平台。 高通开发者专区主页:https://qualcomm.csdn.net/
人工智能物联网机器学习 技术论坛(原bbs) 北京·东城区
社区管理员
  • csdnsqst0050
  • chipseeker
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧