虚拟块设备驱动程序设计与分析
设备驱动程序在嵌入式Linux系统中的实现分析

1 引言
设备驱 动程 序是操 作系 统 内核 和机器硬 件之 间 的接 口 , 为应用 和设备 间 的软件层 , 作 为应用 程序屏 蔽 了硬 件 的细 节 。在 Lnx系统 中 , 件 设 备 只是 iu 硬
一
备 的操作 和控 制 , 须 分析 驱 动程 序 的结构 和 实 现 必
原理。
4 N G n Yi g
【bt c】 T ippr e r e t pr ne f egi i ri t bde L u s m a w l sh s A s at r h ae d c b em o ac o ds n g re e m e d i x yt , s e e ac s s i s h i t i n d v sn h e d n s e l t bi a
・
48 ・
第 1 ・ 2期 0卷 第
王莹 : 设备驱动程序 在嵌入式 Ln x系统 中的实现分析 iu
21 0 0年 4月
可 以分 为 5个部 分 :
件 f. s h里定 义 的 fe oeai s 构 , i — Drt n 结 l o 它包 含 一 系 列 函数指 针 , 这些 函数 指针指 向对设备 的各 种操 作 。
设备 。
2 设备驱动程序实现原理
设备 驱动程 序设计 是嵌 入式 Ln x开发 中重要 iu
பைடு நூலகம்
的部分 , 驱动程序是应用程序与硬件之间的一个中
间软件层 , 应该 为应用 程序展 现硬件 的所有 功能 , 不
2. 驱动程 序 的基 本结构 2
嵌 入式 Ln x 备 驱 动 程 序 都有 一 些 共 性 , iu 设 编 写所有类 型 的驱动 程序 都 是 通用 的 , 作 系统 提 供 操
《嵌入式软件设计》实验报告-9_1 模块驱动_1107082116_陈堃霖

实验序号:9_1实验项目名称:模块驱动设计
学 号
XXX
姓 名
XXX
专业、班
物联网
实验地点
实1-318
指导教师
XXX
实验时间
2013-12-6
一、实验目的
1.学习在LINUX下进行驱动设计的原理
2.掌握使用模块方式进行驱动开发调试的过程
二、实验设备(环境)及要求
硬件:PC机;PXA270试验箱
#define DEVICE_NAME"UP-TECH DEMO"
#define DEMORAW_MINOR1
#define DEMO_Devfs_path"demo/0"
static int demoMajor = 0;
(6)用mknod命令建立demo设备节点
mkdir /dev/demo(注意:2.4kernel不需要建这个路径)
Cat /proc/devices
结果如下:
Character devices:
…
180 usb
253 UP-TECH DEMO
254 pcmcia
BlockБайду номын сангаасdevices:
7 loop
8 sd
31 mtdblock…
其中的253就是主设备号;UP-TECH DEMO是设备名称。这在demo.c里有定义:
(2)使用以下命令编译2.6kernel的驱动程序,编译出来的模块是demo.ko,测试程序是test_demo
make
(3)将demo.ko和test_demo传送到目标机,并修改test_demo权限为可执行
第8章 嵌入式设备驱动程序设计(新)1

