您的位置:首页 > 其它

[笔记分享] [SD] 块设备驱动学习小结

2017-08-15 22:44 567 查看
Chipset: msm8926

OS: Android4.4

Kernel: 3.4.0

相关概念:

块设备与字符设备区别:

以块传输,字符设备以字节为单位。

有对应缓冲区。

可以随机访问。可以不按传输顺序访问,比如访问的是1/10/3/2扇区,可以调整为1/2/3/10扇区。此设计是为了机械类块设备如硬盘设计的,对于SD/EMMC可以直接访问。

Struct block_device_operations:

[blkdev.h]



此struct和字符设备的file_operations类似。

需要关注的有open/release/ioctl/getgeo.

Ioctl: 块设备的请求通过此接口。

Getgeo: 根据块设备的信息填充hd_geometry, 里面包含磁头,扇区,柱面,俗称CHS。

Struct gendisk:

[genhd.h]



Major: 主设备号。

First_minor: 第一个次设备号。

Minors: 最大支持次设备号数。

Part_tbl: 分区信息。

Part0: 磁盘信息

Fops: 块设备操作函数集。

Queue: 请求队列,用户请求通过队列来处理。

Gendisk相关函数:

1. struct gendisk *alloc_disk(int minors)

分配struct gendisk,驱动不能自己分配,需要调用此接口才行。

Minors表示改磁盘最大能支持多少分区。

2. void add_disk(struct gendisk *disk)

注册disk,在驱动初始化完成之后调用。

3. void del_gendisk(struct gendisk *disk)

4. static inline void set_capacity(struct gendisk *disk, sector_t size)

设置disk容量,以扇区为单位。块设备最小寻址单位是扇区,最常见的是512bytes,还

有其他大小,对于内核,与块设备驱动交互的扇区都以512bytes为单位。

许多块设备一次能传输多个扇区。

Struct request:

[blkdev.h]





用request来表示一个等待I/O的请求。

Queuelist: 构成链表。

Q: 指向对应的queue。

__data_len: 要传送的扇区数。

_sector: 要传送的下个sector.

Bio: 请求的block io,里面包含具体的block信息。

Rq_disk: 对应disk设备.

Part: 对应磁盘。

Struct request_queue:

[blkdev.h]







此结构主要描述设备能够支持的请求类型,扇区大小,对齐要求等参数。

还包含了一个I/O调度器,选使用哪种调度算法来控制当前的I/O请求排列,相邻扇区合并等过程。不过对于SD/EMMC这类设备,就不用I/O调度器,直接现来现写。

相关接口:

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

[blk-core.c]初始化请求队列, rfn为对应请求处理函数指针。

struct request_queue *blk_alloc_queue(gfp_t gfp_mask)

[blk-core.c] sd/ram等这些随机访问的非机械设备,不需要复杂I/O调度,应使用此接口

,再调用blk_queue_make_request()将制造函数与队列绑定,这样就直接调用制造函数,而且不会调用请求函数了。

可参考如下比较:



Struct bio:



一个bio对应一个I/O请求。此结构包含具体的I/O请求信息,如扇区索引,传送大小,读写标志等。

Bi_sector: 要传送的第一个sector。

Bi_rw: 读还是写。

Bi_io_vec: 包含对应传输的页,字节数以及偏移地址。

Struct bio_vec:



Gedisk/request/queue/bio/bio_vec关系图:



注册/注销:

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

此接口可选,以后可能会被移除,它只完成了两件事:

1. 分配一个主设备号。

2. 在/proc/devices下创建入口。

不使用请求处理函数:

手机都是对sd/emmc这类随机访问设备操作,因此使用请求处理函数的方法就不阐述了。

在此模式下需要提供一个“制造函数”,类似于请求处理函数。

[blkdev.h]



虽然q参数还在,实际不包含任何请求。

核心是bio,处理完成之后使用bio_endio()通知处理结束。

[bio.c]



Ramdisk举例:

Ramdisk是一种模拟磁盘,数据存在ram中,以块设备的方式访问,对应设备文件一般为/dev/ram%d。

我们来看看上面提到的这些关键数据是如何被使用到的。

[brd.c]

brd_init():



559: 调用register_blkdev()注册块设备,主设备号RAMDISK_MAJOR为1.

563: 分配brd,后面会看下它里面做了什么。

572: 在初始化完成之后将disk添加进去。

执行完register_blkdev()之后在/proc/devices下能看到:



执行完add_disk()之后能在/dev/block下看到ramx节点,总共有16个。





Note: /dev/下的节点和是udev监测sysfs里的设备而创建的。

/dev/block对应sysfs的创建是:

[genhd.c]



而ramx节点的创建对应的sysfs 执行code是:

[brd.c]



[genhd.c]



brd_alloc():



473: 分配request queue,且表示不是用I/O调度器。所以这里没使用请求处理函数的方法。

476: 对应的“制造函数”是brd_make_request().

483: 分配disk.

488: 表明对应的fops是brd_fops.

493: 设置disk的capacity.

brd_make_request():



344: 本次传输是读还是写。

349: 读或者写数据。

356: 结束读写。



314/318: 读写分别只是将数据copy到brd或者从brd的sector copy出来。



brd_fops:





399~403: 如果有被打开了,就将device相关的buffer/cache清理掉。

Kris.Fei
2014/09/09
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: