Linux驱动-块设备驱动

news2025/4/17 5:38:11

Linux驱动-块设备驱动

  • 一,块设备驱动简介
  • 二,无请求队列情况(EMMC和SD卡等)
  • 三,请求队列情况(磁盘等带有I/O调度的设备)
  • 四,两者在驱动上区别

一,块设备驱动简介

  块设备驱动的关键结构在于:结构体gendisk,表示一个块设备,包含设备号,分区,操作函数集,相对于字符设备驱动区别在于:
在这里插入图片描述
  有I/O调度的块设备:(I/O调度意思就是在应用层对块设备进行操作时候,这个调度模块会把对块设备的操作转化为请求request,然后放到请求队列当中,因此在处理时候,主要是对请求队列中的bio请求进行操作),请求队列模式:通过 blk_init_queue 初始化请求队列,由调度器管理 request,再分发给驱动,如磁盘设备。
在这里插入图片描述

  没有I/O调度的设备:EMMC以及SD卡,当其使用时候,相当于直接对结构体bio进行操作,此时的bio不在request结构体中了,绕过传统的 I/O 调度器,每个 bio 直接传递给驱动处理,不再合并为 request,随机进行访问,不需要控制。

  对于块设备的操作,相当于存储设备操作,那么有三个要清除的地方:①物理存储器的地址在哪②内存中的地址在哪③存储长度是多少。由bio来控制,其中bi_sector即物理设备,比如EMMC,bio_vec即内存,比如RAM。
在这里插入图片描述

二,无请求队列情况(EMMC和SD卡等)

  其实这样操作感觉还是不太对劲,相当于在RAM开辟一段空间,来模拟EMMC或者SD卡,但EMMC和SD卡操作时候是和内存RAM进行交互,现在就变成了开辟的RAM和RAM进行操作…主要流程包括①块设备注册(因为是在RAM中开辟的空间,因此在disk_init中对2MB空间进行开辟,真实情况下,连接EMMC是不用开辟这段空间的,这个是模拟EMMC)②申请gendisk③设置自旋锁(无请求队列的起始用不到这个)④初始化请求队列⑤将请求队列和回调函数ram_queue_process关联起来⑥对gendisk中的数据初始化⑦设置gendisk块大小,并将其添加到内核中。

/*4.8日 块设备驱动*/
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/of_gpio.h>
#include <linux/semaphore.h>
#include <linux/timer.h>
#include <linux/i2c.h>
#include <linux/genhd.h>
#include <linux/blkdev.h>
#include <linux/hdreg.h>
#include <asm/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#define RAM_BLOCK_NAME  "mydisk11"
#define RAM_BLOCK_BYTE_SIZE  2*1024*1024   //2MB 字节数
#define RAM_BLOCK_SIZE  2*1024*1024/512   //2MB 即多少个块

struct disk_device{
    int  major;//块设备号
    struct gendisk  *ram_gendisk;
    spinlock_t my_spinlock;
    struct request_queue *queue; //请求队列
    unsigned char *ram_buf;
};
struct disk_device ram;

static int ram_open(struct block_device *dev, fmode_t mode)
{
    printk("ram_open \r\n");
    return 0;
}
static void ram_release(struct gendisk *disk, fmode_t mode)
{
    printk("ram_release \r\n");
}
int ram_getgeo(struct block_device *dev,struct hd_geometry *geo)
{
    return 0;
}
static struct block_device_operations ram_ops ={
    .owner = THIS_MODULE,
    .open = ram_open,
    .release = ram_release,
    .getgeo = ram_getgeo,
};

