一. 框架:
app: open,read,write "1.txt"
--------------------------------------------- 文件的读写
文件系统: vfat, ext2, ext3, yaffs2, jffs2 (把文件的读写转换为扇区的读写)
-----------------ll_rw_block()----------------- (这个函数是块设备操作的入口,提供扇区的读写功能,详细请参阅<LINUX内核源代码情景分析>设备驱动一章)
1. 把"读写"放入队列
2. 调用队列的处理函数(优化/调顺序/合并)
块设备驱动程序
---------------------------------------------
硬件: 硬盘,flash
二. ll_rw_block()分析
<LINUX内核源代码情景分析>
分析ll_rw_block
for (i = 0; i < nr; i++) {
struct buffer_head *bh = bhs[i];
submit_bh(rw, bh);
struct bio *bio; // 使用bh来构造bio (block input/output)
submit_bio(rw, bio);
// 通用的构造请求: 使用bio来构造请求(request)
generic_make_request(bio);
__generic_make_request(bio);
request_queue_t *q = bdev_get_queue(bio->bi_bdev); // 找到队列
// 调用队列的"构造请求函数"
ret =q->make_request_fn(q, bio);
// 默认的函数是__make_request
__make_request
// 先尝试合并
elv_merge(q, &req, bio);
// 如果合并不成,使用bio构造请求
init_request_from_bio(req, bio);
// 把请求放入队列
add_request(q, req);
// 执行队列
__generic_unplug_device(q);
// 调用队列的"处理函数"
q->request_fn(q);
先构造一个make_request(bio),在构造一个请求队列,将请求加入队列
三. 块设备驱动程序编写
参考:drivers\block\xd.c drivers\block\z2ram.c
1. Xd.c分析
register_blkdev(XT_DISK_MAJOR,"xd")
struct request_queue*xd_queue = blk_init_queue(do_xd_request, &xd_lock);// //为设备准备请求队列
xd_drives = xd_initdrives(xd_sigs[controller].init_drive);
for (i = 0; i < xd_drives; i++) {
XD_INFO*p = &xd_info[i];
structgendisk *disk = alloc_disk(64);
disk->major= XT_DISK_MAJOR;
disk->first_minor= i<<6;
disk->fops = &xd_fops;
disk->private_data= p;
disk->queue= xd_queue;
}
z2ram.c分析
register_blkdev(Z2RAM_MAJOR,DEVICE_NAME)
struct gendisk *z2ram_gendisk = alloc_disk(1);
struct request_queue *z2_queue = blk_init_queue(do_z2_request, &z2ram_lock);//为设备准备请求队列
static DEFINE_SPINLOCK(z2ram_lock);
static void do_z2_request(struct request_queue *q)//请求处理函数
{
structrequest *req;
req= blk_fetch_request(q);//从队列中取出请求
memcpy(req->buffer,(char *)addr, size);
}
z2ram_gendisk->major = Z2RAM_MAJOR;
z2ram_gendisk->first_minor = 0;
z2ram_gendisk->fops = &z2_fops;
static const struct block_device_operations z2_fops =
{
.owner =THIS_MODULE,
.open =z2_open,
.release =z2_release,
};
z2ram_gendisk->queue = z2_queue;
add_disk(z2ram_gendisk);
2. 自己编写
2.1 具体步骤:
内容:从内存中划出一部分内存,当做磁盘来操作。
1. 注册设备register_blkdev()
2. 为设备初始化一个请求队列struct request_queue : blk_init_queue()(blk_cleanup_queue())
3. 分配gendisk: alloc_disk
4. 设置gendisk
设备号,名字,操作函数,容量,所属队列等等属性
5. 注册: add_disk
2.2 代码:
#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<asm/system.h>
#include<asm/uaccess.h>
#include<asm/dma.h>
static structgendisk *ramblock_disk;
staticrequest_queue_t *ramblock_queue;
static intmajor;
staticDEFINE_SPINLOCK(ramblock_lock);
#defineRAMBLOCK_SIZE (1024*1024)
static unsignedchar *ramblock_buf;
static intramblock_getgeo(struct block_device *bdev, struct hd_geometry *geo)
{
/* 容量=heads*cylinders*sectors*512 */
geo->heads = 2;
geo->cylinders = 32;
geo->sectors = RAMBLOCK_SIZE/2/32/512;
return 0;
}
static structblock_device_operations ramblock_fops = {
.owner =THIS_MODULE,
.getgeo =ramblock_getgeo,
};
static voiddo_ramblock_request(request_queue_t * q)
{
static int r_cnt = 0;
static int w_cnt = 0;
struct request *req;
//printk("do_ramblock_request%d\n", ++cnt);
while ((req = elv_next_request(q)) !=NULL) {
/* 数据传输三要素: 源,目的,长度 */
/* 源/目的: */
unsigned long offset =req->sector * 512;
/* 长度: */
unsigned long len =req->current_nr_sectors * 512;
if (rq_data_dir(req) == READ)
{
//printk("do_ramblock_requestread %d\n", ++r_cnt);
memcpy(req->buffer,ramblock_buf+offset, len);
}
else
{
//printk("do_ramblock_requestwrite %d\n", ++w_cnt);
memcpy(ramblock_buf+offset,req->buffer, len);
}
end_request(req, 1);//1--succes0--error
}
}
static intramblock_init(void)
{
/* 1. 分配一个gendisk结构体 */
ramblock_disk = alloc_disk(16); /* 次设备号个数: 分区个数+1 */
/* 2. 设置 */
/* 2.1 分配/设置队列: 提供读写能力 */
ramblock_queue =blk_init_queue(do_ramblock_request, &ramblock_lock);
ramblock_disk->queue = ramblock_queue;
major = register_blkdev(0,"ramblock"); /* cat/proc/devices */
ramblock_disk->major = major;
ramblock_disk->first_minor = 0;
sprintf(ramblock_disk->disk_name,"ramblock");
ramblock_disk->fops = &ramblock_fops;
set_capacity(ramblock_disk, RAMBLOCK_SIZE/ 512);//设置容量
/* 3. 硬件相关操作 */
ramblock_buf = kzalloc(RAMBLOCK_SIZE,GFP_KERNEL);
/* 4. 注册 */
add_disk(ramblock_disk);
return 0;
}
static voidramblock_exit(void)
{
unregister_blkdev(major,"ramblock");
del_gendisk(ramblock_disk);
put_disk(ramblock_disk);
blk_cleanup_queue(ramblock_queue);
kfree(ramblock_buf);
}
module_init(ramblock_init);
module_exit(ramblock_exit);
MODULE_LICENSE("GPL");
测试3th,4th:
在开发板上:
1. insmodramblock.ko
2. 格式化: mkdosfs /dev/ramblock
3. 挂接: mount /dev/ramblock /tmp/
4. 读写文件: cd /tmp, 在里面vi文件
5. cd /; umount/tmp/
6. cat/dev/ramblock > /mnt/ramblock.bin
7. 在PC上查看ramblock.bin
sudo mount -o loop ramblock.bin /mnt
测试5th:
1. insmodramblock.ko
2. ls/dev/ramblock*
3. fdisk/dev/ramblock