实验七(补充)学写块设备驱动

合集下载

实验十三 块设备驱动

实验十三 块设备驱动

实验十三块设备驱动实验目的熟悉块设备驱动程序的架构;掌握编写快设备驱动程序的流程和方法;实验步骤1 填空完善下面的块设备驱动程序my_ramblock.c,驱动程序如下:#include <linux/module.h>#include <linux/errno.h>#include <linux/interrupt.h>#include <linux/mm.h>#include <linux/fs.h>#include <linux/kernel.h>#include <linux/timer.h>#include <linux/genhd.h>#include <linux/hdreg.h>#include <linux/ioport.h>#include <linux/init.h>#include <linux/wait.h>#include <linux/blkdev.h>#include <linux/blkpg.h>#include <linux/delay.h>#include <linux/io.h>#include <linux/gfp.h>#include <linux/slab.h>#include <asm/system.h>#include <asm/uaccess.h>#include <asm/dma.h>#define MYDISK_SIZE (1024*1024)#define MYDISK_NAME "ram_sice"static DEFINE_SPINLOCK(mydisk_lock);static const struct block_device_operations mydisk_fops = { .owner = THIS_MODULE,};static struct gendisk *my_disk;static struct request_queue *my_queue;static unsigned char *ram_buffer;static int major;static void myblock_do_request(struct request_queue * q){struct request *req;req = blk_fetch_request(q); /*取出一个请求*/while (req) {unsigned long offset =blk_rq_pos(req)<<9; /*获取req的扇区位置,并乘512字节,请求里的起始地址*/unsigned long len =blk_rq_cur_bytes(req) ;/*要操作数据的大小*/if (rq_data_dir(req) == READ) /*判断是否是读操作*/memcpy(req->buffer, ram_buffer+offset, len);elsememcpy(ram_buffer+offset, req->buffer, len);if (!__blk_end_request_cur(req,0)) /*完成当前请求*/req =blk_fetch_request(q); /*取下一个请求*/}}static int my_ram_init(void){major = register_blkdev(0,MYDISK_NAME); /* 动态分配主设备号*/my_disk = alloc_disk(8); /*8为次设备号个数:分区个数+1 */my_queue = blk_init_queue(myblock_do_request,&mydisk_lock);/* 初始化队列*/ my_disk->queue = my_queue;my_disk->major = major;my_disk->first_minor = 0;sprintf(my_disk->disk_name, MYDISK_NAME);my_disk->fops = &mydisk_fops;set_capacity(my_disk, MYDISK_SIZE/512);ram_buffer = kzalloc(MYDISK_SIZE, GFP_KERNEL);add_disk(my_disk);/* 注册gendisk */return 0;}static void my_ram_exit(void){unregister_blkdev(major, MYDISK_NAME);del_gendisk(my_disk);put_disk(my_disk);blk_cleanup_queue(my_queue);kfree(ram_buffer);}module_init(my_ram_init);module_exit(my_ram_exit);MODULE_LICENSE("GPL");2 编译在开发板上插入模块insmod ramblock.ko格式化mkdosfs /dev/ram_sice挂载mkdir /tmp/rammount /dev/ram_sice /tmp/ram###这里注意,内核需要有对应的msdos文件系统支持Location:-> File systems-> DOS/FAT/NT Filesystems<*> MSDOS fs support在/tmp/ram目录下创建文件vi /tmp/ram/sss.txt卸载umount /dev/ram_sice重新挂载看看文件在不在实验完成后,可以举手叫老师过来检查,以作平时成绩加分!Makefileobj-m += ramblock.oall:make -C /home/sice/linux-3.5 M=`pwd` modules clean:make -C /home/sice/linux-3.5 M=`pwd` modules clean。

实验七 添加设备驱动_计算机操作系统实验指导(Linux版)_[共4页]

实验七 添加设备驱动_计算机操作系统实验指导(Linux版)_[共4页]

110 分是关于中断次数的。

这一部分中记录了从系统启动后到当前时刻发生的系统中断的总次数,以及各类中断分别发生的次数。

这一部分以关键字intr 开头,紧接着的一项是系统发生中断的总次数,之后依次是0 号中断发生的次数,1 号中断发生的次数……其中缺页中断是第14号中断,也就是在关键字intr 之后的第16 项。

该实验可以利用stat 文件提供的数据,在一段时间的开始时刻和结束时刻分别读取缺页中断发生的次数,然后做一个减法操作,就可以得出这段时间内发生缺页中断的次数。

由于stat文件的数据是由系统动态更新的,过去时刻的数据是无法采集到的,所以这里的开始时刻最早也只能是当前时刻,实验中采用的统计时间段就是从当前时刻开始的一段时间。

2.统计缺页中断次数由于每发生一次缺页,都要进入缺页中断服务函数do_page_fault 一次,所以可以认为执行该函数的次数就是系统发生缺页的次数。

因此可以定义一个全局变量pfcount 作为计数变量,在执行do_page_fault时,该变量值加1。

经历的时间可以利用系统原有的变量jiffies。

这是一个系统的计时器,在内核加载完以后开始计时,以10ms(默认)为计时单位。

借助/proc文件系统来读出变量的值。

在/proc文件系统下建立目录pf,以及在该目录下的文件pfcount 和jiffies。

实验提示请参阅2.5节内存管理完成该实验。

实验七 添加设备驱动实验目的(1)了解Linux设备驱动的管理方式。

(2)了解Linux设备驱动程序的组织结构和设备管理机制。

(3)掌握Linux设备驱动程序的编写方法和过程。

(4)掌握Linux设备驱动程序的加载方法。

实验内容(1)编写字符设备驱动程序,要求能对该字符设备执行打开、读、写、I/O控制和关闭5个基本操作。

(2)编写块设备驱动程序,要求能对该字符设备执行打开、读、写、I/O控制和关闭5个基本操作。

(3)编写一个应用程序,测试添加的字符设备和块设备驱动程序的正确性。

设备驱动模型实验报告(3篇)

设备驱动模型实验报告(3篇)

第1篇实验目的1. 理解Linux设备驱动模型的基本概念和结构。

2. 掌握设备驱动模型中总线、设备和驱动的交互方式。

3. 学习如何编写简单的字符设备驱动程序。

4. 熟悉Linux内核中与设备驱动模型相关的系统目录和文件。

