【read--->vfs_read--->do_sync_read--->aio_read--->do_generic_file_read】
static void do_generic_file_read(struct file *filp, loff_t *ppos,
read_descriptor_t *desc, read_actor_t actor)
{
struct address_space *mapping = filp->f_mapping;
struct inode *inode = mapping->host;
struct file_ra_state *ra = &filp->f_ra;
pgoff_t index;
pgoff_t last_index;
pgoff_t prev_index;
unsigned long offset;
unsigned int prev_offset;
int error;
index = *ppos >> PAGE_CACHE_SHIFT;
prev_index = ra->prev_pos >> PAGE_CACHE_SHIFT;
prev_offset = ra->prev_pos & (PAGE_CACHE_SIZE-1);
last_index = (*ppos + desc->count + PAGE_CACHE_SIZE-1) >> PAGE_CACHE_SHIFT;
offset = *ppos & ~PAGE_CACHE_MASK;
for (;;) {
struct page *page;
pgoff_t end_index;
loff_t isize;
unsigned long nr, ret;
cond_resched();
find_page:
page = find_get_page(mapping, index);
if (!page) {
page_cache_sync_readahead(mapping,
ra, filp,
index, last_index - index);
page = find_get_page(mapping, index);
if (unlikely(page == NULL))
goto no_cached_page;
}
if (PageReadahead(page)) {
page_cache_async_readahead(mapping,
ra, filp, page,
index, last_index - index);
}
if (!PageUptodate(page)) {
if (inode->i_blkbits == PAGE_CACHE_SHIFT ||
!mapping->a_ops->is_partially_uptodate)
goto page_not_up_to_date;
if (!trylock_page(page))
goto page_not_up_to_date;
if (!page->mapping)
goto page_not_up_to_date_locked;
if (!mapping->a_ops->is_partially_uptodate(page,
desc, offset))
goto page_not_up_to_date_locked;
unlock_page(page);
}
首先调用find_get_page检查页是否已经包含在页缓存中。如果没有则调用page_cache_sync_readahead发出一个同步预读请求。预读机制很大程度上能够保证数据已经进入缓存,因此再次调用find_get_page查找该页。这次仍然有一定失败的几率,那么就跳转到标签no_cached_page处直接进行读取操作。检测页标志是否设置了PG_readahead,如果设置了该标志就调用page_cache_async_readahead启动一个异步预读操作,这与前面的同步预读操作不同,这里并不等待预读操作的结束。虽然页在缓存中了,但是其数据不一定是最新的,这里通过PageUptodate(page)来检查。如果数据不是最新的,则调用函数mapping->a_ops->readpage进行再次数据读取。
page_ok:
isize = i_size_read(inode);
end_index = (isize - 1) >> PAGE_CACHE_SHIFT;
if (unlikely(!isize || index > end_index)) {
page_cache_release(page);
goto out;
}
nr = PAGE_CACHE_SIZE;
if (index == end_index) {
nr = ((isize - 1) & ~PAGE_CACHE_MASK) + 1;
if (nr <= offset) {
page_cache_release(page);
goto out;
}
}
nr = nr - offset;
if (mapping_writably_mapped(mapping))
flush_dcache_page(page);
if (prev_index != index || offset != prev_offset)
mark_page_accessed(page);
prev_index = index;
ret = actor(desc, page, offset, nr);
offset += ret;
index += offset >> PAGE_CACHE_SHIFT;
offset &= ~PAGE_CACHE_MASK;
prev_offset = offset;
page_cache_release(page);
if (ret == nr && desc->count)
continue;
goto out;
页已经进入页缓存中,而且页中数据也是最新的,此时调用函数actor将页中数据拷贝到用户空间。 函数指针actor指向函数file_read_actor。
page_not_up_to_date:
error = lock_page_killable(page);
if (unlikely(error))
goto readpage_error;
page_not_up_to_date_locked:
if (!page->mapping) {
unlock_page(page);
page_cache_release(page);
continue;
}
if (PageUptodate(page)) {
unlock_page(page);
goto page_ok;
}
readpage:
ClearPageError(page);
error = mapping->a_ops->readpage(filp, page);
if (unlikely(error)) {
if (error == AOP_TRUNCATED_PAGE) {
page_cache_release(page);
goto find_page;
}
goto readpage_error;
}
if (!PageUptodate(page)) {
......
goto readpage_error;
}
unlock_page(page);
}
goto page_ok;
如果页缓存中的数据不是最新的则通过函数mapping->a_ops->readpage读取页缓存最新的数据。如果页缓存中的数据仍然不是最新的则出错返回,否则跳转到标签page_ok处。
readpage_error:
desc->error = error;
page_cache_release(page);
goto out;
no_cached_page:
page = page_cache_alloc_cold(mapping);
if (!page) {
desc->error = -ENOMEM;
goto out;
}
error = add_to_page_cache_lru(page, mapping,
index, GFP_KERNEL);
if (error) {
page_cache_release(page);
if (error == -EEXIST)
goto find_page;
desc->error = error;
goto out;
}
goto readpage;
}
如果没有找到页缓存就通过函数page_cache_alloc_cold分配一个缓存冷页,然后通过函数add_to_page_cache_lru将该页加入到页缓存的LRU链表中,然后跳转到标签readpage处读取该页中数据。
out:
ra->prev_pos = prev_index;
ra->prev_pos <<= PAGE_CACHE_SHIFT;
ra->prev_pos |= prev_offset;
*ppos = ((loff_t)index << PAGE_CACHE_SHIFT) + offset;
file_accessed(filp);
}
【read--->vfs_read--->do_sync_read--->aio_read--->do_generic_file_read--->readpage】
static int ext3_readpage(struct file *file, struct page *page)
{
trace_ext3_readpage(page);
return mpage_readpage(page, ext3_get_block);
}
完成一页数据的读取。ext3_get_block作为函数参数传入,该函数的工作是从文件内的逻辑记录块号到设备上的记录块号之间的映射。
【read--->vfs_read--->do_sync_read--->aio_read--->do_generic_file_read--->readpage--->mpage_readpage】
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;
}
将要读取的数据转换为bio结构,然后提交该bio到请求队列中。