Chinaunix首页 | 论坛 | 博客
  • 博客访问: 2976316
  • 博文数量: 401
  • 博客积分: 12926
  • 博客等级: 上将
  • 技术积分: 4588
  • 用 户 组: 普通用户
  • 注册时间: 2009-02-22 14:51
文章分类

全部博文(401)

文章存档

2015年(16)

2014年(4)

2013年(12)

2012年(82)

2011年(98)

2010年(112)

2009年(77)

分类: LINUX

2010-11-11 17:33:29

块设备层分析(1)

R.wen

一、综述

1是块设备操作的一个分层实现图。当一个进程调用read读取一个文件时,内核执行如下一个过程:首先,它通过VFS层去读取要到的文件块有没有已经被cache了,这个cache由一个buffer_head结构读取。如果要读取的文件块还没有被cache,则就要从文件系统中去读取了,这就是文件系统的映射层,它通过一个address_space结构来引用,然后调用文件系统读函数(readpage)去读取一个页面大小的数据,这个读函数对于不同的文件系统来说,是不一样的。当它从磁盘中读出数据时,它会将数据页链入cache中,当下次再读取时,就不需要再次从磁盘出去读了。Readpage()函数并不是直接去操作磁盘,而只是将请求初始化成一个bio结构,并提交给通用块层(generic block layer)


1

       它就通过submit_bio()去完成的。通用块层再调用相应设备的IO调度器,通过这个调度器的调度算法,将这个bio或合并到已存在的request中,或创建一个新的request,并将这个新创建的request插入到设备的请求队列中去。这就完成了IO调度层的工作。最后就是块设备驱动所做的工作了。IO调度器传递给块驱动的是一个请求队列,块驱动就是要处理这个队列中的请求,直到这个队列为空为止。

二、通用块层(generic block layer)

通用块层操作的是一个bio结构,这个结构主要的数据域是,

unsigned short              bi_vcnt;

struct bio_vec        *bi_io_vec;     /* the actual vec list */

这个就是要读写的数据向量,且每个struct bio_vec       为一个segment

//这个函数主要是调用generic_make_request()去完成工作:

void submit_bio(int rw, struct bio *bio)

{

       ……

       generic_make_request(bio);

}

//这个函数的主要作用是将bio传递给驱动去处理

void generic_make_request(struct bio *bio)

{

       ……

       do {

              char b[BDEVNAME_SIZE];

              //取得块设备相应的队列,每个设备一个

              q = bdev_get_queue(bio->bi_bdev);

             

              /*

              * If this device has partitions, remap block n

              * of partition p to block n+start(p) of the disk.

              */

              blk_partition_remap(bio); //块设备分区信息转换,如将相对于一个分区的的偏移地址转换成相对于整个块设备的绝对偏移等等。

             

              old_sector = bio->bi_sector;

              old_dev = bio->bi_bdev->bd_dev;

              ……

              //这个是块设备队列的请求处理函数。由块设备创建请求队列时初始化。

              //对于IDE等设备,它是__make_request()。但对于ramdisk就不一样了。

              ret = q->make_request_fn(q, bio); // __make_request()

       } while (ret);

}

//这要函数的主要作用就是调用IO调度算法将bio合并,或插入到队列中合适的位置中去

static int __make_request(request_queue_t *q, struct bio *bio)

{

       struct request *req;

       int el_ret, nr_sectors, barrier, err;

       const unsigned short prio = bio_prio(bio);

       const int sync = bio_sync(bio);

       int rw_flags;

       nr_sectors = bio_sectors(bio);

       //用于处理高端内存

       blk_queue_bounce(q, &bio);

      

       spin_lock_irq(q->queue_lock);

       //测试是否能合并,本文忽略IO调度算法

       el_ret = elv_merge(q, &req, bio);

       switch (el_ret) {

              //前两种可以合并

              case ELEVATOR_BACK_MERGE:

                     ……

                     goto out;

              case ELEVATOR_FRONT_MERGE:

                     ……

                     goto out;

             

//不能合并,需要新创一个request

              /* ELV_NO_MERGE: elevator says don't/can't merge. */

              default:

                     ;

       }

get_rq:

      

       rw_flags = bio_data_dir(bio);

       if (sync)

              rw_flags |= REQ_RW_SYNC;

       //新创一个request

       req = get_request_wait(q, rw_flags, bio);

       //初始化这个request

       init_request_from_bio(req, bio);

       spin_lock_irq(q->queue_lock);

       if (elv_queue_empty(q)) //空队列的处理

              blk_plug_device(q);

       add_request(q, req); //将新请求加入队列中去

out:

       if (sync) //如果需要同步,立即处理请求

              __generic_unplug_device(q);

       spin_unlock_irq(q->queue_lock);

       return 0;

end_io:

       bio_endio(bio, nr_sectors << 9, err);

       return 0;

}

//触发块设备驱动进行真正的IO操作

void __generic_unplug_device(request_queue_t *q)

{

       if (unlikely(blk_queue_stopped(q)))

              return;

       if (!blk_remove_plug(q))

              return;

       q->request_fn(q); //设备的请求处理函数,属于驱动层

}    


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