分类: 嵌入式
2013-03-26 13:34:08
字符设备与块设备I/O 操作的不同如下。
(1)块设备只能以块为单位接受输入和返回输出,而字符设备则以字节为单位。
大多数设备是字符设备,因为它们不需要缓冲而且不以固定块大小进行操作。
(2)块设备对于I/O 请求有对应的缓冲区,因此它们可以选择以什么顺序进行响
应,字符设备无须缓冲且被直接读写。对于存储设备而言调整读写的顺序作用巨大,
因为在读写连续的扇区比分离的扇区更快。
(3)字符设备只能被顺序读写,而块设备可以随机访问。虽然块设备可随机访问,
但是对于磁盘这类机械设备而言,顺序地组织块设备的访问可以提高性能,如图13.1
所示,对扇区1、10、3、2的请求被调整为对扇区1、2、3、10的请求。而对SD卡、
RamDisk等块设备而言,不存在机械上的原因,进行这样的调整没有必要。
块设备设备描述:
struct gendisk{
int major; //主设备号
int first_minor; //次设备号
int minors;
char disk_name[DISK_NAME_LEN];//驱动名
struct block_device_operations *fops;
struct requst_queue *queue; //请求队列
。。。。。。。。。。。。。。。。。。
int node_id;
};
分配一个struct gendisk结构
struct gendisk *alloc_disk(int minors);(minors 表示分区数)
注册块设备(注册驱动):
void add_disk(struct gendisk *gd);
字符设备通过file_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);
...................................................
};
在Linux内核中,使用struct request来表示等待处理的块设备I/O请求
struct request{
struct list_head queuelist;//链表结构
sector_t sector;//要操作的首个扇区
unsigned long ur_sectors;//要操作的扇区数目
struct bio *bio;//请求的bio结构的链表
struct bio *biotail;//请求的bio结构的链表尾
。。。。。。。。。。。。。。。。。
char *buffer;(用内存模拟的时候buffer指向用户空间)
};
内核提供了一系列函数用来操作请求队列:
struct request_queue *blk_init_queue(request_fn_proc *rfn, spinlock_t *lock);
初始化请求队列,一般在块设备的模块加载函数中调用。
struct blk_cleanup_queue(request_queue *q);
清除请求队列,这个函数完成进请求队列返回 系统的任务,一般在块设备模块卸载函数中调用。
struct request *elv_next_request(request_queue_t *queue);
返回下一个要处理的请求(由I/O调度器决定),如果没有请求则返回NULL。elv_next_request()不会清除请求,他仍然将这个请求保留在队列上,因此连续调用它两次,两次会返回同一个请求结构体。
void blk_dequeue_request(struct request *req);
从队列中删除一个请求。
内存模拟块设备的驱动代码:
初始化函数中:
static int __init ramblock_init(void)
{
/* 分配一块内存 */
ramblock_buf = kzalloc(RAMBLOCK_SIZE, GFP_KERNEL);
if(ramblock_buf == NULL)
printk("kzalloc fail!\n");
/* 分配一个gendisk结构体 */
ramblock_gd = alloc_disk(5); // 参数表示最大的分区数
/* 初始化一个请求队列 */
ramblock_queue = blk_init_queue(do_ramblock_request, &ramblock_lock);
/* 设置 */
major = register_blkdev(0, "ramblock"); // 注册一个块设备
ramblock_gd->major = major;
ramblock_gd->first_minor =0;
sprintf(ramblock_gd->disk_name, "ramblock");
/* 设置块设备的block_device_operations结构体 */
ramblock_gd->fops = &ramblock_fops;
/* 设置初始化的等待队列 */
ramblock_gd->queue = ramblock_queue;
/* 设置块设备的容量,大小是以扇区为单位的 */
set_capacity(ramblock_gd, RAMBLOCK_SIZE/512);
/* 注册 */
add_disk(ramblock_gd);
return 0;
}
/* 定义设置struct block_device_operations结构 */
static const struct block_device_operations ramblock_fops = {
.owner= THIS_MODULE,
.getgeo = ramblock_getgeo, // 设置磁盘的几何结果
};
static int ramblock_getgeo(struct block_device *bdev, struct hd_geometry *geo)
{
geo->heads = 2;
geo->sectors = 32;
geo->cylinders = RAMBLOCK_SIZE/2/32/512;
return 0;
}
static void do_ramblock_request (struct request_queue * q)
{
struct request *req;
while ((req = blk_fetch_request(q)) != NULL)
{
unsigned long start = blk_rq_pos(req) << 9;
unsigned long len = blk_rq_cur_bytes(req);
if (rq_data_dir(req) == READ)
memcpy(req->buffer, start + ramblock_buf, len);
else
memcpy(start + ramblock_buf, req->buffer, len);
__blk_end_request_all(req, 0);
}
}
模块卸载函数:
static void __exit ramblock_exit(void)
{
unregister_blkdev(major, "ramblock");
del_gendisk(ramblock_gd);
put_disk(ramblock_gd);
blk_cleanup_queue(ramblock_queue);
kfree(ramblock_buf);
}