Linux系统驱动(十九)块设备驱动

news2024/11/16 13:35:09

文章目录

  • 一、块设备驱动简介
    • (一)简介
    • (二)块设备驱动相关概念
  • 二、块设备驱动
    • (一)框架图
      • 1. 虚拟文件系统(VFS)
      • 2. Disk Cache:硬盘的高速缓存
      • 3. 映射层(mapping layer)
      • 4. Generic Block Layer:通用块层
      • 5. I/O Scheduler Layer :I/O调度层
      • 6. 块设备驱动层
    • (二)块设备驱动框架
  • 三、块设备驱动API
    • (一)结构体对象
      • 1. reque_queue和request及bio的关系
    • (二)结构体对象初始化API
  • 四、代码示例

一、块设备驱动简介

(一)简介

系统能够随机访问固定大小(1block–512byte)数据片的设备被称之为块设备。

  • 注:块设备的访问方式是随机的,即可以直接访问设备上的任何块,而不需要按照顺序读取或写入

块设备文件一般都是以安装(挂载)文件系统的方式使用,这也是块设备通常的访问方式。

  • 注:为了使用块设备存储数据,我们需要在其上安装(或挂载)一个文件系统。文件系统是组织和存储文件的一种方式,它定义了如何存储数据、如何命名文件以及如何组织文件的层次结构。安装(挂载)文件系统是将块设备与目录树中的某个点(挂载点)关联起来的过程,之后该目录下的所有文件和目录都将被存储在块设备上

扇区是块设备的基本存储单元,扇区大小一般是2的整数倍,最常见的大小是512字节。所有对块设备的读写操作都是基于扇区进行的。因此,扇区的大小直接决定了块设备能够处理的最小数据量。
扇区是设备的最小可寻址单位,所以块不能比扇区还小,只能数倍与扇区大小。
内核对块大小的要求是:必须是扇区大小的整数倍,并且小于页面的大小,所以块的大小通常是512字节、1K或者4K。

(二)块设备驱动相关概念

磁头:一个磁盘有多少个面就有多少个磁头
磁道:在一个磁头上可以有很多环,这些环就叫做磁道
扇区:磁道上访问数据的最小的单位就是扇区,一个扇区的大小就是512字节

1block = 512字节 1024字节 2048字节 4096字节
1扇区 = 512字节

块设备的能存储的数据 = 磁头 * 磁道 * 扇区 * 512

机械硬盘存放文件时,同一个文件有可能存放在不同的盘片的不同面,且数据可能会分成多个块,并且是无序的存放。因此在读取时,为了减少切换磁头的频率,会先无序的从磁盘读出该文件所有块,然后再对读到内存中的块进行排序后返回给用户空间

  • 注:磁盘擅长连续的读数据而非跳跃的读数据

二、块设备驱动

(一)框架图

在这里插入图片描述

1. 虚拟文件系统(VFS)

隐藏了各种硬件的具体细节,为用户操作不同的硬件提供了一个统一的接口。屏蔽各个文件系统的差异,其基于不同的文件系统格式,比如EXT,FAT等。用户程序对设备的操作都通过VFS来完成,在VFS上面就是诸如open、close、write和read的函数API。

windows文件系统:ntfs
ubuntu文件系统:ext4

2. Disk Cache:硬盘的高速缓存

用户缓存最近访问的文件数据,如果能在高速缓存中找到,就不必去访问硬盘,毕竟硬盘的访问速度慢很多。

3. 映射层(mapping layer)

这一层主要用于确定文件系统的block size,然后计算所请求的数据包含多少个block。同时调用具体文件系统函数来访问文件的inode,确定所请求的数据在磁盘上面的逻辑地址。

4. Generic Block Layer:通用块层

Linux内核把块设备看做是由若干个扇区组成的数据空间,上层的读写请求在通用块层被构造成一个或多个bio结构
在硬盘上连续存储的每个空间会对应一个BIO结构体

5. I/O Scheduler Layer :I/O调度层

负责将通用块层的块I/O操作进行(电梯调度算法)调度、插入、暂存、排序、合并、分发等操作,对磁盘的操作更为高效。负责将通用块层的块I/O操作进行
将连续的BIO(可能属于不同进程)合并成一个request加入到request队列,因此一个request中有一个或多个BIO结构体

6. 块设备驱动层

在块系统架构的最底层,由块设备驱动根据排序好的请求,对硬件进行数据访问。

(二)块设备驱动框架

user:
 open     read    write    close