4、设备驱动程序加载与卸载的 工作过程
8.1.4 设备驱动程序的功能接口 函数模块
一个设备驱动程序模块包含有 5个部分的功能接口函数:
• • • • • (1)驱动程序的注册与释放; (2)设备的打开与关闭; (3)设备的读写操作; (4)设备的控件操作; (5)设备的中断或轮询处理。
1、设备驱动程序的注册与释放
4、加载驱动程序
• 使用insmod命令加载驱动程序。 # insmod demo_drv.o
5、卸载驱动程序
• 使用rmmod命令卸载驱动程序。 # rmmod demo_drv
6、编写用户测试程序
【例8-3】编写一个调用设备驱动程 序功能接口的用户程序。
• 源程序见教材, • 将其保存文件为:test_driver.c 。 • 用arm-linux-gcc对在宿主机上测试, 则用gcc编译)。 # arm-linux-gcc –o test_demo_drv test_driver.c
第8章 嵌入式设备驱动程序设计
本章要点
• 1、设备驱动程序基础知识 • 2、设备驱动程序设计
8.1嵌入式设备驱动程序基础
8.1.1
设备驱动程序概述
1、设备文件
• 设备文件分为三类:字符设备文件、 块设备文件和网络接口设备文件。
2、内核空间和用户空间
• 内核主要负责操作系统最基本的内存管理、 进程调度和文件管理以及虚拟内存、需求 加载、TCP/IP网络功能等。 • 内核空间和用户空间分别引用不同的内存 映射,也就是程序代码使用不同的地址空 间。
3、设备驱动程序和用户应用程序
• 设备驱动程序可以理解为操作系统的一部 分,它的作用就是让操作系统能正确识别 和使用设备。
嵌入式系统中的驱动程序设计与实现

嵌入式系统中的驱动程序设计与实现第一章:嵌入式系统概述嵌入式系统是一种专用型计算机系统,通常包含微处理器、存储器、输入/输出接口和其他外围设备。
这些系统被设计用于执行特定的任务或实现特定的功能。
相对于一般的计算机系统,嵌入式系统通常更加小巧、节能、稳定和高效。
嵌入式系统的应用领域非常广泛,涉及到自动控制、计算机网络、医疗、工业自动化、汽车电子、智能家居等众多领域。
从智能手机和平板电脑,到高铁和飞机上的控制系统,嵌入式系统已经成为现代社会中不可或缺的一部分。
在开发嵌入式系统时,驱动程序是一个非常重要的部分。
驱动程序是一种软件模块,用于控制硬件设备的操作和管理。
它将应用程序与底层硬件之间进行了有效的沟通。
在接下来的章节中,我们将详细介绍嵌入式系统中的驱动程序设计与实现。
第二章:驱动程序的架构嵌入式系统中的驱动程序通常包含两个部分:设备驱动和主程序。
设备驱动负责控制硬件设备的操作和管理。
它向主程序提供硬件抽象层,屏蔽了硬件底层的细节。
主程序则利用设备驱动提供的接口,完成相应的应用功能。
驱动程序的架构通常遵循一般软件工程的设计原则,实现结构分层、模块化、可复用的代码。
设备驱动可以按照不同的硬件设备进行分类,比如网络设备驱动、磁盘设备驱动、串口设备驱动等。
在实现时,可以采用面向对象编程思想,使得代码的设计更加清晰明了。
第三章:驱动程序的实现实现驱动程序的过程通常可以分为以下四个步骤:1. 设备地址映射在计算机系统中,设备通常被映射到一定的地址空间中。
驱动程序需要获取设备的物理地址,并将其映射到操作系统的虚拟地址空间中。
这样,驱动程序才能正确地与硬件设备进行交互。
2. 硬件的初始化和配置在设备地址映射成功后,驱动程序需要对硬件进行初始化和配置,以确保硬件设备能够正常运行。
比如,对于一个串口设备,驱动程序需要配置波特率、数据位、校验位等参数。
3. 设备操作的实现驱动程序的核心是硬件设备的操作函数实现。
驱动程序需要对不同的设备类型实现不同的操作函数,例如对于网络设备,包括接收和发送数据的实现;对于磁盘设备,包括读写数据的实现。
基于嵌入式Linux的SD卡驱动程序的设计与实现 文字版