实验环境- 操作系统:Linux- 编译器:GCC- 内核版本:Linux内核4.19- 开发工具:Makefile、内核模块编译脚本实验内容本实验主要围绕Linux设备驱动模型展开,通过实际编写一个简单的字符设备驱动程序来加深对设备驱动模型的理解。

一、实验原理Linux设备驱动模型是一种分层结构,主要包括以下几层:1. 硬件层:包括各种硬件设备。

2. 总线层:负责管理硬件设备和驱动程序之间的通信。

3. 设备层:包括各种物理设备,如硬盘、网络接口卡等。

4. 驱动层:负责与硬件设备交互,实现设备的初始化、操作等功能。

5. 用户层:通过系统调用与驱动程序交互,实现对硬件设备的操作。

在设备驱动模型中,总线、设备和驱动之间通过以下方式交互:1. 总线注册:驱动程序在初始化时,需要将自身注册到对应的总线上。

2. 设备绑定:驱动程序通过总线找到对应的设备,并将自身绑定到设备上。

3. 设备操作:用户通过系统调用与设备交互,驱动程序负责实现这些操作。

二、实验步骤1. 创建字符设备驱动程序:- 定义字符设备结构体`char_device`,包含设备名称、设备号等信息。

- 实现字符设备初始化函数`char_device_init`,负责初始化字符设备。

- 实现字符设备打开函数`char_device_open`,负责打开字符设备。

- 实现字符设备读写函数`char_device_read`和`char_device_write`,负责读写字符设备数据。

- 实现字符设备关闭函数`char_device_close`,负责关闭字符设备。

2. 注册字符设备驱动程序:- 在`init_module`函数中,注册字符设备驱动程序,包括设备名称、主设备号、次设备号等信息。

设备驱动(2)实验手册

设备驱动(2)实验手册

设备驱动(二)华清远见—嵌入式学院2008【实验内容】编写一个字符设备驱动,包含等待队列、信号量、fifo等功能 【实验目的】掌握待队列、信号量、fifo等功能的实现方法【实验平台】Ubantu7.04【实验步骤】1、将文件夹pipe复制到linux环境中,如:/home/linux/test#su root#cp pipe /home/linux/test –a2、#cd /home/linux/test/pipe3、#make4、通过insmod命令将模块加入内核#insmod pipe.ko5、建立设备结点# mknod /dev/pipe c 253 06、#cat /dev/pipe7、打开新的终端,输入#echo ”hello” > /dev/pipe8、熟悉代码,掌握实验内容【实验内容】编写一个字符设备驱动,包含等待队列、信号量、fifo、poll等功能【实验目的】掌握待poll功能的实现方法【实验平台】Ubantu7.04【实验步骤】1、文件夹poll复制到linux环境中,如:/home/linux/test#su root#cp poll /home/linux/test –a2、#cd /home/linux/test/poll3、#make4、通过insmod命令将模块加入内核#insmod globalfifo.ko5、建立设备结点# mknod /dev/globalfifo c 253 06、#cat /dev/globalfifo7、打开新的终端,输入#echo ”hello” > /dev/ globalfifo8、比较读写及fifo部分和实验一的不同。

9、编译应用程序pollmonitor.c#gcc –o pollmonitor pollmonitor.c10、#./ pollmonitor程序会不断地输出“Poll monitor:can be written”当通过echo向/dev/globalfifo写入一些数据后,将输出“Poll monitor:can be read”和“Poll monitor:can be written”;如果不断地通过echo向/dev/globalfifo写入数据直至写满FIFO,发现pollmonitor程序将只输出“Poll monitor:can be read”。

鱼树笔记之第18课块设备驱动

鱼树笔记之第18课块设备驱动
⑤,“输入子系统”:融入别人写的代码(也是上面 4 种方式写成)。
二,块设备驱动程序:
若块设备驱动程序也按以下字符设备驱动程序的简单思想来写:
APP : open,
read,
write. ----> 对应提供驱动程序的读写等函数。
-------------------------------------
总结:先不执行而是放入队列,优化后再执行(对硬盘有这种要求)。用“字符设备驱 动”程序那样读写时就会在硬盘上跳 来跳去,整体效率会非常低。所以有必要引入“优化过程”。就是读写先不执行,先放到 某个“队列”中去。(调整顺序)
2,flash:
是“块”里有一个一个的扇区。 假若现在要先写“扇区 0”和“扇区 1”。FLASH 要先擦除再写,现在用字符设备驱动的读 写方式来读写:
块设备驱动编写 ................................................................. 11
分配一段内存,用内存来模拟硬盘: ............................................. 11 大致分析: ................................................................... 11
3,再编译后测试: ........................................................ 17 五,开始试着分区: ........................................................... 21 框架: ....................................................................... 25

嵌入式系统实验7 块设备驱动实验-陈文智

嵌入式系统实验7 块设备驱动实验-陈文智

实验步骤:
(2)交叉编译 # make ARCH=arm CROSS_COMPILE=/usr/local/arm2007q1/arm-none-linux-gnueabi-
实验步骤:
3. 下载驱动模块到目标板并加载 (1)为了验证驱动的正确性,必须确保 目标板上的系统没有该驱动或者没有加 载该驱动。所有在前面编译内核时Atmel Multimedia Card Interface support应该设 为空或M(编译为模块)。如下图所示:
第7章 块设备驱动实验
实验目的:
(1)通过实验了解SD卡的工作原理 (2)通过实验掌握块设备驱动开发的特点 (3)通过实验掌握块设备驱动开发的流程
实验环境:
硬件:AT91SAM9G45-EKES开发板、SD 卡、PC机 软件:Windows 2000/NT/XP、Ubuntu 9.10、gcc、gdb、vim
实验任务:
(1)理解和掌握SD卡驱动编写。 (2)测试SD卡驱动。
实验原理:
阅读完本书上篇理论部分第11章,读者 应当知道SD卡驱动分为三层 (card/core/host),其中card和core层与硬 件无关且内核代码以提供。为了提高读 者在硬件基础上编程的能力,本实验编 写host层驱动,也就是编写9G45芯片的 HSMCI接口驱动。在做实验前,首先仔 细阅读芯片的数据手册,了解通过 HDMCI接口对SD卡进行操作的各个细节。 以下是HDMCI接口对SD卡操作的说明
实验步骤:
(4)阅读和编写驱动 A. 打开atmel-mci.c文件,按照其中的中文 提示补全代码 B. 修改原驱动,在其基础上实现到SD插 入和拔出时打印出提示信息,并在插入 时显示写保护状态。
实验步骤:

