首页 > 其他分享 >驱动开发学习笔记---块设备

驱动开发学习笔记---块设备

时间:2022-12-04 10:56:20浏览次数:41  
标签:queue struct request 笔记 --- gendisk 驱动 disk 设备

一、块设备简介

块设备驱动是存储设备驱动,块设备驱动相比字符设备驱动的主要区别如下:

①、块设备只能以块为单位进行读写访问,块是 linux 虚拟文件系统(VFS)基本的数据传输单位。字符设备是以字节为单位进行数据传输的,不需要缓冲。

②、块设备在结构上是可以进行随机访问的,对于这些设备的读写都是按块进行的,块设备使用缓冲区来暂时存放数据,等到条件成熟以后再一次性将缓冲区中的数据写入块设备中。

二、块设备驱动框架

1、注册注销块设备

int register_blkdev(unsigned int major, const char *name)  
void unregister_blkdev(unsigned int major, const char *name)

major: 要注销的块设备主设备号。 name: 要注销的块设备名字。

2、申请和删除磁盘设备

struct gendisk *alloc_disk(int minors)  

minors: 次设备号数量, 也就是 gendisk 对应的分区数量。

void del_gendisk(struct gendisk *gp)

gp: 要删除的 gendisk。

3、将 gendisk 添加到内核

void add_disk(struct gendisk *disk)  

disk: 要添加到内核的 gendisk。

4、设置 gendisk 容量

void set_capacity(struct gendisk *disk, sector_t size)  

disk: 要设置容量的 gendisk。 size: 磁盘容量大小,注意这里是扇区数量。块设备中最小的可寻址单元是扇区,一个扇区一般是 512 字节,有些设备的物理扇区可能不是 512 字节。不管物理扇区是多少,内核和块设备驱动之间的扇区都是 512 字节。所以 set_capacity 函数设置的大小就是块设备实际容量除以512 字节得到的扇区数量。比如一个 2MB 的磁盘,其扇区数量就是(210241024)/512=4096。

5、调整 gendisk 引用计数

内核会通过 get_disk 和 put_disk 这两个函数来调整 gendisk 的引用计数,根据名字就可以知道, get_disk 是增加 gendisk 的引用计数, put_disk 是减少 gendisk 的引用计数。

truct kobject *get_disk(struct gendisk *disk)
void put_disk(struct gendisk *disk)

6、块设备操作集

struct block_device_operations {
int (*open) (struct block_device *, fmode_t);
void (*release) (struct gendisk *, fmode_t);
int (*rw_page)(struct block_device *, sector_t, struct page *,int rw);
int (*ioctl) (struct block_device *, fmode_t, unsigned, unsigned long);
int (*compat_ioctl) (struct block_device *, fmode_t, unsigned, unsigned long);
long (*direct_access)(struct block_device *, sector_t,void **, unsigned long *pfn, long size);
unsigned int (*check_events) (struct gendisk *disk,unsigned int clearing);
 /* ->media_changed() is DEPRECATED, use ->check_events() instead */
int (*media_changed) (struct gendisk *);
void (*unlock_native_capacity) (struct gendisk *);
int (*revalidate_disk) (struct gendisk *);
int (*getgeo)(struct block_device *, struct hd_geometry *);
 /* this callback is with swap_lock and sometimes page table lock
held */
void (*swap_slot_free_notify) (struct block_device *,unsigned long);
struct module *owner;
 };  

块设备数据读写过程:

(1)、请求队列 request_queue

①、初始化请求队列

我们首先需要申请并初始化一个 request_queue,然后在初始化 gendisk 的时候将这个request_queue 地址赋值给 gendisk 的 queue 成员变量。使用 blk_init_queue 函数来完成request_queue 的申请与初始化。

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

rfn: 请求处理函数指针,每个 request_queue 都要有一个请求处理函数,请求处理函数request_fn_proc

void (request_fn_proc) (struct request_queue *q)  

lock: 自旋锁指针,需要驱动编写人员定义一个自旋锁,然后传递进来。,请求队列会使用这个自旋锁。

②、删除请求队列

当卸载块设备驱动的时候我们还需要删除掉前面申请到的 request_queue,删除请求队列使用函数 blk_cleanup_queue

void blk_cleanup_queue(struct request_queue *q)  

q: 需要删除的请求队列。

③、分配请求队列并绑定制造请求函数

blk_init_queue 函数完成了请求队列的申请以及请求处理函数的绑定,这个一般用于像机械硬盘这样的存储设备,需要 I/O 调度器来优化数据读写过程。但是对于 EMMC、 SD 卡这样的非机械设备,可以进行完全随机访问,所以就不需要复杂的 I/O 调度器了。对于非机械设备我们可以先申请 request_queue,然后将申请到的 request_queue 与“制造请求”函数绑定在一起。

