当要读的页在page cache中数据不是最新的或者在页在page cache不存在时,则需要从磁盘读数据。通过接下来的readpage函数来实现从磁盘读文件,readpage函数由具体的文件系统提供。比如ext2文件系统所对应的为:ext2_readpage().
文件系统的读写请求,最终要转换成对块设备的读写请求。这涉及几个问题:
1、文件对用户呈现一个连续的读写接口,但文件在真正的物理设备上的存储可能不是连续的,如果不是连续的,对文件的读写就不能用同一个I/O完成,而是需要拆分。
2、硬盘的读写最小单元通常是扇区,通常一个扇区是512字节,而文件的最小单元是块,一个块可以由多个扇区组成。组成块的扇区物理地址是连续的,而块与块之间可以不连续。
3、内核通过submit_bio来提交一个I/O给底层。同时内核又提供一个submit_bh来提交块。(submit_bh最终也是通过submit_bio来实现的)
static int ext2_readpage(struct file *file, struct page *page)
{
____return mpage_readpage(page, ext2_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;
____clear_buffer_mapped(&map_bh);
____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;
}
mpage_readpage->do_mpage_readpage->mpage_bio_submit最终提交bio给下一层。在提交bio过程中会注册,bio->bi_end_io = mpage_end_io_read供中断处理函数在中断处理过程中调用。
static struct bio *mpage_bio_submit(int rw, struct bio *bio)
{
____bio->bi_end_io = mpage_end_io_read;
____if (rw == WRITE)
________bio->bi_end_io = mpage_end_io_write;
____submit_bio(rw, bio);
____return NULL;
}
mpage_end_io_read这个回调函数会遍历bio结构的每个向量,看相关页面是否获得最新的数据。如果成功,则设置页面状态为最新,同时解锁页面。解锁页面将唤醒之前等待的页面更新进程;如果数据未更新,则设置页面错误,解锁页面,唤醒等待页面更新的进程。
static int mpage_end_io_read(struct bio *bio, unsigned int bytes_done, int err)
{
____const int uptodate = test_bit(BIO_UPTODATE, &bio->bi_flags);
____struct bio_vec *bvec = bio->bi_io_vec + bio->bi_vcnt - 1;
____if (bio->bi_size)
________return 1;
____do {
________struct page *page = bvec->bv_page;
________if (--bvec >= bio->bi_io_vec)
____________prefetchw(&bvec->bv_page->flags);
________if (uptodate) {
____________SetPageUptodate(page);
________} else {
____________ClearPageUptodate(page);
____________SetPageError(page);
________}
________unlock_page(page);
____} while (bvec >= bio->bi_io_vec);
____bio_put(bio);
____return 0;
}