static void  ram_queue_process(struct request_queue *q,struct bio *bio)
{
    int offset = 0;
    unsigned long len = 0;//长度
    struct bio_vec ram_vec;//表示ram中的内存地址
    struct bvec_iter sd_emmc_iter;//连接的设备,比如emmc sd卡
    offset = (bio->bi_iter.bi_sector)*512; //磁盘的偏移地址
    bio_for_each_segment(ram_vec,bio,sd_emmc_iter)//遍历每一个bio
    {
        char *ptr = page_address(ram_vec.bv_page)+ram_vec.bv_offset;
        len = ram_vec.bv_len;
        if(bio_data_dir(bio)==READ)
        {
            memcpy(ptr,ram.ram_buf+offset,len);
        }else if(bio_data_dir(bio)==WRITE)
        {
            memcpy(ram.ram_buf+offset,ptr,len);
        }
        offset = offset + len;
    }
    set_bit(BIO_UPTODATE,&bio->bi_flags);
    bio_endio(bio,0);
    
}    
static int __init disk_init(void)
{
    int ret=0;
    /*1.申请ram内存*/
    ram.ram_buf = kzalloc(RAM_BLOCK_BYTE_SIZE,GFP_KERNEL);//申请字节大小
    if(ram.ram_buf ==NULL)
    {
        printk("kzalloc  error \r\n");
        ret = -1;
        goto kzalloc_ram_error;
    }
    printk("before ram.major = %d \r\n",ram.major);
    /*2.注册块设备*/
    ram.major = register_blkdev(0,RAM_BLOCK_NAME);
    if(ram.major < 0)
    {
        printk("register error \r\n");
        ret = -1;
        goto register_blkdev_error;
    }
    /*3.申请gendisk*/
    ram.ram_gendisk = alloc_disk(3);
    if(ram.ram_gendisk == NULL)
    {
        ret = -1;
        goto RAM_BLOCK_SIZE_error;
    }
    /*4.自旋锁*/
    spin_lock_init(&ram.my_spinlock);
    /*5.初始化请求队列*/
    ram.queue = blk_alloc_queue(GFP_KERNEL);
    if(ram.queue == NULL)
    {
        ret = -1;
        goto blk_queue_error;
    }
    /*制造设置请求函数*/
    blk_queue_make_request(ram.queue,ram_queue_process);
    /*6.给gendisk内值,初始化*/
    ram.ram_gendisk->major = ram.major; //主设备号
    ram.ram_gendisk->first_minor = 0;//次设备号开始
    ram.ram_gendisk->fops = &ram_ops;//操作函数
    ram.ram_gendisk->private_data = &ram; //私有数据
    ram.ram_gendisk->queue = ram.queue;//请求队列
    //memcpy(&ram.ram_gendisk->disk_name,&RAM_BLOCK_NAME,sizeof(RAM_BLOCK_NAME));
    //sprintf(ram.ram_gendisk->disk_name, RAM_BLOCK_NAME);
    snprintf(ram.ram_gendisk->disk_name, 32, "%s",RAM_BLOCK_NAME);
    set_capacity(ram.ram_gendisk,RAM_BLOCK_SIZE);//设置大小,即多少块
    add_disk(ram.ram_gendisk);//添加gendisk到内核
     printk("major = %d \r\n",ram.major);
    return ret;
blk_queue_error:   
    del_gendisk(ram.ram_gendisk);
RAM_BLOCK_SIZE_error:
    unregister_blkdev(ram.major,RAM_BLOCK_NAME);
register_blkdev_error:
    kfree(ram.ram_buf);
kzalloc_ram_error:
    return ret;

}
void __exit disk_exit(void)
{
    /*对gendisk进行操作*/
    del_gendisk(ram.ram_gendisk);
    //put_gendisk(ram.ram_gendisk);
    /*对ram.queue进行操作*/
    blk_cleanup_queue(ram.queue);
    /*对块设备进行操作*/
    unregister_blkdev(ram.major,RAM_BLOCK_NAME);
    /*释放开辟的空间*/
    kfree(ram.ram_buf);

}

