块设备驱动程序设计

一、块设备简介

1、块设备

块设备将数据存储在固定大小的块中,每个块的大小通常在512字节到32768字节之间。磁盘、SD卡都是常见的块设备。

2、块设备VS字符设备

# 块设备和字符设备最大的区别在于读写数据的基本单元不同。块设备读写数据的基本单元为块,例如磁盘通常为一个sector,而字符设备的基本单元为字节。

# 块设备能够随机访问,而字符设备则只能顺序访问。

块设备体系架构:

VFS是对各种具体文件系统的一种封装,为用户程序访问文件提供统一的接口。

Disk Cache

当用户发起文件访问请求的时候,首先会到Disk Cache中寻找文件是否被缓存了,如果在cache中,则直接从cache中读取。如果数据不再缓存中,就必须要到具体的文件系统读取数据了。

Mapping Layer

1、首先确定文件系统的block size,然后计算所请求的数据包含多少个block。

2、调用具体文件系统的函数来访问文件的inode,确定所请求的数据在磁盘上的逻辑块地址。

Generic Block Layer

Linux内核为块设备抽象了统一的模型,把块设备看作是由若干个扇区组成的数据空间。上层的读写请求在通用块层(Generic Block Layer)被构造成一个或多个bio结构。

I/O Scheduler Layer

I/O调度层负责将I/O操作进行排序,采用某种算法(如:电梯调度算法)来高效地处理操作。

电梯调度算法的基本原则:如果电梯现在朝上运动,如果当前楼层的上方和下方都有请求,则先响应所有上方的请求,然后才向下响应下方的请求;如果电梯向下运动,则刚好相反。

Block Device Driver

块设备驱动程序通过发送命令给磁盘控制器实现真正的数据传输。

二、块设备驱动程序设计

设备描述

Linux内核使用struct gendisk(定义于<linux/genhd.h>)来描述块设备。

struct gendisk

{

int major; //主设备号

int first_minor; //次设备号

int minors;

char disk_name[DISK_NAME_LEN]; //驱动名

struct block_device_operations *fops;

struct request_queue *queue; //请求队列

...... ...... ...... ......

int node_id;

}

设备注册

Linux内核使用add_disk函数向内核注册块设备驱动

void add_disk(struct gendisk *gd)

设备操作

字符设备通过file_operations结构来定义使它所支持的操作,块设备使用一个类似的结构:struct block_device_operations.

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);

...... ....... ...... ....... ......................................

};

IO请求

在Linux内核中,使用struct request来表示等待处理的块设备I/O请求。

struct request

{

struct list_head queuelist; //链表结构

sector_t sector; //要操作的首个扇区

unsigned long nr_sectors; //要操作的扇区数目

struct bio *bio; //请求的bio结构体的链表

struct bio *biotail; //请求的bio结构体的链表尾

........................................................................................................................

}

请求队列

简单的讲,请求队列就是IO请求request所形成的队列,在Linux内核中struct request_queue描述。

内核提供了一系列函数用来操作请求队列:

struct request_queue *blk_init_queue(request_fn_proc *rfn,  spinlock_t *lock) //初始化请求队列,一般在块设备驱动的模块加载函数中调用。

void blk_cleanup_queue(request_queue_t *q) //清除请求队列,这个函数完成将请求队列返回给系统的任务,一般在块设备驱动模块卸载函数中调用。

struct request *elv_next_request(request_queue_t *queue) //返回下一个要处理的请求(由I/O调度器决定),如果没有请求则返回NULL。elv_next_request()不会清除请求,它仍然将这个请求保留在队列上,因此连续调用它2次,2次会返回同一个请求结构体。

void blkdev_dequeue_request(struct request *req) //从队列中删除1个请求。

实例代码simple-blk.c是ramdisk的驱动,也就是基于内存的磁盘驱动

块设备驱动测试

1、insmod simple-blk.ko

2、ls /dev/simp_blkdev

3、mkfs.ext3 /dev/simp_blkdev

4、mkdir -p /mnt/blk

5、mount  /dev/simp_blkdev  /mnt/blk