struct request_queue *blk_alloc_queue(gfp_t gfp_mask)

gfp_mask: 内存分配掩码

2、 请求 request

请求队列(request_queue)里面包含的就是一系列的请求(request), request 是一个结构体, 需要从 request_queue 中取出一个一个的 request,然后再从每个 request 里面取出 bio,最后根据 bio 的描述讲数据写入到块设备,或者从块设备中读取数据。

①、 获取请求

request *blk_peek_request(struct request_queue *q)  

q: 指定 request_queue。 返回值: request_queue 中下一个要处理的请求(request),如果没有要处理的请求就返回NULL。

②、开启请求

void blk_start_request(struct request *req)

③、一步到位处理请求

blk_fetch_request 函数来一次性完成请求的获取和开启

struct request *blk_fetch_request(struct request_queue *q)
{
struct request *rq;
​
rq = blk_peek_request(q);
if (rq)
blk_start_request(rq);
return rq;
}  

3、 bio 结构

每个 request 里面会有多个 bio, bio 保存着最终要读写的数据、地址等信息。上层应用程序对于块设备的读写会被构造成一个或多个 bio 结构, bio 结构描述了要读写的起始扇区、要读写的扇区数量、是读取还是写入、页偏移、数据长度等等信息。上层会将 bio 提交给 I/O 调度器,I/O 调度器会将这些 bio 构造成 request 结构,而一个物理存储设备对应一个 request_queue,request_queue 里面顺序存放着一系列的 request。新产生的 bio 可能被合并到 request_queue 里现有的 request 中,也可能产生新的 request,然后插入到 request_queue 中合适的位置,这一切都是由 I/O 调度器来完成的。

 

标签:queue,struct,request,笔记,---,gendisk,驱动,disk,设备
From: https://www.cnblogs.com/zhuzi1/p/16949505.html

相关文章

  • Redis--回顾提要
    一、写在前知识学了就忘!不用就忘!我太健忘!特此记录!用于复习打卡!Redis干就完事了!二、来辣!Redis做异步队列:一般list结构做队列,rpush生产消息,lpop消费消息,当lpop没有消......
  • Python - Scrapy
    1.安装第三方包pipinstallScrapy2.创建项目#生成文件夹scrapydemo1PSE:\PyProject>scrapystartprojectscrapydemo1NewScrapyproject'scrapydemo1',using......
  • 乘法逆元学习笔记
    定义当\(a,b\)满足\(ab\equiv1\pmodp\),\(a,b\)互为\(\pmodp\)的乘法逆元,也记作\(a^{-1}\)和\(b^{-1}\)。前置知识1.费马小定理若\(p\)为质数且\(\gc......
  • 二元一次不定方程学习笔记
    定义含有两个未知数,且未知数项的次数都是\(1\)的不定方程就是二元一次不定方程,一般可以化成下面的形式:\[ax+by=c\]前置知识裴蜀定理定理:对于一个二元一次不定方程,当......
  • 离散对数&BSGS学习笔记
    离散对数定义求\(k\)使得\(a^k\equivn\pmodp\),称\(n\)在模\(p\)意义下以\(a\)为底的对数是\(k\)。如何求离散对数BSGS(BabyStep,GiantStep)大步小步算......
  • 中国剩余定理学习笔记
    作用中国剩余定理(ChineseRemainderTheorem,CRT),也称孙子定理,是用来求解线性同余方程组,即如下面的方程组:\[\begin{cases}x\equiv&a_1\({\rmmod}\p_1)\\x\equi......
  • 威尔逊定理学习笔记
    定理当且仅当\(p\)是质数时,\((p-1)!\equiv-1\pmodp\)。证明首先对于\(p<5\)时,直接证即可。对于\(p\ge5\),分成以下几种情况:\(p\)为合数但不为质数......
  • 卢卡斯定理学习笔记
    内容对于一个质数\(p\),有:\[\LARGEC_n^m\equivC_{[\frac{n}{p}]}^{[\frac{m}{p}]}·C_{n\bmodp}^{m\bmodp}\pmodp\]证明引理:\((1+x)^p\equiv(1+x^p)\pmod......
  • 容斥原理学习笔记
    定义集合两个集合的交集:集合\(A\)与\(B\)的交集可以表示为:\[A\capB=\{x:x\inA\landx\inB\}\]两个集合的并集:集合\(A\)与\(B\)的并集可以表示为:\[A\c......
  • 线性代数学习笔记
    没太听懂的东西初中首先说一下什么是线性。举个例子,一个一次函数\(f(x)=ax+b(a\not=0)\)的函数图像应该是一条直线。同理,函数\(f(x,y)=ax+by+c\)的函数图像也应该......