-------------------(io请求)-----------------------------------
kernel	|中间层: (block_device)
        |   read读1500数据在物理磁盘上三段不连续,构造3个bio结构体
     |	将用户的io请求转化成BIO(block,input ,output),
     |	在物理内存上连续的bio会被合成request,这个request
     |	会被放到内核的一个队列上。
     |---------------------------------------------------------
     |driver:gendisk
     | 1.分配对象
     | 2.对象初始化
     | 3.初始化一个队列  head----request(read)----request(write)---...
     | //4.硬盘设备的初始化
     | 5.注册、注销
------------------------------------------------------------------  
haredware :   分配的内存(模拟真实的设备)(1M)
  • 补充:反汇编命令 objdump

三、块设备驱动API

(一)结构体对象

1. gendisk的结构体对象
    struct gendisk {   
        int major;   			//块设备的主设备号
        int first_minor; 		//起始的次设备号
        int minors; 			//设备的个数,分区的个数
        char disk_name[DISK_NAME_LEN]; //磁盘的名字
        struct disk_part_tbl  *part_tbl;//磁盘的分区表的首地址
        struct hd_struct part0;	//part0分区的描述
        const struct block_device_operations *fops;//块设备的操作方法结构体
        struct request_queue *queue;//队列
        void *private_data; 	//私有数据
    };

2. hd_struct分区的结构体:part0分区的描述
    struct hd_struct {
        sector_t start_sect; 	//起始的扇区号
        sector_t nr_sects;   	//扇区的个数                        
        int  partno;        	//分区号
    };

    //块设备的操作方法结构体
    struct block_device_operations {
        int (*open) (struct block_device *, fmode_t);
        int (*release) (struct gendisk *, fmode_t);
        int (*ioctl) (struct block_device *, fmode_t, unsigned, unsigned long);
        int (*getgeo)(struct block_device *, struct hd_geometry *); 
        //设置磁盘的磁头,磁道,扇区的个数的
    };
    
3. hd_geometry结构体
	struct hd_geometry {
    	  unsigned char heads;
    	  unsigned char sectors;
    	  unsigned short cylinders;
    	  unsigned long start;
	};
----------------------------------------------------------------
4. request_queue结构体
	struct  request_queue 
	{
    	/*双向链表数据结构,将所有加入到队列的IO请求组建成一个双向链表*/
    	struct  list_head  queue_head; 
    	struct list_head    requeue_list; //request队列
    	spinlock_t      requeue_lock;     //队列自旋锁
    	unsigned long     nr_requests;     /* 最大的请求数量 */
    	unsigned long     queue_flags;/*当前请求队列的状QUEUE_FLAG_STOPPED*/ 
	};
5. request结构体
	struct  request
	{
	    struct list_head queuelist;/* 请求对象中的链表元素*/
	    struct request_queue *q; /* 指向存放当前请求的请求队列*/
	    unsigned int __data_len; /* 当前请求要求数据传输的总的数据量 */
	    sector_t __sector;         /* 当前请求要求数据传输的块设备的起始扇区 */
	    struct bio *bio;  /* bio对象所携带的信息转存至请求对象中*/
	    struct bio *biotail; /* bio链表*/
	};
	//通常一个request请求可以包含多个bio,一个bio对应一个I/O请求  
6. bio结构体
	struct bio {  
	    struct bio *bi_next;  /* 指向当前bio的下一个对象*/ 
	    unsigned long  bi_flags;   /* 状态、命令等 */ 
	    unsigned long bi_rw;   /* 表示READ/WRITE*/ 
	    struct block_device *bi_bdev;    /* 与请求相关联的块设备对象指针*/ 
	    unsigned short bi_vcnt;  /* bi_io_vec数组中元素个数 */ 
	    unsigned short bi_idx;  /* 当前处理的bi_io_vec数组元素索引 */
	    unsigned int bi_size;  /* 本次传输需要传输的数据总量,byte(扇区大小整数倍) */ 
	    struct bio_vec *bi_io_vec;/* 指向一个IO向量的数组,数组中的内各元素对应一个物理页的page对象 */
	  };
7. bio_vec结构体
	struct bio_vec {  
	    struct page  *bv_page; //指向用于数据传输的页面所对应的struct page对象
	    unsigned int bv_len;   //表示当前要传输的数据大小  
	    unsigned int bv_offset;//表示数据在页面内的偏移量 
	};

1. reque_queue和request及bio的关系

在这里插入图片描述
新版本的linux块设备驱动会有多个request_queue,一般是有几个核就有几个request队列;
每个request_queue上有多个request节点,以链表形式链在一起;
每个request节点又包含多个bio结构体(可能属于不同的进程,但是必定是在物理上连续存储的);
每个bio结构体又包含多个bio_vec结构体,因为bio结构体在内存上可能又分成了多个块,bio_vec结构体中存放了页号,偏移量和长度
在这里插入图片描述
因此,可以根据bio_vec结构体中的信息获得内存中的线性地址;
然后,根据request节点的起始扇区号,加上dev_addr,得到要操作的物理内存地址;

