Chinaunix首页 | 论坛 | 博客
  • 博客访问: 265649
  • 博文数量: 25
  • 博客积分: 329
  • 博客等级: 一等列兵
  • 技术积分: 1380
  • 用 户 组: 普通用户
  • 注册时间: 2012-08-24 09:43
文章分类

全部博文(25)

文章存档

2014年(4)

2013年(12)

2012年(9)

分类: 嵌入式

2013-03-26 13:34:08

字符设备与块设备I/O 操作的不同如下。 

1)块设备只能以块为单位接受输入和返回输出,而字符设备则以字节为单位。

大多数设备是字符设备,因为它们不需要缓冲而且不以固定块大小进行操作。 

2)块设备对于I/O 请求有对应的缓冲区,因此它们可以选择以什么顺序进行响

应,字符设备无须缓冲且被直接读写。对于存储设备而言调整读写的顺序作用巨大,

因为在读写连续的扇区比分离的扇区更快。 

3)字符设备只能被顺序读写,而块设备可以随机访问。虽然块设备可随机访问,

但是对于磁盘这类机械设备而言,顺序地组织块设备的访问可以提高性能,如图13.1

所示,对扇区11032的请求被调整为对扇区12310的请求。而对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);

}

阅读(2688) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~