分类: LINUX
2015-07-11 13:57:30
原文地址:block_read_full_page分析 作者:zixin
block_read_full_page函数是当read设备的时候readpage调用的函数,最低层的主函数。
注:
/*
*在解析block_read_full_page时《情景分析》中上册634页有一句话:这个数组(指arr)随
*后被作为参数传递给ll_rw_block( )
*但这里源码的却只用了 submit_bh
*那么我认为在ext2_readpage()中调用block_read_full_page是通过submit_bh( )发出了对各
*个记录块的读入请求
*而并没有对ll_rw_block()发请求
*对ll_rw_block()发出请求的调用一般在直接对块设备读块时才有相关调用
*至少调用ll_rw_block()的时候就意味着是一个同步的读写
*/
基础知识概述:;
块设备缓冲区用buffer_head结构描述,系统中有NR_SIZES种不同尺寸的缓冲区,每种缓
冲区的尺寸为512< grow_buffers(blocksize);
在free_list[]上扩建一页块长为blocksize的备用缓冲区;
bh = create_buffers(page,blocksize,async);
创建块长为blocksize的buffer_head结构来描述页面page.
create_empty_buffers(page,dev,blocksize); //我们这里用到这个
创建块长为blocksize,块设备为dev的buffer_head结构来描述页面page
bh = get_unused_buffer_head(async);
取备用的buffer_head结构,async=1允许进程暂时睡眠.
缓冲区首部结构参见ll_rw_block分析文档
#define MAX_BUF_PER_PAGE (PAGE_CACHE_SIZE / 512) 每页最多的缓冲块数
block_read_full_page函数分析
/*
* Generic "read page" function for block devices that have the normal
* get_block functionality. This is most of the block device filesystems.
* Reads the page asynchronously --- the unlock_buffer() and
* mark_buffer_uptodate() functions propagate buffer state into the
* page struct once IO has completed.
*/
int block_read_full_page(struct page *page, get_block_t *get_block)
/*
*get_block将文件的逻辑块号转换为块设备的逻辑块号
*/
{
struct inode *inode = page->mapping->host;
unsigned long iblock, lblock;
struct buffer_head *bh, *head, *arr[MAX_BUF_PER_PAGE];
unsigned int blocksize, blocks;
int nr, i;
if (!PageLocked(page))
PAGE_BUG(page);
blocksize = 1 << inode->i_blkbits;
/*
*求块大小
*情景分析中为blocksize = inode->i_sb->blocksize,索引节点指向超级块得到其块大小
*这里是2的inode->i_blkbits(索引节点所指的块位数)次方
*/
/*
*每个缓冲页面包含若干记录块缓冲区
*page字段buffers-〉这些干记录块缓冲区的buffer_head数据结构队列
*尚未建立就通过create_empty_buffers建立此队列
*/
if (!page->buffers)
/*以inode的i_dev 为设备标识符号kdev_t传入来初始化bh所属逻辑设备b_dev*/
create_empty_buffers(page, inode->i_dev, blocksize);
head = page->buffers;
blocks = PAGE_CACHE_SIZE >> inode->i_blkbits;
/*记录块起始地址*/
iblock = page->index << (PAGE_CACHE_SHIFT - inode->i_blkbits);
/*文件末尾*/
lblock = (inode->i_size+blocksize-1) >> inode->i_blkbits;
bh = head;
nr = 0;
i = 0;
/*
*对该页面各个记录块缓冲区的循环
*/
do {
/*
*如果记录块内容是一致的,跳过
*/
if (buffer_uptodate(bh))
continue;
/*
*如果记录块缓冲区还没有和物理记录块建立映射关系且记录块起始地址未超出文件末尾
*建立映射
*/
if (!buffer_mapped(bh)) {
if (iblock < lblock) {
if (get_block(inode, iblock, bh, 0))
continue;
}
if (!buffer_mapped(bh)) {
memset(kmap(page) + i*blocksize, 0, blocksize);
flush_dcache_page(page);
kunmap(page);
set_bit(BH_Uptodate, &bh->b_state);
continue;
}
/* get_block() might have updated the buffer synchronously */
if (buffer_uptodate(bh))
continue;
}
/*真正需要读入的记录块缓冲区的buffer_head放到指针数组arr[]中*/
arr[nr] = bh;
nr++;
} while (i++, iblock++, (bh = bh->b_this_page) != head);
if (!nr) {
/*
* all buffers are uptodate - we can set the page
* uptodate as well.
*/
/*
* 所有的buffers都为uptodate则其对应的所在页也可均设为uptodate
*/
SetPageUptodate(page);
UnlockPage(page);
return 0;
}
/* Stage two: lock the buffers */
/* 给所有的buffers加锁*/
for (i = 0; i < nr; i++) {
struct buffer_head * bh = arr[i];
lock_buffer(bh);
/*
* 设置bh的end_io字段,确定设备I/O完成要启动的操作
* 这里读是异步的,不用等待。中断完成读写后,调用end_buffer_io_async()
* 将bh的相关标志设定好,同时将与bh对应的page的UPDATE标志也设定好,于是预读的page在下次文件读时就可以直接使用了
*/
set_buffer_async_io(bh);
}
/* Stage 3: start the IO */
for (i = 0; i < nr; i++) {
struct buffer_head * bh = arr[i];
if (buffer_uptodate(bh))
/*再次检查后,如果buffer是最新的,就不用读了*/
end_buffer_io_async(bh, 1);
else
/*调用submit_bh提交驱程*/
submit_bh(READ, bh);
}
wakeup_page_waiters(page);
return 0;
}
***********************
inline void set_buffer_async_io(struct buffer_head *bh)
{
bh->b_end_io = end_buffer_io_async;
mark_buffer_async(bh, 1);
}
***********************