嵌入式-Linux 设备驱动实验报告

嵌入式-Linux 设备驱动实验报告

嵌入式系统实验报告Linux 设备驱动实验学院专业学生姓名实验台号指导教师提交日期一、实验目的1.了解Linux驱动程序的结构;2.掌握Linux驱动程序常用结构体和操作函数的使用方法;3.初步掌握Linux驱动程序的编写方法及过程;4.掌握Linux驱动程序的加载方法。

二、实验内容1.实现helloworld驱动,观察驱动的加载和释放过程;2.根据参考代码,分析数码显示驱动的结构和原理,给出设备程序的主要组成部分框图;3.利用数码显示驱动模块,编写测试程序实现按键对数码显示的控制,包括点亮和关闭,显示不同数字等。

三、实验原理3.1驱动程序介绍驱动程序负责将应用程序如读、写等操作正确无误的传递给相关的硬件,并使硬件能够做出正确反应的代码。

驱动程序像一个黑盒子,它隐藏了硬件的工作细节,应用程序只需要通过一组标准化的接口实现对硬件的操作。

3.2 Linux设备驱动程序分类Linux设备驱动程序在Linux的内核源代码中占有很大的比例,源代码的长度日益增加,主要是驱动程序的增加。

虽然Linux内核的不断升级,但驱动程序的结构还是相对稳定。

Linux系统的设备分为字符设备(char device),块设备(block device)和网络设备(network device)三种。

字符设备是指在存取时没有缓存的设备,而块设备的读写都有缓存来支持,并且块设备必须能够随机存取(random access)。

典型的字符设备包括鼠标,键盘,串行口等。

块设备主要包括硬盘软盘设备,CD-ROM等。

网络设备在Linux里做专门的处理。

Linux的网络系统主要是基于BSD unix的socket 机制。

在系统和驱动程序之间定义有专门的数据结构(sk_buff)进行数据传递。

系统有支持对发送数据和接收数据的缓存,提供流量控制机制,提供对多协议的支持。

3.3驱动程序的结构驱动程序的结构如图3.1所示,应用程序经过系统调用,进入核心层,内核要控制硬件需要通过驱动程序实现,驱动程序相当于内核与硬件之间的“系统调用”。

块设备驱动 知识点

块设备驱动 知识点

块设备驱动知识点块设备驱动是存储设备驱动的一种,用于操作硬盘类的存储设备。

以下是关于块设备驱动的一些知识点:1. 块设备:块设备是指能够随机访问固定大小数据片的设备,这些设备通常以安装文件系统的方式使用。

块设备文件中的数据是以块为单位进行传输的,每个块都有自己的地址,可以在设备的任意位置读取一定长度的数据。

常见的块设备包括硬盘、U盘、SD卡等。

2. 块设备驱动的作用:块设备驱动的主要作用是管理块设备的读写操作,将上层应用对块设备的操作转化为对底层物理磁盘的操作。

块设备驱动需要实现对块设备的打开、关闭、读写等操作,并处理可能出现的错误和异常情况。

3. 块设备驱动的特点:块设备驱动的特点是只能以块为单位进行读写访问,块是Linux虚拟文件系统(VFS)基本的数据传输单位。

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

4. 块设备驱动的实现:块设备驱动的实现通常涉及到对底层物理磁盘的读写操作,以及对上层文件系统的支持。

块设备驱动需要了解底层物理磁盘的硬件特性,包括扇区大小、块大小等,并根据这些特性进行读写操作。

同时,块设备驱动还需要与上层文件系统进行交互,将文件系统的操作转化为对底层物理磁盘的操作。

5. 块设备驱动与字符设备驱动的区别:块设备驱动与字符设备驱动的主要区别在于访问方式和数据传输单位。

字符设备是以字节为单位进行数据传输的,不需要缓冲,而块设备是以块为单位进行数据传输的,需要使用缓冲区来暂存数据。

此外,字符设备只能顺序读写当前数据,而块设备可以进行随机访问,读写操作更加灵活。

总之,块设备驱动是存储设备驱动的一种,用于管理块设备的读写操作。

块设备驱动需要了解底层物理磁盘的硬件特性,并与上层文件系统进行交互,实现对块设备的有效管理。

块设备驱动-实验手册

块设备驱动-实验手册

实验一、sbull驱动【实验内容】在Ubantu10.10系统上编写一个sbull驱动,将一段ram空间模拟为disk使用。

并在这个disk上建立文件系统。

【实验目的】掌握块设备的编写方法。

【实验平台】Ubantu10.10【实验步骤】1、将文件夹sbull_farsight_2.6.35复制到linux环境中,如:/home/linux/workdir/test2、编译linux@ubuntu:~/workdir/test/sbull_farsight_2.6.35$ make3、通过insmod命令将模块加入内核linux@ubuntu:~/workdir/test/sbull_farsight_2.6.35$sudo insmod ./sbull.ko4、#sudo fdisk /dev/sbulla出现磁盘分区界面,选择m出现帮助信息选择n添加新的分区选择p建立一个新的主分区选择w,保存分区信息5、格式化disk#mkfs.ext2 /dev/sbulla16、挂载文件系统#mkdir mnt#sudo mount –t ext2 /dev/sbulla1 mnt7、思考:如果分区后没有及时挂载文件系统(>30s),设备为何会失效。

8、修改模式为RM_NOQUEUE测试,同样可以同样的功能(因为是ram,所以可以不需要quque)enum {RM_SIMPLE = 0, /* The extra-simple request function */RM_FULL = 1, /* The full-blown version */RM_NOQUEUE = 2, /* Use make_request */};9、修改模式为RM_FUL、RM_NOQUEUE 测试,同样可以正常运行。

嵌入式模块驱动实验报告

嵌入式模块驱动实验报告

实验一 Linux移植实践一、实验目的移植是嵌入式开发中非常重要的环节。

Linux是嵌入式开发中应用最广泛的操作系统,其移植过程具有代表性。

本实验针对arm平台,进行Linux操作系统移植的实践。

移植工作分为:bootloader移植、内核移植和文件系统移植三个部分。

通过移植实践,加深对嵌入式产品的底层操作系统开发的理解,熟悉如何从嵌入式裸机到基本软件环境建立的过程。