在这里插入图片描述

(二)结构体对象初始化API

1. 初始化结构体
  	struct gendisk *mydisk;

    struct gendisk *alloc_disk(int minors)
    //void put_disk(struct gendisk *disk)
    //归还引用计数
    功能:分配gendisk的内存,然后完成必要的初始化
    参数:
        @minors:分区的个数
    返回值:成功返回分配到的内存的首地址,失败返回NULL


    int register_blkdev(unsigned int major, const char *name)
    //void unregister_blkdev(unsigned int major, const char *name)
    功能:申请设备设备驱动的主设备号
    参数:
        @major : 0:自动申请
                  >0 :静态指定
        @name  :名字  cat /proc/devices
    返回值: 
            major=0 ;成功返回主设备号,失败返回错误码
            major>0 :成功返回0 ,失败返回错误码

    void set_capacity(struct gendisk *disk, sector_t size)
    功能:设置磁盘的容量

    struct request_queue *blk_mq_init_sq_queue(struct blk_mq_tag_set *set,
     const struct blk_mq_ops *ops,unsigned int queue_depth,unsigned int set_flags)
    //void blk_cleanup_queue(struct request_queue *q)
    功能:用于在给定队列深度的情况下使用mq ops设置队列的助手,以及通过mq ops标志传递的助手
    参数:
        @被初始化的tag对象,tag被上层使用,里面包含硬件队列的个数,队列的操作方法结构体,标志位等
     @放入到tag中的操作方法结构体
     @ tag中指定支持的队列深度
     @将tag中队列的处理标志位,例如BLK_MQ_F_SHOULD_MERGE, BLK_MQ_F_BLOCKING等
    返回值:成功返回队列指针,失败返回错误码指针 
2. 队列处理相关函数
	blk_mq_start_request(rq); //开始处理队列
	blk_mq_end_request(rq, BLK_STS_OK); //结束队列处理
	rq_for_each_segment(bvec, rq, iter) //从request->bio_vec
	void* b_buf = page_address(bvec.bv_page) + bvec.bv_offset; //将页地址转换为线性地址(内地址)
	rq_data_dir(rq))   //从request获取本次读写的方向  WRITE 1   READ 0
	dev_addr+(rq->__sector *512) //磁盘设备的地址

3.注册、注销
    void add_disk(struct gendisk *disk)
    //注册
    void del_gendisk(struct gendisk *disk)
    //注销

四、代码示例


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

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

相关文章

IP代理如何增强网络安全性?

在当今的数字时代,网络安全已成为一个关键问题,而使用 IP 代理可以成为增强网络安全的有效方法。根据请求信息的安全性,IP 代理服务器可分为三类:高级匿名代理、普通匿名代理和透明代理。此外,根据使用的用途&#xff…

NT35510的LCD函数详解01(洋桃电子-触摸屏开发者笔记)

NT35510的LCD函数详解01(洋桃电子-触摸屏开发者笔记) 资料下载: 洋桃电子 YoungTalk 探索最好的 STM32 教学 (doyoung.net) 接口类型 NT35510 数据手册(英文).pdf NT35510 应用手册(英文).…

Jenkins持续集成工具学习

一、从装修厨房看项目开发效率优化 二、持续集成工具 三、JavaEE项目部署方式对比 四、Jenkins+SVN持续集成环境搭建

WebGoC题解(18) 630.电线杆(2019NHOI小乙)

题目描述 小C在农场的附近看到有n颗电线杆排成一行,相邻之间距离为20。它们高度可能不一样,但高度相同的电线杆顶端有电线连接。如下面示意图中,电线杆用粗细为6的垂直直线画,电线用粗细为2的水平直线画。给定每个电线杆的高度&am…

Linux-Haproxy搭建Web群集

LVS在企业应用中抗负载能力强 不支持正则处理,不能实现动静分离对于大型网格,LVS的实施配置复杂,维护成本较高 Haproxy是一款可提供高可用性、负载均衡、及基于TCP和HTTP应用的代理的软件 适用于负载大的Web站点运行在硬件上可支持数以万计的…

AI大模型开发——4.transformer模型(0基础也可懂)(1)

无论是想怎样学习大模型,transformer都是一个绕不开的话题。transformer的出现彻底改变了nlp领域,进一步推动了大模型的产生,可以说,transformer就是大模型开发的鼻祖。 可能只通过说大家会有些不理解。大家可以看下方的大语言模型…

打卡第四十四天:最长公共子序列、不相交的线、最大子序和、判断子序列