module_init(disk_init);
module_exit(disk_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("WYT");

三,请求队列情况(磁盘等带有I/O调度的设备)

/*4.8日 块设备驱动*/
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/of_gpio.h>
#include <linux/semaphore.h>
#include <linux/timer.h>
#include <linux/i2c.h>
#include <linux/genhd.h>
#include <linux/blkdev.h>
#include <linux/hdreg.h>
#include <asm/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#define RAM_BLOCK_NAME  "mydisk11"
#define RAM_BLOCK_BYTE_SIZE  2*1024*1024   //2MB 字节数
#define RAM_BLOCK_SIZE  2*1024*1024/512   //2MB 即多少个块

struct disk_device{
    int  major;//块设备号
    struct gendisk  *ram_gendisk;
    spinlock_t my_spinlock;
    struct request_queue *queue; //请求队列
    unsigned char *ram_buf;
};
struct disk_device ram;

static int ram_open(struct block_device *dev, fmode_t mode)
{
    printk("ram_open \r\n");
    return 0;
}
static void ram_release(struct gendisk *disk, fmode_t mode)
{
    printk("ram_release \r\n");
}
int ram_getgeo(struct block_device *dev,struct hd_geometry *geo)
{
    return 0;
}
static struct block_device_operations ram_ops ={
    .owner = THIS_MODULE,
    .open = ram_open,
    .release = ram_release,
    .getgeo = ram_getgeo,
};

void  ram_queue_process(struct request_queue *q)
{
    int err = 0;
    unsigned long start = 0;
    unsigned long len = 0;
    void *buff = NULL;
    struct request *req;
    req = blk_fetch_request(q);//获取第一个请求,并把它移出
    while(req != NULL)
    {
        /*进行数据操作*/
        start = blk_rq_pos(req)*512;//起始地址
        len = blk_rq_cur_bytes(req);//大小,字节单位
        buff = bio_data(req->bio);//目的让bio中的数据和buff指针执指向一个位置
        if(rq_data_dir(req) == READ)//读操作
        {
            memcpy(buff,ram.ram_buf+start,len);//正常用磁盘的情况,
                                                //应该是将ram.ram.ram_buf+start用bio中的物理存储地址
        }else if(rq_data_dir(req) == WRITE){
            memcpy(ram.ram_buf+start,buff,len);
        }
        /*判断是不是最后一个*/
        if(!__blk_end_request_cur(req,err))//完成当前部分请求,返回0,否则返回其他数值
        {
            req = blk_fetch_request(q);//if判断当前请求完成后,会继续申请下一个请求,直到申请到NULL
        }
    }
}    
static int __init disk_init(void)
{
    int ret=0;
    /*1.申请ram内存*/
    ram.ram_buf = kzalloc(RAM_BLOCK_BYTE_SIZE,GFP_KERNEL);//申请字节大小
    if(ram.ram_buf ==NULL)
    {
        printk("kzalloc  error \r\n");
        ret = -1;
        goto kzalloc_ram_error;
    }
    printk("before ram.major = %d \r\n",ram.major);
    /*2.注册块设备*/
    ram.major = register_blkdev(0,RAM_BLOCK_NAME);
    if(ram.major < 0)
    {
        printk("register error \r\n");
        ret = -1;
        goto register_blkdev_error;
    }
    /*3.申请gendisk*/
    ram.ram_gendisk = alloc_disk(3);
    if(ram.ram_gendisk == NULL)
    {
        ret = -1;
        goto RAM_BLOCK_SIZE_error;
    }
    /*4.自旋锁*/
    spin_lock_init(&ram.my_spinlock);
    /*5.初始化请求队列*/
    ram.queue = blk_init_queue(ram_queue_process,&ram.my_spinlock);
    if(ram.queue == NULL)
    {
        ret = -1;
        goto blk_queue_error;
    }
    /*6.给gendisk内值,初始化*/
    ram.ram_gendisk->major = ram.major; //主设备号
    ram.ram_gendisk->first_minor = 0;//次设备号开始
    ram.ram_gendisk->fops = &ram_ops;//操作函数
    ram.ram_gendisk->private_data = &ram; //私有数据
    ram.ram_gendisk->queue = ram.queue;//请求队列
    //memcpy(&ram.ram_gendisk->disk_name,&RAM_BLOCK_NAME,sizeof(RAM_BLOCK_NAME));
    //sprintf(ram.ram_gendisk->disk_name, RAM_BLOCK_NAME);
    snprintf(ram.ram_gendisk->disk_name, 32, "%s",RAM_BLOCK_NAME);
    set_capacity(ram.ram_gendisk,RAM_BLOCK_SIZE);//设置大小,即多少块
    add_disk(ram.ram_gendisk);//添加gendisk到内核
     printk("major = %d \r\n",ram.major);
    return ret;
blk_queue_error:   
    del_gendisk(ram.ram_gendisk);
RAM_BLOCK_SIZE_error:
    unregister_blkdev(ram.major,RAM_BLOCK_NAME);
register_blkdev_error:
    kfree(ram.ram_buf);
kzalloc_ram_error:
    return ret;

}
void __exit disk_exit(void)
{
    /*对gendisk进行操作*/
    del_gendisk(ram.ram_gendisk);
    //put_gendisk(ram.ram_gendisk);
    /*对ram.queue进行操作*/
    blk_cleanup_queue(ram.queue);
    /*对块设备进行操作*/
    unregister_blkdev(ram.major,RAM_BLOCK_NAME);
    /*释放开辟的空间*/
    kfree(ram.ram_buf);

}

module_init(disk_init);
module_exit(disk_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("WYT");

四,两者在驱动上区别

  无I/O调度设备,虽然在申请了请求队列(blk_alloc_queue),但是没有用到,最终调用的是它的ram_queue_process-1,这个函数主要处理bio中的数据。有I/O调度的设备,在请求队列申请时候,调用函数ram_queue_process-2,这个函数在处理时候,是对请求队列中request中的bio进行操作。

	/*无I/O调度(不使用请求队列)*/
    ram.queue = blk_alloc_queue(GFP_KERNEL);
    /*制造设置请求函数*/
    blk_queue_make_request(ram.queue,ram_queue_process-1);
	
	/*有I/O调度(使用请求队列)*/
	  ram.queue = blk_init_queue(ram_queue_process-2,&ram.my_spinlock);

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2331687.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【算法学习】链表篇:链表的常用技巧和操作总结

算法学习&#xff1a; https://blog.csdn.net/2301_80220607/category_12922080.html?spm1001.2014.3001.5482 前言&#xff1a; 在各种数据结构中&#xff0c;链表是最常用的几个之一&#xff0c;熟练使用链表和链表相关的算法&#xff0c;可以让我们在处理很多问题上都更加…

2台8卡L20服务器集群推理方案

1、整体流程梳理 #mermaid-svg-0aNtsWUnOH7ewXpN {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-0aNtsWUnOH7ewXpN .error-icon{fill:#552222;}#mermaid-svg-0aNtsWUnOH7ewXpN .error-text{fill:#552222;stroke:#55…

HarmonyOS:使用geoLocationManager (位置服务)获取位置信息

一、简介 位置服务提供GNSS定位、网络定位&#xff08;蜂窝基站、WLAN、蓝牙定位技术&#xff09;、地理编码、逆地理编码、国家码和地理围栏等基本功能。 使用位置服务时请打开设备“位置”开关。如果“位置”开关关闭并且代码未设置捕获异常&#xff0c;可能导致应用异常。 …

系统分析师(二)--操作系统

概述 进程管理 选项A&#xff1a;该进程中打开的文件 进程中打开的文件是由整个进程来管理的&#xff0c;同一进程下的各个线程都可以对这些打开的文件进行访问和操作&#xff0c;所以进程中打开的文件是可以被这些线程共享的。 选项B&#xff1a;该进程的代码段 进程的代码…

安科瑞测频仪表:新能源调频困局的破局者

安科瑞顾强 在“双碳”目标推动下&#xff0c;风电、光伏等新能源正加速成为电力供应的核心力量。然而&#xff0c;新能源发电的间歇性与波动性&#xff0c;如同一把“双刃剑”&#xff0c;在提供清洁电力的同时&#xff0c;也给电网稳定运行带来了前所未有的挑战。国家能源局…

富士相机照片 RAF 格式如何快速批量转为 JPG 格式教程

富士&#xff08;Fujifilm&#xff09;相机拍摄的 RAW 格式文件&#xff08;RAF&#xff09;因其高质量和丰富的图像信息而受到摄影师的喜爱。然而&#xff0c;RAF 文件通常体积较大且不易于分享或直接使用。为了方便处理&#xff0c;许多人选择将其转换为更通用的 JPG 格式。在…

Linux 入门指令(1)

&#xff08;1&#xff09;ls指令 ls -l可以缩写成 ll 同时一个ls可以加多个后缀 比如 ll -at (2)pwd指令 &#xff08;3&#xff09;cd指令 cd .是当前目录 &#xff08;4&#xff09;touch指令 &#xff08;5&#xff09;mkdir指令 &#xff08;6&#xff09;rmdir和rm…

Redis缓存数据库一致性

前言&#xff1a; 在系统开发中经常使用关系型数据库&#xff0c;为了提升关系型数据库的读性能&#xff0c;一般会使用redis加一层缓存&#xff0c;缓存和数据库是分离的两次操作&#xff0c;本文用来分析如何操作能保证缓存和数据库的数据一致性。 一、读场景 二、写场景 …

Android Coil 3 Fetcher大批量Bitmap拼接成1张扁平宽图,Kotlin

Android Coil 3 Fetcher大批量Bitmap拼接成1张扁平宽图&#xff0c;Kotlin <uses-permission android:name"android.permission.WRITE_EXTERNAL_STORAGE" /><uses-permission android:name"android.permission.READ_EXTERNAL_STORAGE" /><u…

文件相关:treecpmv命令扩展详解

拷贝和移动文件 序号命令对应英文作用01tree [目录名]tree以树状图列出文件目录结构02cp 源文件 目标文件copy复制文件或者目录03mv 源文件 目标文件move移动文件或者目录&#xff0f;文件或者目录重命名 一、 tree命令 &#xff08;1&#xff09;定义 tree 命令可以以树状…

S32K144的m_data_2地址不够存,重新在LD文件中配置地址区域

在开发平台软件的时候代码中超出了64K的内存&#xff0c;单纯在ld文件中&#xff0c;增加m_data_2的存储长度&#xff0c;原先是0x00007000,我将长度修改为0x00008000,起始地址还是0x20000000,软件编译没有报错堆栈超出&#xff0c;但是软件下载到单片机中之后&#xff0c;144不…

【STM32】综合练习——智能风扇系统

目录 0 前言 1 硬件准备 2 功能介绍 3 前置配置 3.1 时钟配置 3.2 文件配置 4 功能实现 4.1 按键功能 4.2 屏幕功能 4.3 调速功能 4.4 倒计时功能 4.5 摇头功能 4.6 测距待机功能 0 前言 由于时间关系&#xff0c;暂停详细更新&#xff0c;本文章中&#xff0c;…

【重装系统】大白菜自制U盘装机,备份C盘数据,解决电脑启动黑屏/蓝屏

1. 准备 U 盘 U 盘容量至少 8G&#xff0c;备份 U 盘的数据&#xff08;后期会格式化&#xff09; 2. 从微软官网下载操作系统镜像 https://www.microsoft.com/zh-cn/software-download/windows11 3. 下载安装大白菜 https://www.alipan.com/s/33RVnKayUfY 4. 插入 U 盘&#…

vue实现目录锚点且滚动到指定区域时锚点自动回显——双向锚点

最近在用vue写官网&#xff0c;别问我为什么用vue写官网&#xff0c;问就是不会jq。。。。vue都出现11年了。。。 左侧目录&#xff1a;点击时&#xff0c;右侧区域可以自动滚动到指定的位置。 右侧区域手动滚动时&#xff0c;左侧锚点可以自动切换到对应的目录上 从而实现…

Flutter Invalid constant value.

0x00 问题 参数传入变量&#xff0c;报错&#xff01; 代码 const Padding(padding: EdgeInsets.all(20),child: GradientProgressIndicator(value: _progress), ),_progress 参数报错&#xff1a;Invalid constant value. 0x01 原因 这种情况&#xff0c;多发生于&#xff…

【精品PPT】2025固态电池知识体系及最佳实践PPT合集(36份).zip

精品推荐&#xff0c;2025固态电池知识体系及最佳实践PPT合集&#xff0c;共36份。供大家学习参考。 1、中科院化学所郭玉国研究员&#xff1a;固态金属锂电池及其关键材料.pdf 2、中科院物理所-李泓固态电池.pdf 3、全固态电池技术研究进展.pdf 4、全固态电池生产工艺.pdf 5、…

如何计算设备电池工作时长?

目录 【mAh&#xff08;毫安时&#xff09;计算方法】 【Wh&#xff08;瓦时&#xff09;计算方法】 【为什么仅用电流&#xff08;mA&#xff09;和时间&#xff08;h&#xff09;就能计算电池使用时长&#xff08;mAh&#xff09;&#xff1f;】 1. mAh 的本质是“电荷量…

抽象类及其特性

目录 1、概念2、语法3、特性4、作用 1、概念 在面向对象中&#xff0c;所有对象都是通过类来描述的&#xff0c;但是并不是所有的类都可以用来描述对象。比如下述例子中的 Animal 类&#xff0c;Dog 类和 Cat 类是 Animal 类的子类&#xff0c;可以分别描述小狗和小猫&#xf…

【教程】xrdp修改远程桌面环境为xfce4

转载请注明出处&#xff1a;小锋学长生活大爆炸[xfxuezhagn.cn] 如果本文帮助到了你&#xff0c;欢迎[点赞、收藏、关注]哦~ 目录 xfce4 vs GNOME对比 配置教程 1. 安装 xfce4 桌面环境 2. 安装 xrdp 3. 配置 xrdp 使用 xfce4 4. 重启 xrdp 服务 5. 配置防火墙&#xff…

利用python从零实现Byte Pair Encoding(BPE):NLP 中的“变形金刚”

BPE&#xff1a;NLP 界的“变形金刚”&#xff0c;从零开始的奇幻之旅 在自然语言处理&#xff08;NLP&#xff09;的世界里&#xff0c;有一个古老而神秘的传说&#xff0c;讲述着一种强大的魔法——Byte Pair Encoding&#xff08;BPE&#xff09;。它能够将普通的文本“变形…