二、实验内容(编译部分)及要求1、bootloader移植--------vivi(1)准备源代码和交叉编译工具。

(2)配置make menuconfig(3)编译(4)实验参考见《EduKit2410 Linux book.pdf》。

2、内核移植---------linux-2.4.18(1)准备源代码和交叉编译工具(2)配置(3)编译(4)实验参考见《EduKit2410 Linux book.pdf》。

3、文件系统移植---------- root.cramfs(1)准备源代码和交叉编译工具(2)配置(3)编译(4)实验参考见《EduKit2410 Linux book.pdf》。

三、分组及验收要求1、分组及验收要求共15组,每组4名同学。

当堂验收,并每组合作完成实验报告一份。

2、环境:地点:物联网实验室(8#618)硬件环境:EduKit2410实验箱(英蓓特),cpu: s3c2410软件环境:winserver2003; cygwin;交叉编译环境:cross-armtools-linux-edukit2410.tar.bz2四、实验步骤1、cygwin安装由于粗心,忘了添加,后来又完善了gcc编译工具图1-12、安装交叉编译器1、运行cygwin,进入/tmp,解压交叉编译器的压缩包图1-22、安装交叉编译工具.sh文件不能正常运行,看了脚本之后,手动一个一个进行解压,拷贝等工作图1-33、验证交叉编译工具图1-4三、安装vivi1、解压图6图1-5正确解压后,可以看到多了一个 vivi 目录,即 vivi 源代码的安装目录,后面的 vivi配置及编译都得进入 vivi 目录进行。

块设备驱动程序分析

块设备驱动程序分析
mtd_blktrans_thread:
res = do_blktrans_request(tr, dev, req);
tr->readsect(dev, block, buf))
tr->writesect(dev, block, buf)
q->make_request_fn = sbull_make_request;

// 每个块设备都有一个“请求队列”,里面的“请求”由上层的“文件系统”提供的信息构造出来
// 底层的硬件驱动要提供“处理请求的函数”
bio->bi_sector = bh->b_blocknr * (bh->b_size >> 9);
bio->bi_bdev = bh->b_bdev;
bio->bi_io_vec[0].bv_page = bh->b_page;
bio->bi_io_vec[0].bv_len = bh->b_size;
// 初始化“通用磁盘结构”“generic disk”
dev->gd->major = sbull_major;
dev->gd->first_minor = which*SBULL_MINORS; // 第一个次设备号
dev->gd->fops = &sbull_ops; // block_device_operations
register_mtd_blktrans
mtd_blkdevs.c (drivers\mtd):
tr->blkcore_priv->rq = blk_init_queue(mtd_blktrans_request(处理请求的函数), &tr->blkcore_priv->queue_lock);

实验七(补充)sbull虚拟的磁盘驱动的编写

实验七(补充)sbull虚拟的磁盘驱动的编写

实验七(补充)sbull虚拟的磁盘驱动的编写sbull虚拟的磁盘驱动的编写分类:Embedded[ARM/DSP...]驱动2011-11-28 17:24 191人阅读评论(0) 收藏举报原理指导:我们通过vmalloc在内存中开辟一部分空间,作为一个虚拟的磁盘,然后我们以块设备的方式来访问这片内存,例如这个sbull模型。

sbull(Simple Block Utility for Loading Localities),该驱动程序实现了一个使用系统内存的块设备,从本质上讲,属于一种RAM 磁盘驱动程序。

字符设备的IO操作则是直接不绕弯的,块设备的IO 操作会配对和整合。

驱动的任务是处理请求,对于这些请求的排队和整合的工作有IO调度算法处理。

所以块设备的核心工作就是:请求处理函数或者是制造请求。

Block_devices_operations结构体中没有读写一类的成员函数,而只是包含打开、释放和IO 控制等函数。

块设备的流程:(1)先把模块模型搭建好MODULE_LICENSE("Dual BSD/GPL");static struct block_device_operations sbull_ops = {.owner = THIS_MODULE,.open = sbull_open,.release = sbull_release,.media_changed = sbull_media_changed,.revalidate_disk = sbull_revalidate,.ioctl = sbull_ioctl,.getgeo = sbull_getgeo,};module_init(sbull_init);module_exit(sbull_exit);(2)定义一个我们用内存虚拟出来的块设备sbull_devstruct sbull_dev {int size; /* Device size in sectors */u8 *data; /* The data array */short users; /* How many users */short media_change; /* Flag a media change? */spinlock_t lock; /* For mutual exclusion */struct request_queue *queue; /* The device request queue */ struct gendisk *gd; /* The gendisk structure */struct timer_list timer; /* For simulated media changes */};这个设备结构体是我们工作的核心,也许你不知道需要哪些成员,不要紧,还是那句话,编写驱动的时候,需要设备表现出那些性质和功能,相应的添加上就OK了。

(简易USB驱动)开发指导

(简易USB驱动)开发指导

实验七(2)设备驱动开发指导块设备种类多,使用广泛,其驱动程序的开发也比字符设备复杂。

通过本实验,大家要开发一个实际块设备(U盘)的驱动程序,将能够更深入地掌握块设备驱动程序的开发方法。

Linux下已经有一个通用的U盘驱动程序usb-storage.o,其源程序放在目录drivers\usb\storage下(相对于内核源码根目录)。

但这个驱动的实现相当复杂,本实验希望开发一个相对简单些的U盘驱动程序,不求高性能,只求结构明朗、清晰易懂,主要是让大家掌握一个实际块设备的驱动方式,从而加深理解。

事实上,本实验开发的驱动程序应该能够适用于所有基于Bulkonly传输协议的USB大容量存储设备(USB Mass Storage),比如USB移动硬盘和USB外置光驱,USB闪存盘(U 盘)只是其中的一种。

由于USB大容量存储设备具有容量大、速度快、连接灵活、即插即用、总线供电等优点,它们得到了广泛使用,掌握这类设备驱动程序的开发技术无疑具有很强的实用性。

实验内容编写一个U盘驱动程序myudisk,只要求能够驱动某个型号的U盘,能够支持U盘的常规操作,如命令hexdump、mke2fs和mount等。

同时,要求在系统内核日志中显示出U盘的容量。

若有余力,可增加多分区支持功能。