一、最长公共子序列 题目 文章 视频 本题和最长重复子数组区别在于这里不要求是连续的了,但要有相对顺序,即:"ace" 是 "abcde" 的子序列,但 "aec" 不是 "abcde" 的子序列。 确定dp数…

4个快捷高效的ai在线写作工具推荐。

ai在线写作因其快速的创作方式,高效的写作效率以及能够为我们带来无限的灵感而被广泛应用。如果你还不会使用ai进行写作的话,就看看下面这4款AI写作工具吧。 1、笔灵在线创作 直通车 :https://ibiling.cn 这是个在线的AI工具网站,在内容创作…

个人可识别信息(PII) AI 去除 API 数据接口

个人可识别信息(PII) AI 去除 API 数据接口 ai / 隐私保护 基于 AI 模型自动去除个人识别信息(PII) 个人信息保护 / AI 模型 。 1. 产品功能 基于自有专业模型进行 PII 自动去除高效处理敏感信息全接口支持 HTTPS(TLS v1.0 / v1.1 / v1.2 /…

地质灾害评估和治理工程勘查设计资质乙级资质办理标准

地质灾害评估和治理工程勘查设计资质乙级资质的办理标准主要包括单位条件、专业技术人员条件、仪器设备要求以及申请材料等方面。以下是详细的办理标准: 一、单位条件 **1、法人资格:**申请单位应具有企业法人或者事业单位法人资格。 **2、管理体系&a…

龙良曲pytorch课时1-课时13

前言 这篇是个人学习龙曲良老师的pytorch课程的笔记,疑惑地方自己加的内容 一、pytorch引入 1. 自动求导 在深度学习中,我们通常需要训练一个模型来最小化损失函数。这个过程可以通过梯度下降等优化算法来实现。梯度是函数在某一点上的变化率&#x…

Linux基础入门--目录结构之基本目录操作及注意事项

😀前言 本篇博文是关于Linux基础入门–目录结构的基本介绍、基本目录和操作命令,希望你能够喜欢 🏠个人主页:晨犀主页 🧑个人简介:大家好,我是晨犀,希望我的文章可以帮助到大家&…

滴答定时器笔记

SysTick介绍 1.1 什么是SysTick? Systick,即滴答定时器,是内核中的一个特殊定时器,用于提供系统级的定时服务。该定时器是一个24位的 递减计数器,具有自动重载值寄存器的功能。当计数器到达自动重载值时,它…

无人机中的温度/湿度/气压传感器详解!!!

一、温度传感器 温度传感器是一种用来测量物体或环境的温度变化的传感器。在无人机中,温度传感器通常采用红外线热成像技术,通过红外线相机获取物体表面的温度数据,实现对环境和物体温度的监测和测量。该技术具有响应速度快、无需接触、测量…

100个练习学习Rust!构文・整数・变量

前一篇文章 【0】准备 【1】构文・整数・变量 ← 本次全部文章列表 《100 Exercise To Learn Rust》第2回,也就是实际演习的第1回!从这次开始,我们会适度减少前置说明,直接进入问题的解决! 本次的相关页面 1.1. Syn…

SpringBoot入门第一篇

目录 SpringBootSpring Boot核心特性Spring Boot应用结构目录结构样例目录结构讲解 Spring Boot版本更新概览升级建议总结 常见使用场景社区和资源常见疑问**Spring Boot的自动配置机制是如何工作的?****Spring Boot支持哪些常见的配置文件格式?****为什…

硬件设计-1/f噪声、均方根(RMS)噪声与等效噪声带宽

简介: 学习下噪声的知识,为什么写这个呢,因为前几年有次面试和别人谈到这个问题。 定义解释: 1/F噪声 高频下的噪声为白噪声(即其频谱密度不会随频率而变化)。这种情况适用于运算放大器的大 部分频率范围,但在低频率…

C语言问答进阶--2、C语言简介及基本的输入输出函数

C语言简介 Q:您好,能大体介绍下C语言吗? A:当然可以。C语言是一种计算机语言,它主要被用在系统编程里,可以说,C语言的产生就和操作系统的编写密不可分。 【C语言简要历史】 1978年由美国电话电报…

Redis7.0.15 主从复制、哨兵模式搭建

主从复制:master以写为主,slave以读为主,当master数据变化的时候,自动将新的数据异步同步到其他的slave数据库 1. Redis复制介绍: https://redis.io/docs/latest/operate/oss_and_stack/management/replication/ 读写…

Unity游戏开发001

Unity游戏开发 系列文章的目录: 第一章:Hello,Unity! “好读书,不求甚解;每有会意,便欣然忘食。” 本文目录: Unity游戏开发 Unity游戏开发前言今天我们来体验一下unity开发创建第一…