6、cp /etc/init.d/* /mnt/blk

7、ls /mnt/blk

8、umount  /mnt/blk

9、ls /mnt/blk

数据访问流程

struct request_queue

{

.........

make_request_fn *make_request_fn;

.........

}

数据访问流程:

BIO

1个struct bio代表1次块设备I/O请求,IO调度器可将连续的bio合并成1个请求struct request.

struct bio

{

sector_t bi_sector; //要访问的第1个扇区

unsigned int bi_size; //以字节为单位所需传输的数据大小

struct bio_vec *bi_io_vec; //实际的vec列表

..........................................................................................................................................

}

struct bio_vec

{

struct page *bv_page; //页指针

unsigned int bv_len; //传输的数据长度

unsigned int bv_offset; //偏移量

}

_make_request

在_make_request函数中,使用了IO调度器(elevator)将多个bio的访问顺序进行优化,调整,合并为一个request,然后提交给用户指定的函数处理。但是对于ramdisk、U盘、记忆棒之类的设备,并不存在磁盘所面临的寻道时间(没有机械的磁头)。因此对这样的“块设备”而言,一个I/O调度器不但发挥不了作用,反而其本身将白白耗掉不少内存和CPU。

解决办法:驱动程序自己实现request_queue所需要的make_request_fn函数,不使用_make_request

请求队列

request_queue_t *blk_alloc_queue(int gfp_mask) //分配“请求队列”,对于FLASH、RAM盘等完全随机访问的非机械设备,并不需要进行复杂的I/O调度,这个时候,应该使用上述函数分配1个“请求队列”。

void blk_queue_make_request(request_queue_t *q, make_request_fn * mfn)//绑定“请求队列”和“制造请求”函数。

三、SD卡驱动测试

见课件。

可参考:http://m.blog.csdn.net/blog/tangkai177/8532152

块设备驱动程序设计,布布扣,bubuko.com

时间: 2024-11-10 02:34:43

块设备驱动程序设计的相关文章

linux块设备驱动---程序设计(转)

块设备驱动注册与注销 块设备驱动中的第1个工作通常是注册它们自己到内核,完成这个任务的函数是 register_blkdev(),其原型为:int register_blkdev(unsigned int major, const char *name); major 参数是块设备要使用的主设备号,name为设备名,它会在/proc/devices中被显示. 如果major为0,内核会自动分配一个新的主设备号register_blkdev()函数的返回值就是这个主设备号.如果返回1个负值,表明发

LINUX块设备驱动&lt;12/13/14/15&gt;

第 12章 +---------------------------------------------------+ |                 写一个块设备驱动                   | +---------------------------------------------------+ | 作者:赵磊                                         | | email: [email protected]             

写一个块设备驱动15完

http://blogold.chinaunix.net/u3/108239/showart.php?id=2144637 第15章(最终章) +---------------------------------------------------+|                 写一个块设备驱动                  |+---------------------------------------------------+| 作者:赵磊                    

【深入Linux块设备驱动层次之一】整体层次

最近在做文件系统和linux块设备相关的工作,需要对文件系统和底层块设备之间的关系和交互有比较深入的了解.笔者参考的书籍有敖青云所著的<存储技术原理分析-基于Linux2.6内核原代码>,同时参考的还有Jonathan Corbet.Alessandro Rubini和Greg Kroah-Hartman 一起著作的linux设备经典书籍<Linux设备驱动Edition 3 >.陈学松写的<深入Linux设备驱动内核机制>.对比了一下,还是敖青云写的很存储结合更紧密一

块设备驱动框架分析(二)

参考:块设备驱动之一  块设备驱动之二  块设备驱动之三 总结上一篇的块设备驱动的步骤: 1. 分配gendisk: alloc_disk static struct gendisk * ramblock_disk = alloc_disk(16); /* 次设备号个数: 分区个数+1 */2. 设置2.1 分配/设置队列: // 它提供读写能力static struct request_queue  * ramblock_queue = blk_init_queue(do_ramblock_r

SylixOS块设备驱动模型

1. 块设备简介 块设备是I/O设备中的一类,是将信息存储在固定大小的块中,每个块有自己的地址,数据块大小通常在512字节到32768字节之间.块设备的基本特征是每个块都能独立于其他块而读写.磁盘是最常见的块设备. 2. 技术实现 SylixOS实现了兼容POSIX标准的输入输出系统,SylixOS的I/O概念继承了UNIX操作系统的概念,认为一切皆为文件.本章介绍SylixOS在I/O层之下提供的块设备模型,用户驱动可以使用此标准化的设备模型来编写,这样可以对上层提供统一的.标准的设备API,

linux块设备驱动之实例

前两篇blog已经基本熟悉了块设备的相关结构,这里来总结下ldd3中提到的一些块设备驱动例题: 1.注册:向内核注册个块设备驱动,其实就是用主设备号告诉内核这个代表块设备驱动 sbull_major = register_blkdev(sbull_major, "sbull"); if (0 >= sbull_major){ printk(KERN_WARNING "sbull: unable to get major number!\n"); return

LINUX块设备驱动&lt;2&gt;

转自:http://blog.chinaunix.net/uid-15724196-id-128140.html 第2章 +---------------------------------------------------+|                 写一个块设备驱动                  |+---------------------------------------------------+| 作者:赵磊                               

LINUX块设备驱动&lt;5&gt;

转自:http://blog.chinaunix.net/uid-15724196-id-128143.html 第5章 +---------------------------------------------------+|                 写一个块设备驱动                  |+---------------------------------------------------+| 作者:赵磊