实验基础和思路在教材中P130,讲解了如何编写一个Ramdisk块设备驱动程序(sbull.c),称为radimo;在文献《Linux Device Drivers》讲解了如何编写一个USB设备驱动程序,并以Linux源代码中的usb-skeleton.c为例。

虽然前者驱动的并不是一个实际的块设备,且后者又只是针对usb字符设备,但是它们提供了一个不错的基础,通过合并我们就能基本得到一个支持usb块设备的驱动程序。

之所以说基本得到,是因为合并后只是有了块设备、USB设备的驱动支持框架,但还缺一样:对U盘(USB块设备)的实际访问操作。

用计算机网络技术普及办公自动化

用计算机网络技术普及办公自动化
验 , S实 验 。 GP I Ln x基础 实验 :CLn x平 台构建实验 , I J i C u i iu J GPO驱 动 L D 流水灯实验 , RT /y 1IC驱 动, AT 0 5 E UA 1t S , t 2 C 12 读
写实验 , WM 驱 动, P 单路 P WM 输 出, D 转换, D 转换 A/ A/ 驱动 , 单路 A/ 转换实验 , D 块设备驱动 , F 卡实验 , P C UD 通讯 实验 , CP通讯实验, C mu T l L x内核实验 ,C iu a I Ln x内 J 核配置/ 编译 实验, 制作 R AM S DIK。 Mi GU n I图形界面 实验 : IG 0 C iu i Mii uIfrl L x移植 l a n 实验 , nGUI Mi i 消息处理实验 , 下拉式菜单实验 , 话框应 对 用编 程实验 , 件应用编程实验 , 控 自定义控件实验 , 简易编 辑器 实验, 图显示 实验 , DI 图实验 。 位 G 绘 优点: 嵌入式单片机 教学采用 了近 年来很流行 的嵌入 式系统 ,近年 来 , , 嵌入式 系统业 界人士掀起 了广泛学 习嵌 入式系统理论及应用开发 的热潮 , 相信用这类教学方式能 紧跟上 电子工业发展 的方 向。 缺点: 首先 , 在教学方面要加入 嵌入式系统 的基础理 论; 次, 其 这些开 发设备 的投入 要 比前面两种要 多。
N OSI S C 3 I OP 2位 C U 处 理 器 , cc A M 7S C 处 理 I P A tl R o
有如下两 点: 1 用 计算机 网络构 建办公 自动 化的现代社 会环 境
基于 ̄ OSI C/ — I的综合 实验 : B E P O 编程器实 US — 2 R M 验 , L / F驱动 使用 实验, Z GC UDP通讯实 验, P S通讯实 GR

实验七Linux块设备驱动

实验七Linux块设备驱动

实验七Linux块设备驱动实验七:Linux块设备驱动块设备是与字符设备并列的概念,这两类设备在Linux中驱动的结构有较大差异,总体而言,块设备驱动比字符设备驱动要复杂得多,在I/O操作上表现出极大的不同,缓冲、I/O调度、请求队列等都是与块设备驱动相关的概念。

本章将详细讲解Linux块设备驱动的编程方法。

1.块设备的I/O操作特点字符设备与块设备I/O操作的不同如下:(1)块设备只能以块为单位接受输入和返回输出,而字符设备则以字节为单位。

大多数设备是字符设备,因为它们不需要缓冲而且不以固定块大小进行操作。

(2)块设备对于I/O请求有对应的缓冲区,因此它们可以选择以什么顺序进行响应,字符设备无须缓冲且被直接读写。

对于存储设备而言调整读写的顺序作用巨大,因为在读写连续的扇区比分离的扇区更快。

(3)字符设备只能被顺序读写,而块设备可以随机访问。

虽然块设备可随机访问,但是对于磁盘这类机械设备而言,顺序地组织块设备的访问可以提高性能。

而对SD卡、RamDisk(RamDisk 是通过使用软件将RAM模拟当做硬盘来使用的一种技术)等块设备而言,不存在机械上的原因,进行这样的调整没有必要。

2.Linux块设备驱动结构2.1.block_device_operations结构体在块设备驱动中,有一个类似于字符设备驱动中file_operations 结构体的block_device_operations结构体,它是对块设备操作的集合,定义如代码清单1所示。

代码清单1 block_device_operations结构体下面对其主要的成员函数进行分析。

与字符设备驱动类似,当设备被打开和关闭时将调用它们。

2.IO控制上述函数是ioctl()系统调用的实现,块设备包含大量的标准请求,这些标准请求由Linux 块设备层处理,因此大部分块设备驱动的ioctl()函数相当短。

被内核调用来检查是否驱动器中的介质已经改变,如果是,则返回一个非0值,否则返回0。

Linux块设备驱动初级教程

Linux块设备驱动初级教程

前言研究IO也很久了,一直无法串联bio和块设备驱动,只知道bio经过IO调度算法传递到块设备驱动,怎么过去的,IO调度算法在哪里发挥作用,一直没有完全搞明白,查看了很多资料,终于对块设备驱动有所理解,也打通了bio到块设备。

一、传统块设备我们先来实现一个基于内存的传统块设备驱动。