re turn -1; // 注册块设备驱动程序 blk_init_que ue (BLK_DEFAULT_QUEUE(ma jor),s d_re que s t); blk_de v[ma jor].re que s t_que ue .re que s t_fn = s d_re que s t; // 设置块设备的请求传输函数 re a d_a he a d[ma jor] = 10; s d_s ize s = kma lloc(3*s ize of(int),GFP _KERNEL); mmc_blk_s ize s = mmc_s ize s + 1; mmc_ha rd_s e cts = mmc_s ize s + 2; *mmc_s ize s = s d_info.s ize /1024; *s d_blk_s ize s = 1024; *s d_ha rd_s e cts = s d_info.ha rd_s e ct; blk_s ize [ma jor] = s d_s ize s ; blks ize _s ize [ma jor] = s d_blk_s ize s ; ha rds e ct_s ize [ma jor] = s d_ha rd_s e cts ; // 设置设备管理的设备大小、扇区大小信息 re turn 0; } 注册的主要工作是初始化 SD 卡,向内核注册块设备以 及设置块设备的信息。注册之后,L inux 在有数据要进行读 写的时候,会调用驱动程序的 request函数。下面是该函数的 伪代码: s ta tic void mmc_re que s t(re que s t_que ue _t *q) { uns igne d long nr; int blk; while (1) { INIT_REQUES T; // 检查缓冲队列是否为空 s witch(CURRENT->cmd) { ca s e READ:
Win98下虚拟设备驱动程序的设计与实现

文章编号:1009-671X (2001)09-0035-04Win 98下虚拟设备驱动程序的设计与实现王 磊1,朱齐丹1,温 强2(1.哈尔滨工程大学自动化学院,黑龙江哈尔滨150001;2.哈尔滨工程大学机电工程学院,黑龙江哈尔滨 150001)摘 要:介绍了电话网络测试系统的组成和结构,Windows 98的内核管理机制和应用程序权限级别,简述了在Windows98下开发虚拟驱动程序的几种方法,并给出了在电话网络测试系统中应用虚拟设备驱动程序VxD 实例。
①关 键 词:虚拟设备驱动程序;VxD ;硬件中断;电话网络测试系统中图分类号:TP39.6 文献标识码:ADesign and Actu alization of Visu al Device Driver Program in Win 98WAN G Lei 1,ZHU Qi-dan 1,WEN Qiang 2(1.Automation College ,Harbin Engineering University ,Harbin 150001,China ;2.Mechanical and Electrical En gineering Col 2lege ,Harbin Engineering University ,Harbin 150001,China )Abstract :The structure and buildup of the telephone net test system ,the kernel management of Windows 98and the right limit of the applied program were introduced.Several ways of developing visual device driver program in Win 98were depicted.A developing example of using VxD in the telephone net test sys 2tem was given.K ey w ords :visual device driver program ;VxD ;hardware interruption ;telephone net test system0 引 言从Windows 3.1开始,对于硬件设备的访问便采取了一种设备驱动程序的方法,客户通过设备驱动程序来获得硬件的参数或者设置,但是由于16位的操作系统基于原来的DOS ,所以客户程序仍然可以通过一些B IOS 或者DOS 的DPM I 中断调用来实现对硬件的操作。
《虚拟仪器与LabVIEW程序设计》章节思考与练习题含答案(大学期末复习资料)

