块设备工作原理分析

2022-05-08 16:12:52 浏览数 (1)

代码语言:javascript复制
/*分析 块设备的工作原理*/
void ll_rw_block(int rw, int nr, struct buffer_head *bhs[])
{
	int i;

	for (i = 0; i < nr; i  ) {
		struct buffer_head *bh = bhs[i];

		if (!trylock_buffer(bh))
			continue;
		//写操作
		if (rw == WRITE) {
			if (test_clear_buffer_dirty(bh)) {
				bh->b_end_io = end_buffer_write_sync;
				get_bh(bh);
				submit_bh(WRITE, bh);
				continue;
			}
		} else {
			//读操作
			if (!buffer_uptodate(bh)) {
				bh->b_end_io = end_buffer_read_sync;
				get_bh(bh);
				//提交操作
				submit_bh(rw, bh);
				continue;
			}
		}
		unlock_buffer(bh);
	}
}

int submit_bh(int rw, struct buffer_head * bh)
{
	/*
	 * from here on down, it's all bio -- do the initial mapping,
	 * submit_bio -> generic_make_request may further map this bio around
	 */
	 
	//分配bio结构,然后用dh初始化bio结构
	bio = bio_alloc(GFP_NOIO, 1);

	bio->bi_sector = bh->b_blocknr * (bh->b_size >> 9);
	bio->bi_bdev = bh->b_bdev;
	bio->bi_io_vec[0].bv_page = bh->b_page;
	bio->bi_io_vec[0].bv_len = bh->b_size;
	bio->bi_io_vec[0].bv_offset = bh_offset(bh);

	bio->bi_vcnt = 1;
	bio->bi_idx = 0;
	bio->bi_size = bh->b_size;

	bio->bi_end_io = end_bio_bh_io_sync;
	bio->bi_private = bh;

	bio_get(bio);
	//根据注释进一步初始化bio结构
	submit_bio(rw, bio);

}

//用bio结构构造request请求
void submit_bio(int rw, struct bio *bio)
{
	generic_make_request(bio);	
}

void generic_make_request(struct bio *bio)
{
	struct bio_list bio_list_on_stack;

	//如果当前的bio_list存在了,直接返回
	if (current->bio_list) {
		/* make_request is active */
		bio_list_add(current->bio_list, bio);
		return;
	}
	
	//将bio_list_on_stack放入bio_list中
	BUG_ON(bio->bi_next);
	bio_list_init(&bio_list_on_stack);
	current->bio_list = &bio_list_on_stack;
	do {
		//进一步构造通用请求
		__generic_make_request(bio);
		bio = bio_list_pop(current->bio_list);
	} while (bio);
	current->bio_list = NULL; /* deactivate */
	
}

static inline void __generic_make_request(struct bio *bio)
{
do {
	  //获得请求队列
		q = bdev_get_queue(bio->bi_bdev);

		//调用队列中的"构造请求函数"
		ret = q->make_request_fn(q, bio);   //当我们写驱动程序时,不提供request函数时,系统有一个默认的函数__make_request
	} while (ret);	
}

//看默认的request函数都干啥了
static int __make_request(struct request_queue *q, struct bio *bio)
{
	/*
	 * Check if we can merge with the plugged list before grabbing
	 * any locks.
	 */
	 
	 //先尝试合并,如果合并成功则退出
	if (attempt_plug_merge(current, q, bio))
		goto out;

	spin_lock_irq(q->queue_lock);

	//如果不退出,则接着继续合并。调用的是电梯队列中的elevator_merge_fn函数
	el_ret = elv_merge(q, &req, bio);
	if (el_ret == ELEVATOR_BACK_MERGE) {//向后开始合并
		if (bio_attempt_back_merge(q, req, bio)) {
			if (!attempt_back_merge(q, req))
				elv_merged_request(q, req, el_ret);
			goto out_unlock;
		}
	} else if (el_ret == ELEVATOR_FRONT_MERGE) {//从前面开始合并
		if (bio_attempt_front_merge(q, req, bio)) {
			if (!attempt_front_merge(q, req))
				elv_merged_request(q, req, el_ret);
			goto out_unlock;
		}
	}
	
	//如果合并不成功,则用bio结构构造request
	init_request_from_bio(req, bio);

	//执行队列
 __blk_run_queue(q);

}void __blk_run_queue(struct request_queue *q)
{
	if (unlikely(blk_queue_stopped(q)))
		return;

  //调用队列的处理函数, 当我们写驱动时,我们写的处理函数就在这里使用
	q->request_fn(q);
}

/*
总结:
     (1): 对于块设备来说,不能像字符设备是按顺序存取的。为了提高效率,总是将同方向的任务先处理(就像电梯一样)。这样系统的效率会大大的提高。
     (2): 当处理任务时,先是把任务放入队列中,根据某种算法(电梯算法)来优化任务。重新排序任务的顺序等。
     (3): 然后当我们写驱动程序的时候就会编写自己的request去替代默认的request函数去处理请求的。
*/

0 人点赞