1.1 初始化一些东西//暂时使用COMPAQ_SMART2_MAJOR作为主设备号,防止设备号冲突#define SIMP_BLKDEV_DEVICEMAJOR COMPAQ_SMART2_MAJOR//块设备名#define SIMP_BLKDEV_DISKNAME "simp_blkdev"//用一个数组来模拟一个物理存储#define SIMP_BLKDEV_BYTES (16*1024*1024)unsigned char simp_blkdev_data[SIMP_BLKDEV_BYTES];static struct request_queue*simp_blkdev_queue;//请求队列static struct gendisk*simp_blkdev_disk;//块设备struct block_device_operations simp_blkdev_fops ={//块设备的操作函数.owner =THIS_MODULE,};1.2 加载驱动整个过程1.创建request_queue(每个块设备一个队列),绑定函数simp_blkdev_do_request2.创建一个gendisk(每个块设备就是一个gendisk)3.将request_queue和gendisk绑定4.注册gendiskstatic int__init simp_blkdev_init(void){int ret;//初始化请求队列simp_blkdev_queue =blk_init_queue(simp_blkdev_do_request, NULL);//这个方法将会在1.5仔细分析simp_blkdev_disk =alloc_disk(1);//申请simp_blkdev_disk//初始化simp_blkdev_diskstrcpy(simp_blkdev_disk->disk_name, SIMP_BLKDEV_DISKNAME);//设备名simp_blkdev_disk->major =SIMP_BLKDEV_DEVICEMAJOR;//主设备号simp_blkdev_disk->first_minor =0;//副设备号simp_blkdev_disk->fops =&simp_blkdev_fops;//块设备操作函数指针simp_blkdev_disk->queue =simp_blkdev_queue;//设置块设备的大小,大小是扇区的数量,一个扇区是512Bset_capacity(simp_blkdev_disk, SIMP_BLKDEV_BYTES>>9);add_disk(simp_blkdev_disk);//注册simp_blkdev_diskreturn0;}1.3 simp_blkdev_do_request1.调用调度算法的elv_next_request方法获得下一个处理的request2.如果是读,将simp_blkdev_data拷贝到request.buffer,3.如果是写,将request.buffer拷贝到simp_blkdev_data4.调用end_request通知完成static void simp_blkdev_do_request(struct request_queue*q){struct request*req;while((req =elv_next_request(q)) !=NULL) {//根据调度算法获得下一个requ estswitch(rq_data_dir(req)) {//判断读还是写case READ:memcpy(req->buffer, simp_blkdev_data +(req->sector <<9),req->current_nr_sectors <<9);end_request(req, 1);//完成通知break;case WRITE:memcpy(simp_blkdev_data +(req->sector <<9),req->buffer,req->current_nr_sectors <<9);end_request(req, 1);//完成通知break;default:/* No default because rq_data_dir(req) is 1 bit */break;}}1.4 卸载驱动static void__exit simp_blkdev_exit(void){del_gendisk(simp_blkdev_disk);//注销simp_blkdev_diskput_disk(simp_blkdev_disk);//释放simp_blkdev_diskblk_cleanup_queue(simp_blkdev_queue);//释放请求队列}千万别忘记下面代码module_init(simp_blkdev_init);module_exit(simp_blkdev_exit);1.5 blk_init_queue看了上面的代码,可能还是无法清晰的了解request_queue如何串联bio和块设备驱动,我们深入看一下simp_blkdev_queue =blk_init_queue(simp_blkdev_do_request, NULL);//调用blk_init _queuestruct request_queue*blk_init_queue(request_fn_proc *rfn, spinlock_t *lock){return blk_init_queue_node(rfn, lock, NUMA_NO_NODE);//跳转1.5.1}EXPORT_SYMBOL(blk_init_queue);//1.5.1struct request_queue*blk_init_queue_node(request_fn_proc *rfn, spinlock_t *lock, int node_id){struct request_queue*q;q =blk_alloc_queue_node(GFP_KERNEL, node_id, lock);if(!q)return NULL;q->request_fn =rfn;//也就是simp_blkdev_do_requestif(blk_init_allocated_queue(q) <0) {//转1.5.2blk_cleanup_queue(q);return NULL;}return q;}EXPORT_SYMBOL(blk_init_queue_node);//1.5.2int blk_init_allocated_queue(struct request_queue*q){...blk_queue_make_request(q, blk_queue_bio);//转1.5.3if(elevator_init(q))//初始化IO调度算法goto out_exit_flush_rq;return0;...}EXPORT_SYMBOL(blk_init_allocated_queue);//1.5.3void blk_queue_make_request(struct request_queue*q, make_request_fn *mfn){...q->make_request_fn =mfn;//mfn也就是blk_queue_bio...}EXPORT_SYMBOL(blk_queue_make_request);static blk_qc_t blk_queue_bio(struct request_queue*q, struct bio*bio)//完成bio如何插入到request_queue{//IO调度算法发挥作用的地方}整个调用完成之后,会绑定当前块设备的request_queue两个重要方法q->make_request_fn =blk_queue_bio;//linux默认实现q->request_fn =simp_blkdev_do_request;//驱动自己实现1.5.1 make_request_fn(struct request_queue *q, struct bio *bio)submit_bio会调用make_request_fn将bio封装成request插入到request_queue,默认会使用linux系统实现的blk_queue_bio。

块设备驱动

块设备驱动

请求队列
• 块设备将挂起的块请求保存在请求队列中,该队列由 request_queue 结构体表示,定义在文件<linux/blkdev .h>中, 包含一个双向请求队列 以及相关控制信息。通过内核中像文件系统这样高层的代码将请求加 入到队列中, 请求队列只要不为空, 队列对应的块设备驱动程序 就会从队列头获取请求, 然后将其加入到对应的块设备中去, 请求队 列表中的每一项都是一个单独的请求,由 request 结构体表示。 request_queue 是请求队列,通过它找到 request, 将这些请求连成一 体, 然后在 request中包含 bio,然后通过 bio 结构体找到对应的 page ,然后通过 page 读取物理内存中的信息。 如下图所示。(request_queue结构体 request结构体)
• request()函数对请求队列进行检查,保证请求队列中至少有一个请求 在等待处理。如果没有请求,request()函数返回,任务结束。
(3)打开操作 (4)ioctl 操作 (5)释放设备操作
• • • • • • • •

块设备驱动程序描述符是一个包含在<linux/blkdev .h>中的 blk_dev_struct 类型 的数据结构, 其定义如下所示: struct blk_dev_struct { request_queue_t request_queue; queue_proc *queue; void *date; }; request_queue 是一个结构体,包含了初始化之后的 I/O 请求队列。 queue 是一个函数指针,当其为非 0 时,就调用这个函数来找到具体 设备的请求队列,这是为考虑具有同一主设备号的多种同类设备而设的一个 域, 该指针也在初始化时就设置好。 data 是辅助 queue 函数找到特定设备的请求队列,保存一些私有的数据。

字符设备驱动实验报告(3篇)

字符设备驱动实验报告(3篇)

第1篇一、实验背景与目的随着计算机技术的飞速发展,操作系统对硬件设备的支持越来越丰富。

设备驱动程序作为操作系统与硬件之间的桥梁,扮演着至关重要的角色。

本实验旨在通过学习Linux字符设备驱动的开发,加深对设备驱动程序的理解,提高实践能力。

二、实验环境与工具1. 操作系统:Linux Ubuntu 20.042. 编程语言:C3. 开发工具:gcc、make4. 驱动框架:Linux内核三、实验内容本实验主要完成以下内容:1. 字符设备驱动程序的基本框架2. 字符设备的打开、读取、写入和关闭操作3. 字符设备驱动的注册与注销4. 字符设备驱动的用户空间交互四、实验步骤1. 创建设备文件首先,我们需要在`/dev`目录下创建一个名为`mychar`的字符设备文件。

