[笔记分享] [SD] 块设备驱动学习小结
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
上一篇: Kotlin 与 Java基本语法对比
下一篇: c#获取windows桌面背景代码示例