当前位置:首页 > 嵌入式 > 嵌入式教程
[导读]块设备通常指一些需要以块(如512字节)的方式写入的设备,如IDE硬盘、SCSI硬盘、光驱等。它的驱动程序的编写过程与字符型设备驱动程序的编写有很大的区别。

11.4块设备驱动编程

块设备通常指一些需要以块(如512字节)的方式写入的设备,如IDE硬盘、SCSI硬盘、光驱等。它的驱动程序的编写过程与字符型设备驱动程序的编写有很大的区别。

块设备驱动编程接口相对复杂,不如字符设备明晰易用。块设备驱动程序对整个系统的性能影响较大,速度和效率是设计块设备驱动程要重点考虑的问题。系统中使用缓冲区与访问请求的优化管理(合并与重新排序)来提高系统性能。

1.编程流程说明

块设备驱动程序的编写流程同字符设备驱动程序的编写流程很类似,也包括了注册和使用两部分。但与字符驱动设备所不同的是,块设备驱动程序包括一个request请求队列。它是当内核安排一次数据传输时在列表中的一个请求队列,以最大化系统性能为原则进行排序。在后面的读写操作时会详细讲解这个函数,图11.5为块设备驱动程序的流程图,请读者注意与字符设备驱动程序的区别。

图11.5块设备驱动程序流程图

2.重要数据结构

每个块设备物理实体由一个gendisk结构体来表示(在</linux/genhd.h>中定义),每个gendisk可以支持多个分区。

每个gendisk中包含了本物理实体的全部信息以及操作函数接口。整个块设备的注册过程是围绕gendisk来展开的。在驱动程序中需要初始化的gendisk的一些成员如下所示。

structgendisk

{

intmajor;/*主设备号*/

intfirst_minor;/*第一个次设备号*/

intminors;/*次设备号个数,一个块设备至少需要使用一个次设备号,而且块设

备的每个分区都需要一个次设备号,因此这个成员等于1,则表明该块

设备是不可被分区的,否则可以包含minors–1个分区。*/

chardisk_name[32];/*块设备名称,在/proc/partions中显示*/

structhd_struct**part;/*分区表*/

structblock_device_operations*fops;/*块设备操作接口,与字符设备的

file_operations结构对应*/

structrequest_queue*queue;/*I/O请求队列*/

void*private_data;/*指向驱动程序私有数据*/

sector_tcapacity;/*块设备可包含的扇区数*/

……/*其他省略*/

};

与字符设备驱动程序一样,块设备驱动程序也包含一个在<linux/fs.h>中定义的block_device_operations结构,其定义如下所示。

structblock_device_operations

{

int(*open)(structinode*,structfile*);

int(*release)(structinode*,structfile*);

int(*ioctl)(structinode*,structfile*,unsigned,unsignedlong);

long(*unlocked_ioctl)(structfile*,unsigned,unsignedlong);

long(*compat_ioctl)(structfile*,unsigned,unsignedlong);

int(*direct_access)(structblock_device*,sector_t,unsignedlong*);

int(*media_changed)(structgendisk*);

int(*revalidate_disk)(structgendisk*);

int(*getgeo)(structblock_device*,structhd_geometry*);

structmodule*owner;

};

从该结构的定义中,可以看出块设备并不提供read()、write()等函数接口。对块设备的读写请求都是以异步方式发送到设备相关的request队列之中。

3.块设备注册和初始化

块设备的初始化过程要比字符设备复杂,它既需要像字符设备一样在加载内核时完成一定的工作,还需要在内核编译时增加一些内容。块设备驱动程序初始化时,由驱动程序的init()完成。

块设备的初始化过程如图11.6所示。

图11.6块设备驱动程序初始化过程

(1)向内核注册。

使用register_blkdev()函数对设备进行注册。

intregister_blkdev(unsignedintmajor,constchar*name);

其中参数major为要注册的块设备的主设备号,如果其值等于0,则系统动态分配并返回主设备号。参数name为设备名,在/proc/devices中显示。如果出错,则该函数返回负值。

与其对应的块设备的注销函数为unregister_blkdev(),其格式如下所示。

intunregister_blkdev(unsignedintmajor,constchar*name);

其参数必须与注册函数中的参数相同。如果出错则返回负值。

(2)申请并初始化请求队列。

这一步要调用blk_init_queue()函数来申请并初始化请求队列,其格式如下所示。

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

其中参数rfn是请求队列的处理函数指针,它负责执行块设备的读、写请求。参数lock为自旋锁,用于控制对所分配的队列的访问。

(3)初始化并注册gendisk结构。

内核提供的gendisk结构相关函数如表11-16所示。

表11-16 gendisk结构相关函数

函数格式

说明

structgendisk*alloc_disk(intminors)

动态分配gendisk结构,参数为次设备号的个数

voidadd_disk(structgendisk*disk)

向系统注册gendisk结构

voiddel_gendisk(structgendisk*disk)

从系统注销gendisk结构