可以使用以下命令:```bashmknod /dev/mychar c 123 0```其中,`123`是主设备号,`0`是次设备号。

2. 编写字符设备驱动程序创建一个名为`mychar.c`的文件,并编写以下代码:```cinclude <linux/module.h>include <linux/fs.h>include <linux/uaccess.h>static int major = 123; // 设备号static int device_open(struct inode inode, struct file filp);static int device_release(struct inode inode, struct file filp);static ssize_t device_read(struct file filp, char __user buf, size_t count, loff_t pos);static ssize_t device_write(struct file filp, const char __user buf, size_t count, loff_t pos);static struct file_operations fops = {.open = device_open,.release = device_release,.read = device_read,.write = device_write,};static int __init mychar_init(void) {major = register_chrdev(0, "mychar", &fops);if (major < 0) {printk(KERN_ALERT "mychar: can't get major number\n");return major;}printk(KERN_INFO "mychar: registered correctly with major number %d\n", major);return 0;}static void __exit mychar_exit(void) {unregister_chrdev(major, "mychar");printk(KERN_INFO "mychar: Goodbye from the LKM!\n");}static int device_open(struct inode inode, struct file filp) {printk(KERN_INFO "mychar: Device has been opened\n");return 0;}static int device_release(struct inode inode, struct file filp) {printk(KERN_INFO "mychar: Device has been closed\n");return 0;}static ssize_t device_read(struct file filp, char __user buf, size_t count, loff_t pos) {printk(KERN_INFO "mychar: Device has been read\n");return count;}static ssize_t device_write(struct file filp, const char __user buf, size_t count, loff_t pos) {printk(KERN_INFO "mychar: Device has been written\n"); return count;}module_init(mychar_init);module_exit(mychar_exit);MODULE_LICENSE("GPL");MODULE_AUTHOR("Your Name");MODULE_DESCRIPTION("A simple character device driver");```保存文件,并使用以下命令编译:```bashmake```3. 加载字符设备驱动程序将编译生成的`mychar.ko`文件加载到内核中:```bashinsmod mychar.ko```4. 测试字符设备驱动程序使用以下命令查看`/dev/mychar`设备文件:```bashls -l /dev/mychar```使用`cat`命令测试读取和写入操作:```bashcat /dev/mycharecho "Hello, world!" > /dev/mychar```观察系统日志,确认驱动程序的打开、读取、写入和关闭操作。

实验七(补充)RamDisk块设备驱动实例开发讲解

实验七(补充)RamDisk块设备驱动实例开发讲解

1.首先我们来看一下,块设备在整个 Linux 中应用的总体结构,如图:RamDisk 块设备驱动实例开发讲解主要讲述和总结了本人在学习嵌入式linux 中的每个步骤。

一为总结经验,二希望能给想入门嵌入式Linux 的朋友提供方便。

如有错误之处,谢请指正。

共享资源,欢迎转载: htt P : //hbhua ngga 一、开发环境主机:VMWare--Fedora 9开发板:Mini2440--64MB Na nd, Kernel:2630.4 编译器:arm-li nux-gcc-4.3.2二、块设备基本概念扇区(Sectors):任何块设备硬件对数据处理的基本单位。

通常, 512byte 。

块(Blocks):由Linux 制定对内核或文件系统等数据处理的基本单位。

通常, 个或多个扇区组成。

段(Segments):由若干个相邻的块组成。

是 Linux 内存管理机制中一个内存页或者内存页的一部分。

页、段、块、扇区之间的关系图如下:综合上描述:块设备驱动是基于扇区(sector)来访问底层物理磁盘,基于块(block)来访问 上层文件系统。

扇区一般是 2的n 次方大小,典型为 512B ,内核也要求块是 2的n 次方大 小,且块大小通常为扇区大小的整数倍,并且块大小要小于页面大小,典型大小为512B 、1K 或 4K 。

