当所读文件所在的块并不在页高速缓存,我们需要使用readpage方法把页从磁盘读到内存中来,并加入到页缓存中去。
address_space对象的readpage方法存放的是函数地址,这种函数激活从磁盘到页高速缓存的io数据传送。对于普通文件,这个字段通常指向调用mpage_readpage( )函数的封装函数。如ext2文件系统的readpage方法:
static int ext2_readpage(struct file *file, struct page *page)
{
return mpage_readpage(page, ext2_get_block);
}
该函数调用 mpage_readpage函数,传给他需要读入页高速缓存的页面的页描述符地址,以及ext2_get_block函数地址作为参数。
需要封装函数是因为mpage_readpage()函数接收的参数为待填充页的页描述符page及有助于mpage_readpage()找到正确块的函数的地址get_block。封装函数依赖文件系统并因此能提供适当的函数来得到块。这个函数把相对于文件开始位置的逻辑块号转换为相对于磁盘分区开始位置的逻辑块号。
后一个参数依赖于普通文件所在文件系统的类型。在我们的例子中,这个参数就是ext2_get_block()函数的地址。所传递的get_block函数总是用缓冲区首部buffer_head来存放有关重要信息,如块设备(b_dev字段)、设备上请求数据的位置(b_blocknr字段)和块状态(b_state字段)。
函数mpage_readpage()在从磁盘读入一页时可选择两种不同的策略。如果包含请求数据的块在磁盘上是连续的,那么函数就用单个bio描述符向通用块层发出读I/O操作;而如果不连续,函数就对页上的每一块用不同的bio描述符来读。所以,依赖于文件系统的get_block函数的一个重要作用就是:确定文件中的下一块在磁盘上是否也是下一块。
int mpage_readpage(struct page *page, get_block_t get_block)
{
struct bio *bio = NULL;
sector_t last_block_in_bio = 0;
struct buffer_head map_bh;
unsigned long first_logical_block = 0;
map_bh.b_state = 0;
map_bh.b_size = 0;
bio = do_mpage_readpage(bio, page, 1, &last_block_in_bio,
&map_bh, &first_logical_block, get_block);
if (bio)
mpage_bio_submit(READ, bio);
return 0;
}
首先将一个类型为buffer_head的变量map_bh的b_state和b_size字段清零;随后调用函数 do_mpage_readpage 函数创建了一个 bio 请求,该请求指明了要读取的数据块所在磁盘的位置、数据块的数量以及拷贝该数据的目标位置——缓存区中 page 的信息。然后调用 mpage_bio_submit 函数处理请求。
下面我们就研究一下do_mpage_readpage