块设备(二)
来源:互联网 发布:java车位管理系统源码 编辑:程序博客网 时间:2024/06/08 05:24
块设备驱动(二)
块设备数据访问流程:
generic_make_request () ------> __genenric_make_request() -----> q->make_request_fn(q, bio)
当有用户需求来的时候,在通用块层使用generic_make_request () 形成一个请求,然后调用
__genenric_make_request(bio)形成一个bio,再调用块设备请求队列的q->make_request_fn(q, bio),每个块设备请求队列request_queue都有一个函数指针,
struct request_queue {
.......................
make_request_fn *make_request_fn ;
......................
}
struct request_queue *blk_init_queue(request_fn_proc *rfn,spinlock *lock) {
return blk_init_queue_node (rfn,lock,-1) ;
}
blk_init_queue_node (request_fn_proc *rfn,spinlock *lock) {
......................
blk_queue_make_request (q, __make_request) ; //把等待队列q和函数__make_request关联,__make_request负责制造struct request,每个请求由struct request描述
.........................
}
void blk_queue_make_request (struct request_queue *q,make_request_fn *mfn) {
.........................
q->make_request_fn = mfn ;
.........................
}
make_request制造请求元素是Bio,1个struct Bio代表一次块设备I/O请求,IO调度器可将连续的bio合并成一个请求struct request,对每个扇区的访问就是一个bio,request把连续的访问合在一起,通过__make_reques按照调度算法访问磁盘合并bio,提高访问效率。
struct bio {
secotor_t bi_sector ; //要访问的第一个扇区
unsigned int bi_size ; //以字节为单位传输数据的大小
struct bio_vec *bi_io_vec ; //实际的vec列表
...........................
};
struct bio_vec {
struct page *bv_page ; //页指针
unsigned int bv_len ; //传输的数据长度
unsigned int bv_offset ; //偏移量
}; //反应用户信息,从磁盘读取数据的存放位置,存放在哪一页以及偏移
有的块设备需要调度算法去支撑,比如磁盘的磁头要移动,但是有的设备没有这种移动,比如内存、U盘,就不要这种调度器,不用制作make_request,直接依次处理bio,这样就不会形成register。要直接处理bio就不许内核提供的处理函数__make_request,自己实现请求队列里的__make_request_fn。
自己实现:
request_queue_t *blk_alloc_queue (int gfp_mask) 分配一个请求队列。
void blk_queue_make_request (request_queue_t *q, make_request_fn *mfn)
把自己实现的bio请求处理函数mfn赋值给请求队列q里的成员q->make_request_fn = mfn。
块设备驱动程序(不使用IO调度器)#define SIMP_BLKDEV_DEVICEMAJOR COMPAQ_SMART2_MAJOR#define SIMP_BLKDEV_DISKNAME "simp_blkdev"#define SIMP_BLKDEV_BYTES (16*1024*1024)static struct request_queue *simp_blkdev_queue;static struct gendisk *simp_blkdev_disk;unsigned char simp_blkdev_data[SIMP_BLKDEV_BYTES];static int simp_blkdev_make_request(struct request_queue *q, struct bio *bio)//在simple-blk.c传给request_queue,从里取出request处理,这里传给request和bio{ struct bio_vec *bvec; int i; void *dsk_mem; if ((bio->bi_sector << 9) + bio->bi_size > SIMP_BLKDEV_BYTES) { //访问数据量是否超出磁盘范围 printk(KERN_ERR SIMP_BLKDEV_DISKNAME ": bad request: block=%llu, count=%u\n", (unsigned long long)bio->bi_sector, bio->bi_size);#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 24)//程序的兼容性 bio_endio(bio, 0, -EIO);#else bio_endio(bio, -EIO);#endif return 0; } dsk_mem = simp_blkdev_data + (bio->bi_sector << 9); bio_for_each_segment(bvec, bio, i) {//bio由一个一个段构成,对于每个段要去进行相应处理 void *iovec_mem; switch (bio_rw(bio)) { //判断bio方向 case READ: case READA: iovec_mem = kmap(bvec->bv_page) + bvec->bv_offset; //kmap把页指针转换成地址加上偏移得到数据位于地址。 memcpy(iovec_mem, dsk_mem, bvec->bv_len); kunmap(bvec->bv_page); break; case WRITE: iovec_mem = kmap(bvec->bv_page) + bvec->bv_offset; memcpy(dsk_mem, iovec_mem, bvec->bv_len); kunmap(bvec->bv_page); break; default: printk(KERN_ERR SIMP_BLKDEV_DISKNAME ": unknown value of bio_rw: %lu\n", bio_rw(bio));#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 24) bio_endio(bio, 0, -EIO);#else bio_endio(bio, -EIO);#endif return 0; } dsk_mem += bvec->bv_len; }#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 24) bio_endio(bio, bio->bi_size, 0);#else bio_endio(bio, 0);#endif return 0;}//没有request,直接拿到处理bio,绕过调度算法struct block_device_operations simp_blkdev_fops = { .owner = THIS_MODULE,};static int __init simp_blkdev_init(void){ int ret; simp_blkdev_queue = blk_alloc_queue(GFP_KERNEL);//分配一个请求队列 if (!simp_blkdev_queue) { ret = -ENOMEM; goto err_alloc_queue; } blk_queue_make_request(simp_blkdev_queue, simp_blkdev_make_request); //绑定make_request和请求队列,在simple-blk.c当有请求后,首先调用内核__make_request,然后调用自己提供的simp_blkdev_do_request,在本程序没有使用__make_rwquest有bio时,直接交给simp_blkdev_make_request,自己要实现其他调度算法,把bio按照自己方法处理形成一个request。 simp_blkdev_disk = alloc_disk(1); if (!simp_blkdev_disk) { ret = -ENOMEM; goto err_alloc_disk; } strcpy(simp_blkdev_disk->disk_name, SIMP_BLKDEV_DISKNAME); simp_blkdev_disk->major = SIMP_BLKDEV_DEVICEMAJOR; simp_blkdev_disk->first_minor = 0; simp_blkdev_disk->fops = &simp_blkdev_fops; simp_blkdev_disk->queue = simp_blkdev_queue; set_capacity(simp_blkdev_disk, SIMP_BLKDEV_BYTES>>9); add_disk(simp_blkdev_disk); //填充gendisk并注册到内核 return 0;err_alloc_disk: blk_cleanup_queue(simp_blkdev_queue);err_alloc_queue: return ret;}static void __exit simp_blkdev_exit(void){ del_gendisk(simp_blkdev_disk); put_disk(simp_blkdev_disk); blk_cleanup_queue(simp_blkdev_queue);}module_init(simp_blkdev_init);module_exit(simp_blkdev_exit);
- 块设备(二)
- 块设备驱动程序<二>
- 块设备驱动程序<二>
- 块设备驱动实战高级篇二(在内核块设备中运用进程)
- Linux块设备驱动(二)————块设备的体系架构
- linux 块设备驱动(二)——块设备数据结构
- 块设备驱动之二
- linux下的块设备驱动(二)
- [linux驱动]linux块设备学习笔记(二)
- Linux 驱动之块设备结构体 (二)
- Ceph实战入门系列(二)——块设备
- 块设备(一)
- Linux设备驱动--块设备(二)之相关结构体
- Linux设备驱动--块设备(二)之相关结构体
- 基于mini6410的linux驱动学习总结(二 字符设备与块设备的区别)
- Linux设备驱动--块设备(二)之相关结构体
- Linux设备驱动--块设备(二)之相关结构体
- Linux设备驱动--块设备(二)之相关结构体
- 开源VOIP代理、客户端和开发库一览
- C语言vector的使用方法
- Android OpenGL ES(四)其他语句介绍
- javascript原生代码实现通用运动框架
- 图像传输格式解析
- 块设备(二)
- 趣味程序设计_高次方数
- 计算机图像导入DM642目标板的方法尝试
- CentOS中文显示和中文输入法安装
- hdu 1227 Fast Food(dp)
- <原创> hdu 1015 Safecracker
- 理解专业程序员
- [转载]iphone开发--改变UIPageControl里的小点的颜色
- js和css的顺序关系