三、块设备在Linux 中的结构1个扇区的大小为1个块由1虚拟文件系统血刃磁盘缓存CC«K«)____ L___ ________ _________II!磁盘文件系统a)“kFS) 块设备文件系ii31ock FS) I通用块层((jeneric Block Layer )T TI/D 调度层(1/0 Scheduler Layer)块设备驱动(£Iock Devi ce Driver)J I 内核空间慷件硬件CKarJ B I skJ2•块设备驱动层(Block Device Driver)在总体结构中扮演的角色。

  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

学写块设备驱动(一)----了解gendisk及request处理函数以下是一个最简单的块设备驱动,写完可以对编写块设备驱动的框架有初步了解。

环境:Linux2.6.29simp_blkdev模块,used by列的值重新为0收获:1.要让一个最简单的块设备驱动可用,必须实现的关键结构为gendisk和request_queue。

gendisk结构描述一个磁盘,包括主从设备号、设备操作函数、容量等信息,它通过gendisk->queue和request_queue联系起来,request_queue初始化时又向内核块设备层注册了处理request的函数(该例子中为simp_blkdev_do_request)。

2.该版本适用于 2.6.29内核,从 2.6.31内核开始,一些api发生变化(见blk_end_request_all(request,error),insmod直接死机;假如使用__blk_end_request_all(request,error),可以insmod,但是不能够mkfs和mount,查看dmesg 为“EXT3-no journal”错误。

查看源码blk-core.c,只知道blk_end_request_all比__blk_end_request_all多了加锁和解锁的操作,但是由于队列锁方面的知识不足,现在无法解决该错误,故暂时使用2.6.29内核实践学习。

如果读者你恰巧遇到同样的问题并恰巧成功了,请您一定告诉我答案。

学写块设备驱动(二)----更换IO调度器上节我们的块设备驱动已经可以使用了,本节我们对其进行一点小的改动,修改其使用的IO调度器。

我们知道,标准磁盘的寻道延时很高,故有了IO调度器存在的必要,它通过对IO请求进行合并或者排序来提高块设备的使用效率。

但是因为我们目前的块设备在内存中,即没有通常的磁盘寻道延时,且读写迅速,所以我们不需要IO调度器为我们做多余的事情浪费资源。

目前Linux有四种IO调度器,anticipatory、cfq、deadline和noop。

例子这里最适合我们的是noop,它基本对IO请求什么都不做。

我们想要更换IO调度器,关键的函数是这么两个:其次,由于我们使用blk_init_queue(request_fn_proc *)初始化IO请求队列时,通用块层为我们初始化了默认的IO调度器,所以我们需要下面的函数来释放它:效果没有更换IO调度器之前(cfq):更换IO调度器(noop):学写块设备驱动(三)----踢开IO调度器,自己处理bio(上)前两篇我们编写了在内存中的最简单的块设备驱动程序,并为其更换了我们心仪的‟noop…IO调度器。

本篇我们试着搞清楚内核的块设备层在这里为我们做的事情,以及我们如何做点自己想做的事情。

其实,我们前面两篇都是围绕着请求队列(request_queue)这东西做事情。

初始化请求队列时我们注册上驱动处理请求(request)的策略函数(simp_blkdev_do_request),然后在gendisk结构初始化时又填充上前面初始化好的queue。

后面我们又用…noop‟IO调度器更换掉默认的'cfq'调度器。

下面试着搞清楚通用块层在这里的框架机制。

先看一张图:当通用块层以上的层要对块设备进行访问时,通常是准备好一个bio,调用generic_make_request(struct bio *bio),OK。

但是我们是编写底层驱动的可怜IT男,要是不知道generic_make_request是怎么把我们前面实现的simp_blkdev_do_request和request_queue联系起来,那就相当没有安全感。

于是,我们开始RTFSC。

既然是围着request_queue做文章,那么我们先看下这个数据结构:对上面的数据结构留个印象,我们开始看通用块层的入口函数generic_make_request(struct bio *bio)。

你可以发现下面的调用关系generic_make_request(struct bio *bio) ------>__generic_make_request ------>q->make_request_fn(q, bio)于是我们得出结论,generic_make_request()最终是通过调用request_queue.make_request_fn函数完成bio所描述的请求处理的。

那么,make_request_fn具体又指向哪个函数呢?我们前面也没有实现过make_request_fn 这样的函数啊?!我们只记得初始化request_queue时调用了blk_init_queue(request_fn_proc *, spinlock_t *)这个函数,所以我们来看一下这个函数,blk_init_queue(request_fn_proc *rfn, spinlock_t *lock)------>blk_init_queue_node()------>q->unplug_fn=generic_unplug_device;q->request_fn=rfn;blk_queue_make_request(q,__make_reqeust)------>q->make_request_fn=mfn(即__make_request)原来,我们request_queue的make_request_fn实际上指向了__make_request()函数。

这样,大名鼎鼎的__make_request()函数就可以叫来某个IO调度器帮忙,并对bio做些利于用户的加工,比如将其映射到非线性映射区域。

至此,我们知道了一个新的request是如何被提交给IO调度器的了(通过__make_request)。

我们也知道了我们编写的simp_blkdev_do_request 就是在这里被赋值给q->request_fn的了(通过blk_queue_make_request)。

那么,通用块层的框架是什么时候对我们的simp_blkdev_do_request函数进行调用,从而真正执行对数据的拷贝了呢?这里有点复杂,先补充一点理论知识,就一点,块设备有“阻塞”和“非阻塞”的状态,从而通用块层才能利用这样的机制推迟对请求的处理,从而给了IO调度参与的机会;当“非阻塞”时(该函数为blk_remove_plug),才能够处理请求。

有了这样的知识,我们RTFSC时见到blk_remove_plug就不奇怪了。

好了,我们下面给出q->request_fn指向的simp_blkdev_do_request在何时被调用。

这次从__make_request()开始,这函数果然强大。

__make_request()------>add_request()------>__elv_add_request()------>elv_insert()------>blk _remove_plug------>blk_unplug_timeout------>kblocked_schedule_work------>blk_unplug_work( )------>q->unplug_fn()如果你循到了上面的调用关系,那么恭喜你,你遇到了新问题,q->unplug_fn是哪只鸟,它调用的谁??如果你记性还不错,那么你会想起我们blk_init_queue调用关系里面有一个“q->unplug_fn=generic_unplug_device;”,很好,我们发现q->unplug_fn其实调用的是通用块层另一个小有名气的函数,generic_unplug_device()。

我们继续寻找调用关系。

generic_unplug_device()------>__generic_unplug_device()------>q->request_fn()到这里,长征结束,期间和IO调度器打个照面,见识了块设备解除阻塞状态,还唤醒了内核的工作队列,但最终我们可以大呼一口气了。

综上所述,当我们实现了块设备驱动程序的策略函数(例如前面实现的simp_blkdev_do_request)并用其作为参数初始化一个request_queue后,通用块层的make_request_fn函数指针帮我们指定了强力帮手__make_request,该帮手又拉上了IO调度器,于是,一个bio经过通用块层、IO调度层的处理,最后以request_queue中的request喂给我们实现的策略函数。

学写块设备驱动(三)----踢开IO调度器,自己处理bio(下)本篇的(上)基本搞清楚了我们已经实现的内存块设备驱动和通用块层之间的丝丝联系。

现在我们该做点自己想做的事情了:踢开IO调度器,自己来处理bio。

踢开IO调度器很容易,即不使用__make_request 这个系统指定的强力函数,如何不使用?其实我们从(上)的blk_init_queue()函数中也能看出来,系统使用了blk_queue_make_request(q, __make_request)这个函数,那么我们也可以使用这个函数来指定我们自己的策略函数,从而替换掉__make_request函数。

那初始化request_queue的blk_init_queue函数也不需要了。

直接看更改过后的源码:明白了吧,我们的块设备驱动由于也是虚拟的块设备,故并不受益于IO调度,而受益于直接处理bio。

该函数的第二个参数就是我们需要编写的处理bio的函数。

int (your_make_request) (struct request_queue *q, struct bio *bio) // 这是我们需要编写的主要函数,功能即对bio进行处理。

bio的结构自己去google吧,在这里我们只点出,bio 对应块设备上一段连续空间的请求,bio中包含的多个bio_vec用来指出这个请求对应的每段内存。

所以,该函数的本质即在一个循环中,处理bio中的每个bio_vec。

bio_for_each_segment(bvl, bio, i) // 宏,用来方便我们对bio结构进行遍历。

bio->bi_sector //bio请求的块设备起始扇区bio->bi_size //bio请求的扇区数void bio_endio(struct bio *bio, int error) // 结束bio请求。

相关文档
最新文档