首先使用alloc_disk()函数动态分配gendisk结构,接下来,对gendisk结构的主设备号(major)、次设备号相关成员(first_minor和minors)、块设备操作函数(fops)、请求队列(queue)、可包含的扇区数(capacity)以及设备名称(disk_name)等成员进行初始化。

在完成对gendisk的分配和初始化之后,调用add_disk()函数向系统注册块设备。在卸载gendisk结构的时候,要调用del_gendisk()函数。

4.块设备请求处理

块设备驱动中一般要实现一个请求队列处理函数来处理队列中的请求。从块设备的运行流程,可知请求处理是块设备的基本处理单位,也是最核心的部分。对块设备的读写操作被封装到了每一个请求中。

已经提过调用blk_init_queue()函数来申请并初始化请求队列。表11-17列出了一些与请求处理相关的函数。

表11-17 请求处理相关函数

函数格式

说明

request_queue_t*blk_alloc_queue(intgfp_mask)

分配请求队列

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

分配并初始化请求队列

structrequest*blk_get_request
(request_queue_t*q,intrw,intgfp_mask)

从队列中获取一个请求

voidblk_requeue_request(request_queue_t*q,structrequest*rq)

将请求再次加入队列

voidblk_queue_max_sectors
(request_queue_t*q,unsignedshortmax_sectors)

设置最大访问扇区数

voidblk_queue_max_phys_segments
(request_queue_t*q,unsignedshortmax_segments)

设置最大物理段数

voidend_request(structrequest*req,intuptodate)

结束本次请求处理

voidblk_queue_hardsect_size
(request_queue_t*q,unsignedshortsize)

设置物理扇区大小

以上简单地介绍了块设备驱动编程的最基本的概念和流程。更深入的内容不是本书的重点,有兴趣的读者可以参考其他书籍。

本站声明: 本文章由作者或相关机构授权发布,目的在于传递更多信息,并不代表本站赞同其观点,本站亦不保证或承诺内容真实性等。需要转载请联系该专栏作者,如若文章内容侵犯您的权益,请及时联系本站删除。
换一批
延伸阅读

嵌入式 linux将是下述内容的主要介绍对象,通过这篇文章,小编希望大家可以对它的相关情况以及信息有所认识和了解,详细内容如下。

关键字: 嵌入式 嵌入式linux Linux

嵌入式系统已经成为我们日常生活中不可或缺的一部分,它们存在于我们的手机、家用电器、汽车、医疗设备等各个领域。而Linux操作系统在嵌入式领域中也占据着重要地位。本文将深入探讨嵌入式Linux的特点和特性,以及如何学习这一...

关键字: 嵌入式系统 Linux操作系统 设备驱动

摘要:基于Linux字符设备操作接口和各类串行总线的共,性,按照分层的思想,抽象出各种总线的统一接口。统一接口的应用层API与底层的具体总线操作形式无关,而且便于应用系统的升级和移植。文中给出了一种多种串行总线统一接口的...

关键字: 统一接口 嵌入式系统 Linux 设备驱动 串行总线

一般来讲,我们平时了解到的linux的是指linux语言,其实不是。

关键字: Linux 嵌入式linux

好的单片机编程软件受到众多开发人员青睐,而对单片机编程软件了解较多的朋友都知道,目前市场上主要流通的单片机编程软件为Keil和IAR。本文中,主要为大家讲解IAR单片机编程软件的基础教程。如果你对IAR存在一定兴趣,不妨...

关键字: iar 单片机编程软件 基础教程

所谓动态内存分配(Dynamic Memory Allocation)就是指在程序执行的过程中动态地分配或者回收存储空间的分配内存的方法。动态内存分配不像数组等静态内存分配方法那样需要预先分配存储空间,而是由系统根据程序...

关键字: 内存 动态内存 基础教程 存储空间

PC在刚诞生的时候,其实是没有GPU的,所有的图形计算都由CPU来计算。后来人们意识到CPU做图形计算太慢了,于是他们设计了专门的图形加速卡用来帮忙处理图形计算,再后来,NVIDIA提出了GPU的概念,将GPU提升到了一...

关键字: CPU GPU pc 基础教程

我们都知道固态硬盘采用闪存颗粒NAND Flash作为存储介质,所以它是固态硬盘中最重要的构成部分,其好坏也就决定着固态硬盘质量的好坏,而我们目前常见的NAND闪存主要有四种类型:Single Level Cell(SL...

关键字: qlc 固态硬盘 基础教程 闪存芯片

随着电子技术、无线通信技术的蓬勃发展,出现了各种非接触式无线数据传输标准。技术的不断推进升级,使得人们对这些标准提出了更加严格的要求,一种低成本实现非接触式无线数据传输的技术也由此应运而生。

关键字: NFC 基础教程 无线传输 无线技术

HDMI(高清晰度多媒体接口)是首个也是业界唯一支持的不压缩全数字的音频/ 视频接口。HDMI 通过在一条线缆中传输高清晰、全数字的音频和视频内容,极大简化了布线,为消费者提供最高质量的家庭影院体验。HDMI 在单线缆中...

关键字: dtv hdmi 基础教程 音频接口
关闭
关闭