字符设备与块设备I/O操作的不同如下:
(1)块设备只能以块为单位接受输入和返回输出,而字符设备则以字节为单位。大多数设备是字符设备,因为他们不需要缓冲而且不以固定块大小进行操作。
(2)块设备对于I/O请求有对应的缓冲区,因此它们可以选择以什么顺序进行响应,字符设备无需缓冲且被直接读写。对于存存储设备而言调整读写的顺序作用巨大,因为在读写连续的扇区比分离扇区更快。
(3)字符设备只能被顺序读写,而块设备可以随机访问。虽然块设备可以随机访问,但对于磁盘这类机械设备而言,顺序地组织块设备的访问可以提高性能。
之所以引入块设备就是因为在操作机械设备如磁盘的时候,如果同一时间要去读和写不同的扇区(先读一个扇区,再写另一个扇区,再读另外的扇区),读写这个过程比较快,但是定位就要占很部分的时间,所以做一下调整,先读完了再去写,这样就可以减速少定位花去的时间,总体上让读写速度得到提高。
框架:
app: open,read,write "1.txt"
--------------------------------------------- 文件的读写
文件系统: vfat, ext2, ext3, yaffs2, jffs2 (把文件的读写转换为扇区的读写)
-----------------ll_rw_block----------------- 扇区的读写
1. 把"读写"放入队列
2. 调用队列的处理函数(优化/调顺序/合并)
块设备驱动程序
---------------------------------------------
硬件: 硬盘,flash
分析ll_rw_block
for (i = 0; i < nr; i++) { /* nr 请求个数 */
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);
怎么写块设备驱动程序呢?
1. 分配gendisk: alloc_disk
2. 设置
2.1 分配/设置队列: request_queue_t // 它提供读写能力
blk_init_queue
2.2 设置gendisk其他信息 // 它提供属性: 比如容量
3. 注册: add_disk
- /* 参考xd.c z2ram.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 <asm/system.h>
- #include <asm/uaccess.h>
- #include <asm/dma.h>
- static struct gendisk *ramblock_disk;
- static request_queue *ramblock_queue;
- static int major;
- #define RAMBLOCK_SIZE (1024*1024) /* 分配大小1MB */
- static unsigned char *ramblock_buf; /* 缓冲区 */
- static DEFINE_SPINLOCK(ramblock_lock); /* 定义一个自旋锁 */
- static int ramblock_getgeo(ramblock_disk, struct hd_geometry *geo)
- {/* 容量 = heads*cylinders*sectors*512 最后512是一个扇区有512个字节 */
- // geo->heads = unit[drive].type->heads; /* 磁头数 */
- geo->heads = 2;
- // geo->cylinders = unit[drive].type->tracks; /* cyllinders 柱面数 */
- geo->cylinders = 32;
- // geo->sectors = unit[drive].dtype->sects * unit[drive].type->sect_mult; /* 扇区数 */
- geo->sectors = RAMBLOCK_SIZE/2/32/512 /* 单位为字节 */
- return 0;
- }
- static struct block_device_operations ramblock_fops = {
- .owner = THIS_MODULE,
- //.ioctl = ramblock_ioctl,
- .getgeo = ramblock_getgeo, /* geo geometry几何 getgeo获得几何属性 */
- };
- static void do_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);
- /* 以电梯调度算法(elv)取出下一个请求 */
- while ((req = elv_next_request(q)) != NULL) {
- /* 数据传输三要素:源、目的、长度 */
- /* 源/目的 */
- unsigned long offset = req->sector << 9; /* 左移9位相当于乘以512(一个扇区的大小) */
- /* 目的/源 */
- // req->buffer
- /* 长度 */
- unsigned long len = req->current_nr_sectors << 9;
- if (rq_data_dir(req) == READ)
- {
-
- // printk("do_ramblock_request read%d\n", ++r_cnt);
- memcpy(req->buffer, ramblock_buf + offset, len); /* 这里是直接从内存拷,如果 是实际的设备则要涉及相关的硬件操作 */
- }
- else
- {
- //printk("do_ramblock_request write%d\n", ++w_cnt);
- memcpy(ramblock_buf + offset, req->buffer, len);
- }
- end_request(req,1); /* 1表示成功,0表失败 */
- }
- }
- static int ramblock_init(void)
- {
- /* 1.分配一个gendisk结构体 */
- ramblock_disk = alloc_disk(16); /* minors是指次设备号个数即分区个数 + 1
- 当minors为0时,表示整个磁盘,minors为1时表示只有一个磁盘分区且不可再分区 */
- /* 2.设置gendisk结构体 */
- /* 2.1 分配/设置一个队列 提供读写能力 */
- ramblock_queue = blk_init_queue(do_ramblock_request, ramblock_lock);
- ramblock_disk ->queue = ramblock_queue;
-
- /* 2.2 设置其他属性:比如容量等 */
- 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;
- // ramblock_disk->private_data = p;
- // ramblock_disk->quese = ramdisk_queue;
- set_capacity(ramblock_disk, RAMBLOCK_SIZE/512); /* 容量是以扇区为单位 */
- /* 3.硬件相关操作 分配内存 */
- ramblock_buf = kzalloc(RAMBLOCK_SIZE, GFP_KERNEL);
- /* 4.注册 */
- add_disk(ramblock_disk)
- return 0;
- }
- static void ramblock_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_DESCRIPTION("block driver test for the s3c2440");
- MODULE_LICENSE("GPL");
阅读(717) | 评论(0) | 转发(0) |