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

weixin_32310361 2025-04-17 10:35:56

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

...全文
236 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");

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

本PDF电子书包含上下两册,共1576页,带目录,高清非扫描版本。 作者: 毛德操 胡希明 丛书名: Linux内核源代码情景分析 出版社:浙江大学出版社 目录 第1章 预备知识 1.1 Linux内核简介. 1.2 Intel X86 CPU系列的寻址方式 1.3 i386的页式内存管理机制 1.4 Linux内核源代码中的C语言代码 1.5 Linux内核源代码中的汇编语言代码 第2章 存储管理 2.1 Linux内存管理的基本框架 2.2 地址映射的全过程 2.3 几个重要的数据结构函数 2.4 越界访问 2.5 用户堆栈的扩展 2.6 物理页面的使用和周转 2.7 物理页面的分配 2.8 页面的定期换出 2.9 页面的换入 2.10 内核缓冲区的管理 2.11 外部设备存储空间的地址映射 2.12 系统调用brk() 2.13 系统调用mmap() 第3章 中断、异常和系统调用 3.1 X86 CPU对中断的硬件支持 3.2 中断向量表IDT的初始化 3.3 中断请求队列的初始化 3.4 中断的响应和服务 3.5 软中断与Bottom Half 3.6 页面异常的进入和返回 3.7 时钟中断 3.8 系统调用 3.9 系统调用号与跳转表 第4章 进程与进程调度 4.1 进程四要素 4.2 进程三部曲:创建、执行与消亡 4.3 系统调用fork()、vfork()与clone() 4.4 系统调用execve() 4.5 系统调用exit()与wait4() 4.6 进程的调度与切换 4.7 强制性调度 4.8 系统调用nanosleep()和pause() 4.9 内核中的互斥操作 第5章 文件系统 5.1 概述 5.2 从路径名到目标节点 5.3 访问权限与文件安全性 5.4 文件系统的安装和拆卸 5.5 文件的打开与关闭 5.6 文件的写与读 5.7 其他文件操作 5.8 特殊文件系统/proc 第6章 传统的Unix进程间通信 6.1 概述 6.2 管道和系统调用pipe() 6.3 命名管道 6.4 信号 6.5 系统调用ptrace()和进程跟踪 6.6 报文传递 6.7 共享内存 6.8 信号量 第7章基于socket的进程间通信 7.1系统调用socket() 7.2函数sys—socket()——创建插口 7.3函数sys—bind()——指定插口地址 7.4函数sys—listen()——设定server插口 7.5函数sys—accept()——接受连接请求 7.6函数sys—connect()——请求连接 7.7报文的接收与发送 7.8插口的关闭 7.9其他 第8章设备驱动 8.1概述 8.2系统调用mknod() 8.3可安装模块 8.4PCI总线 8.5块设备驱动 8.6字符设备驱动概述 8.7终端设备与汉字信息处理 8.8控制台的驱动 8.9通用串行外部总线USB 8.10系统调用select()以及异步输入/输出 8.11设备文件系统devfs 第9章多处理器SMP系统结构 9.1概述 9.2SMP结构中的互斥问题 9.3高速缓存与内存的一致性 9.4SMP结构中的中断机制 9.5SMP结构中的进程调度 9.6SMP系统的引导 第10章系统引导和初始化 10.1系统引导过程概述 10.2系统初始化(第一阶段) 10.3系统初始化(第二阶段) 10.4系统初始化(第三阶段) 10.5系统的关闭和重引导

2,856

社区成员

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

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