do_generic_file_read()函数

来源:互联网 发布:16单片机pwm调速原理 编辑:程序博客网 时间:2024/04/26 10:34

本文重点分析一下linux内核中的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;      /* offset into pagecache page */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;/* Did it get truncated before we got the lock? */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);}page_ok:/* * i_size must be checked after we know the page is Uptodate. * * Checking i_size after the check allows us to calculate * the correct value for "nr", which means the zero-filled * part of the page is not copied back to userspace (unless * another truncate extends the file - this is desired though). */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 is the maximum number of bytes to copy from this page */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 users can be writing to this page using arbitrary * virtual addresses, take care about potential aliasing * before reading the page on the kernel side. */if (mapping_writably_mapped(mapping))flush_dcache_page(page);/* * When a sequential read accesses a page several times, * only mark it as accessed the first time. */if (prev_index != index || offset != prev_offset)mark_page_accessed(page);prev_index = index;/* * Ok, we have the page, and it's up-to-date, so * now we can copy it to user space... * * The actor routine returns how many bytes were actually used.. * NOTE! This may not be the same as how much of a user buffer * we filled up (we may be padding etc), so we can only update * "pos" here (the actor routine has to update the user buffer * pointers and the remaining count). */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;page_not_up_to_date:/* Get exclusive access to the page ... */error = lock_page_killable(page);if (unlikely(error))goto readpage_error;page_not_up_to_date_locked:/* Did it get truncated before we got the lock? */if (!page->mapping) {unlock_page(page);page_cache_release(page);continue;}/* Did somebody else fill it already? */if (PageUptodate(page)) {unlock_page(page);goto page_ok;}readpage:/* * A previous I/O error may have been due to temporary * failures, eg. multipath errors. * PG_error will be set again if readpage fails. */ClearPageError(page);/* Start the actual read. The read will unlock the 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)) {error = lock_page_killable(page);if (unlikely(error))goto readpage_error;if (!PageUptodate(page)) {if (page->mapping == NULL) {/* * invalidate_mapping_pages got it */unlock_page(page);page_cache_release(page);goto find_page;}unlock_page(page);shrink_readahead_size_eio(filp, ra);error = -EIO;goto readpage_error;}unlock_page(page);}goto page_ok;readpage_error:/* UHHUH! A synchronous read error occurred. Report it */desc->error = error;page_cache_release(page);goto out;no_cached_page:/* * Ok, it wasn't cached, so we need to create a new * 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;}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);}


0 0