第1章虚拟仪器概述1.测试测量仪器发展至今经过了那些阶段?答:经历了4个阶段,即:第一代模拟式仪器(或指针式仪器)、第二代数字式仪器、第三代智能仪器、第四代虚拟仪器。
2.什么是虚拟仪器,它有哪些特点?答:虚拟仪器是指在以计算机为核心的硬件平台上,其功能由用户设计和定义,具有虚拟仪器面板,其测试功能由测试软件实现的一种计算机仪器系统。
特点:虚拟含义主要有两点:1、仪器面板是虚拟的,通过调用控件选板中的控件实现3.简述虚拟仪器的系统组成?答:虚拟仪器系统由硬件平台和软件平台两大部分完成:硬件平台:计算机、I/O接口设备;软件平台:4.简述虚拟仪器的软件层次结构?答:测试管理层:用户及仪器设备等管理。
应用程序开发层:用户根据仪器功能需求开发设计的虚拟仪器程序。
仪器驱动层:完成对特定仪器的控制和通信的程序集合。
I/O总线驱动层:完成对仪器寄存器进行直接存储数据操作,并为仪器设备与仪器驱动程序提供信息传递的底层软件。
第2章一个简单VI的设计1.输入两个数,求两个数的和差运算,并显示结果。
2.程序运行中,用旋钮控件改变图形曲线的颜色。
建立波形图表的属性节点,改为可写,并指定为曲线Plot的颜色Color属性。
第3章几种常用的程序结构1.创建一个VI产生100个随机数,求其最小值和平均值。
2.创建一个VI,每秒显示一个0到1之间的随机数。
同时,计算并显示产生的最后四个随机数的平均值。
只有产生4个数以后才显示平均值,否则显示0。
每次随机数大于0.5时,使用Beep.vi产生蜂鸣声。
3.求X的立方和(使用For和While循环)。
4.编程求1000内的“完数”。
“完数”指一个数恰好等于它本身的因子之和。
例如28=14+7+4+2+1。
5.创建一个VI ,实现加、减、乘、除四种运算方式。
6.编写一个程序测试输入以下字符所用的时间:LabVIEW is a graphical programming language.7.使用公式节点创建VI ,完成下面公式计算,并将结果显示在同一个屏幕上。
设备驱动程序

设备驱动程序与设备密切相关的代码放在设备驱动程序中,每个设备驱动程序处理一种设备类型,例如,即使系统支持若干不同商标的终端,只要其差别不大,就可以设计一个终端驱动程序。
但是,若系统支持的终端性能差别很大,如不灵活的硬拷贝终端与带有小鼠标的智能位映象图形终端,则必须设计不同的终端驱动程序。
上一节我们介绍了设备控制器做的工作,知道每一个控制器都设有一个或多个设备寄存器,用来存放向设备发送的命令和参数。
设备驱动程序负责泄放这些命令,并监督它们正确执行。
因此,磁盘驱动程序是操作系统中唯一知道磁盘控制器设置有多少寄存器以及这些寄存器作用的。
只有它才了解磁盘拥有的扇区数、磁道数、柱面数、磁头数、臂的移动、磁盘交叉访问系数、马达驱动器,磁头稳定时间和其它所有保证磁盘正常工作的机制。
一般,设备驱动程序的任务是接收来自与设备无关的上层软件的抽象请求,并执行这个请求。
一个典型的请求是“读第几块”。
如果请求到来时,驱动程序的进程空闲,它立即开始执行这个请求;若驱动程序的进程正在执行一个请求,这时它将新到来的请求排到一个等待处理的I/O请求队列中,待正执行的请求完成后,再依次从I/O请求队列中取出一个个I /O请求,逐个处理。
以磁盘为例,实际实现一个I/O请求的第一步是将这个抽象请求(READ(文件名,记录号))转换成磁盘的具体参数。
对于磁盘驱动程序来说,就是计算请求块实际在磁盘的位置,检查驱动器的马达是否正在运转,确定磁头是否定位在正确的柱面上等等。
总之,它必面决定需要控制器的哪些操作,以及按照什么样的次序实现。
一旦明确应向控制器发送哪些命令,它就向控制器一次只能接收一条命令(如DMA方式下),有一些控制器则接收一个命令链表(通道方式下),然后自行控制执行,不再求助于操作系统。
在设备驱动程序的进程泄放一条或多条命令后,系统有两种处理方式,多数情况下,执行设备驱动程序的进程必须等待命令完成。
这样,在命令开始执行后,它阻塞自已,直到中断处理时将它解除阻塞为止。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
如果只是为了应付考试,这个文档就太啰嗦了,不用看,不过还是可以帮助记忆,考试只会考其中加粗字体的几个函数中的一个,至于是哪个我不能断定,因此要记的还是比较多的,要是能理解就更好了,结合课本和下面的解释应该能大体上弄明白这个虚拟块设备驱动的实现过程,毕竟设备驱动是内核的一部分,光看下面的解释也是还是很头晕的,不过坚持看下去还是有收获的,我也差不多花了半天时间,不过,要是打算……的话就可以直接跳过了。
#define MAJOR_NR 70 //我们创造的虚拟块设备的主设备号#define DEVICE_NAME “bdemo”//我们创造的虚拟块设备的名字,当设备加载成功后可用lsmod命令查看到该设备模块名#define blkdemo_devs 2 //虚拟块设备的个数#define blkdemo_rahead 2 //读取块设备时预读的扇区个数#define blkdemo_size 4 //每个虚拟块设备的大小,单位为KB#define blkdemo_blksize 1024 //设备每个数据块的大小,即block,单位为字节#define blkdemo_hardsect 512 //设备每个扇区的大小,单位为字节struct blkdemo_device { // 这里定义了我们将要创造的虚拟块设备的数据结构int size; // 用来记录真实块设备的容量,即下面data指针所指向数据存储区的大小int use_cnt; // 用来记录正在使用该块设备的程序的个数int hardsect; // 用来保存该块设备每个扇区的大小,单位为字节,即设备的使用计数u8 *data; // 该指针所指向的内存区域就是该块设备真正用来存储数据的区域,在该设备还未被加载函数初始化时,该指针为// 空,即系统还没有为该设备分配内存区域。
};static int blkdemo_sizes[blkdemo_devs]; //用来保存我们创建的所有虚拟块设备的大小,单位为KBstatic int blkdemo_blksizes[blkdemo_devs]; //用来保存我们创建的所有虚拟块设备中每个数据块的大小,单位为字节static int blkdemo_hardsects[blkdemo_devs];//用来保存我们创建的所有虚拟块设备中每个扇区的大小,单位为字节//上面的这三个数组将会在我们加载这些设备时被注册到内核的数据结构中(即让内核中与之相关的一些指针指向它们,让内核能够读取我们所创建的设备的一些重要信息//对于一个新的设备,内核肯定不知道他为何物,要想让内核识别我们自己创造的设备,则必须将该设备的一些信息、使用这个设备的方//法等告诉内核,由于内核早已编译成型,至于如何去告诉内核就早已模式化。
内核中有几个指针数组(书本page81)专门用来完成上面的部分任务:// blk_size[];// blksize_size[];// hardsect_size[];// read_ahead[];//这几个数组都为每一个主设备号留有一个位置,对于2.4的内核,主设备号和次设备号均用8位二进制来表示(即短整型的高八位和//低八位),因此这几个数组都包含有256个元素,每个元素都是与主设备号对应的一个指针,如果主设备号所对应的设备不存在,则该//指针置为空(NULL),其实其中很多指针都为空,因为一般电脑上都没有那么多不同类型的块设备,当然,对于我们所创造的这个块设//备而言,它与系统中所存在的其他块设备的类型都不同,要为其确定一个主设备号,这个没什么硬性的规定,只要找一个没被使用的主//设备号就可以了,这个程序中使用的是70(前面的MOJOR_NR宏)。
上面我们定义了保存有虚拟块设备信息的数组,现在只要将他们的//首地址赋给这几个数组中下标70(主设备号)所对应的指针元素即可。
这一过程是在后面的加载函数中完成的。
static int blksize = blkdemo_blksize;struct blkdemo_device blkdemo_dev[blkdemo_devs];//这里才真正创建了我们虚拟块设备对应的结构体变量(一个全局数组),//每个元素为对应一个虚拟块设备虚拟块设备的打开函数(open()):int blkdemo_open(struct inode *inode, strcut file *filp){ //设备文件对应的节点(inode)结构中包含有对应的设备号int num;num = DEVICE_NR(inode->i_rdev);//用DEVICE_NR宏可求出该节点所对应设备的次设备号,所以num即为次设备号if (!blkdemo_dev[num].use_cnt) { //如果该设备的使用计数为0,则说该设备没有被任何程序使用,当虚拟块设备没有被//任何程序使用时,内核先前为该设备所分配的存储区很可能已经被释放掉了,甚至对于可移动设备而言,有可能该设备都被拔掉了(当//然,我们的虚拟块设备是不可能的),因此,在打开该设备时要进行严格的检查,不然会导致设备打开出错而造成系统崩溃。
check_disk_change(inode->i_rdev);//首先检查该块设备是否发生了变化,比如已经被移除了(该设备不可能,所以//此处没有用if来判断,只是形式的调用了一下该函数。
if (!blkdemo_dev[num].data)//然后判断该设备的数据存储区域是否已经被释放掉了return –ENOMEM; //如果是,则返回,告知系统该设备无法打开,-ENOMEM是一个内核中定义的宏,它代表的意思是//“error,no memory”。
}//如果上述情况均未发生,一切正常,则打开设备,对于这个虚拟的块设备,其实没有什么好打开的,不过还是意思一下:blkdemo_dev[num].use_cnt++; //将设备的使用计数加1,表示又多了个程序使用该设备。
MOD_INC_USE_COUNT; //并且将内核所管理的模块使用计数也加1,好让内核也知道多了一个程序使用该虚拟设备模块。
模块使//用计数是内核管理模块时要用的,只有当一个设备的模块使用计数为0时才能卸载该模块,这个值也可以通过lsmod命令查看到return(0);//返回0,表示设备已成功打开}虚拟块设备的释放函数(release()):int blkdemo_release(struct inode *inode, struct file *filp){//释放并不代表将此设备从内核中移除了,他是对调用它的程序而言的,只表示这个程序不再使用该设备了int num;num = DEVICE_NR(inode->i_rdev);//求出设备的次设备号blkdemo_dev[num].use_cnt--;//既然使用该设备的程序少了一个,则应该将该设备的使用计数减1MOD_DEC_USE_COUNT;// 并且将内核所管理的模块使用计数也减1return(0);//返回0,表示设备释放成功}虚拟块设备的请求函数(request()):void blkdemo_request(request_queue_t *q){ //块设备和字符设备在数据的读写是有区别的。
对于块设备,程序对数据读写的请求一般不会立即得到回应,程序首先要提出对数据//的请求,此时内核会动态的分配一个request结构(page74,图7-1中有request结构的抽象描述),并将请求的详细信息记录到//这个request结构中,然后将这个结构按照某些规则插入到该设备的请求队列中,当系统处于较为合适的状态时,内核就会对队列上//的所有请求进行集中处理。
采用这些繁琐的方法都由真实块设备的物理特性决定的,因为大部分块设备都和硬盘类似,读取数据时要进//行寻道等一些复杂的命令操作,而这是一个相当耗时的过程,如果每当有程序请求数据时内核就立即去操作磁盘,那么系统大部分宝贵//的时间都被消耗在了等待磁盘响应上了,因此内核中构建了一套专门操作块设备的方法,来对请求的数据进行集中处理,以提高磁盘的//吞吐量和系统的整体性能,request()函数的任务就是按顺序处理这条请求队列,直到队尾,//除非出现意外错误而返回。
struct request *req;int res = 1; //用来记录对当前请求的处理是否成功,成功则置1,失败则清0,供后面的end_request()函数使用。
int num;int size;u8 *ptr;while (1) {INIT_REQUEST;//测试当前的请求是否有效req = CURRENT;//CURRENT指针由内核中的end_request()函数管理,它指向请求队列中当前要处理的request结构。
num = DEVICE_NR(req->rq_dev);//获取所请求的设备的次设备号ptr = blkdemo_dev[num].data + req->sector * blkdemo_dev[num].hardsect;// 设备数据存储区的首地址 + 请求的首个扇区 * 该设备每个扇区的字节大小,最后,ptr指向所要请求的数据size = req->current_nr_sectors * blkdemo_dev[num].hardsect;// 当前请求的扇区总数 * 该设备每个扇区的字节大小,因此size为当前请求所请求的总字节数if (ptr + size > blkdemo_dev[num].data + blkdemo_dev[num].size) { //判断所请求数据的地址是否超出//了该设备的数据存储区的范围。
超出范围后会导致内存溢出,造成系统崩溃,决不能容忍。
printk(KERN_WARNNING “blkdemo: request past end of device\n”);//向控制台或日志打印出警告信息res = 0; //请求失败,res置0}//如果正常则下面打印出当前请求的详细信息(仅调试时使用,可以不写)printk(“<1> request %p: cmd %i sec %li (nr.%li)\n”, req,//<1>和KERN_ALERT是等价的,这是内核中定req->cmd, req->sector, req->current_nr_sectors);//义的8种日志级别宏之一(page43)switch (cmd) { //判断当前请求要对所请求的数据做何种操作case READ://如果是读,则,memcpy(req->buffer,ptr, size);//把从ptr开始的size字节复制到发出该请求的程序所提供的缓冲区中去res = 1;//完成了请求,res置1break;case WRITE://如果是写,则,memcpy(ptr, req->buffer, size);//把发出该请求的程序的缓冲区中的数据复制到该设备中ptr所指向的内存区res = 1; //完成了请求,res置1break;default: //未知请求res = 0;//无法完成,res置0}end_request(res);//根据请求是否成功来调整CURRENT指针变量的值,为处理请求队列中的下一个请求作准备}}struct block_device_operations blkdemo_bdops = { //初始化虚拟块设备操作函数接口open: blkdemo_open,release: blkdemo_release,};虚拟块设备的加载函数:static int __init blkdemo_init(void)//__init为加载函数标志,用此标志修饰的函数只能在模块被插入内核由内核调用{int i;int ret;ret = devfs_register_blkdev(MAJOR_NR, DEVICE_NAME, &blkdemo_bdops);//注册块设备,该函数成功时返回主设//备号,失败时返回负值,当参数中主设备号MAJOR_NR为0时,自动为设备分配主设备号,非0时使用MAJOR_NR指定的主设备号if (ret < 0) {//如果返回值小于0,说明设备注册失败printk(KERN_WARNNING “devfs_register_blkdev() failed\n”);//打印出警告信息return(ret);//返回错误代号}if (MAJOR_NR == 0)//如果MAJOR_NR为0,则blkdemo_major = ret; //使用系统自动分配的主设备号else //否则blkdemo_major = MAJOR_NR;//直接使用我们指定的主设备号blk_init_queue(BLK_DEFAULT_QUEUE(blkdemo_major), blkdemo_request);//内核为每个主设备号都保留了一个请求//队列,也是通过一个数组实现的,BLK_DEFAULT_QUEUE能返回该默认的请求队列,blk_init_queue()函数通过创建一个请求队列//头将该队列和处理该请求队列的request()关联起来// 下面的就开始将我们所创造的虚拟块设备的信息告诉系统:read_ahead[blkdemo_major] = blkdemo_rahead; //告诉系统该类型块设备的预读扇区数for (i = 0; i < blkdemo_devs; i++)blkdemo_sizes[i] = blkdemo_size;//确定每个块设备的大小,以KB为单位blk_size[blkdemo_major] = blkdemo_sizes;//告诉系统保存有这些块设备的大小的数组的内存首地址for (i = 0; i < blkdemo_devs; i++)blkdemo_blksizes[i] = blkdemo_blksize;//确定每个块设备的每个数据块的大小,以字节为单位blksize_size[blkdemo_major] = blkdemo_blksizes;//告诉系统保存有这些块设备的每个数据块大小的数组的首地址for (i = 0; i < blkdemo_devs; i++)blkdemo_hardsects[i] = blkdemo_hardsect; //确定每个块设备的每个扇区的大小,以字节为单位hardsect_size[blkdemo_major] = blkdemo_hardsects;//告诉系统保存有这些块设备的每个扇区大小的数组的首地址for (i = 0; i < blkdemo_devs; i++)register_disk(NULL, MKDEV(blkdemo_major, i), 1, &blkdemo_bdops,//注册每个块设备分区。