普通文件的readpage方法(do

xiaoxiao2021-02-28  62

当所读文件所在的块并不在页高速缓存,我们需要使用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

static struct bio * do_mpage_readpage(struct bio *bio, struct page *page, unsigned nr_pages, sector_t *last_block_in_bio, struct buffer_head *map_bh, unsigned long *first_logical_block, get_block_t get_block) { struct inode *inode = page->mapping->host;//得到文件的索引节点 const unsigned blkbits = inode->i_blkbits; //得到每一个页面应该存放多少个块 const unsigned blocks_per_page = PAGE_CACHE_SIZE >> blkbits; const unsigned blocksize = 1 << blkbits; sector_t block_in_file; sector_t last_block; sector_t last_block_in_file; sector_t blocks[MAX_BUF_PER_PAGE]; unsigned page_block; unsigned first_hole = blocks_per_page; struct block_device *bdev = NULL; int length; int fully_mapped = 1; unsigned nblocks; unsigned relative_block; /*如果置位,则该页是块设备页高速缓存的页,也就是该页与描述组成该页的块的缓冲区首 部链表相关。这意味着该页过去已从磁盘读入过,而且页中的块在磁盘上不是相邻的。跳到 标号confused处,用一次读一块的方式读该页。*/ if (page_has_buffers(page)) goto confused; //页中第一块的文件块号,也就是相对于文件起始位置页中第一块的索引 block_in_file = (sector_t)page->index << (PAGE_CACHE_SHIFT - blkbits); //要读的最后一页中最后一块的文件块号 last_block = block_in_file + nr_pages * blocks_per_page; //该文件最后一页中最后一块的文件块号 last_block_in_file = (i_size_read(inode) + blocksize - 1) >> blkbits; if (last_block > last_block_in_file) last_block = last_block_in_file; page_block = 0; /* * 处理如果map_bh已经被映射,并且页中第一块的文件块号位于传递进来的 * first_logical_block参数和一个buffer_head最后一个块之间,则对未映射的部 * 分进行处理。由于我们在mpage_readpage函数中将map_bh的b_state字段清零了 * 的,而且传递进来的first_logical_block为0,所以略过这段代码。 */ nblocks = map_bh->b_size >> blkbits; if (buffer_mapped(map_bh) && block_in_file > *first_logical_block && block_in_file < (*first_logical_block + nblocks)) { unsigned map_offset = block_in_file - *first_logical_block; unsigned last = nblocks - map_offset; for (relative_block = 0; ; relative_block++) { if (relative_block == last) { clear_buffer_mapped(map_bh); break; } if (page_block == blocks_per_page) break; blocks[page_block] = map_bh->b_blocknr + map_offset + relative_block; page_block++; block_in_file++; } bdev = map_bh->b_bdev; } /* * Then do more get_blocks calls until we are done with this page. */ map_bh->b_page = page; while (page_block < blocks_per_page) { map_bh->b_state = 0; map_bh->b_size = 0; if (block_in_file < last_block) { //从该页中一共要读 (last_block-block_in_file)个块*块大小 个字节 map_bh->b_size = (last_block-block_in_file) << blkbits; if (get_block(inode, block_in_file, map_bh, 0)) goto confused;//页中连续的块在磁盘中并不连续 *first_logical_block = block_in_file; } //bh没有被映射,可能是一个文件空洞 if (!buffer_mapped(map_bh)) { fully_mapped = 0; if (first_hole == blocks_per_page) first_hole = page_block; page_block++; block_in_file++; continue; } /* some filesystems will copy data into the page during * the get_block call, in which case we don't want to * read it again. map_buffer_to_page copies the data * we just collected from get_block into the page's buffers * so readpage doesn't have to repeat the get_block call */ //如果块缓存区是最新的,将其数据直接copy到page if (buffer_uptodate(map_bh)) { map_buffer_to_page(page, map_bh, page_block); goto confused; } //有文件空洞,不能一次从磁盘读一整页 if (first_hole != blocks_per_page) goto confused; /* hole -> non-hole */ /* 判断请求的块缓存是不是连续的。如果不连续,就跳转到confused*/ if (page_block && blocks[page_block-1] != map_bh->b_blocknr-1) goto confused; //物理上相邻的块的个数 nblocks = map_bh->b_size >> blkbits; for (relative_block = 0; ; relative_block++) { if (relative_block == nblocks) { clear_buffer_mapped(map_bh); break; } else if (page_block == blocks_per_page) break; //记录每一个块对应的磁盘逻辑块号 blocks[page_block] = map_bh->b_blocknr+relative_block; page_block++; block_in_file++; } bdev = map_bh->b_bdev; } /*运行至这,说明页中的所有块在磁盘上是相邻的。然而,它可能是文件中的最后一页,因 此页中的一些块可能在磁盘上没有映像。如果这样的话,将页中相应的块缓冲区填上0;如果 不是这样,将页描述符的标志PG_mappedtodisk置位。*/ if (first_hole != blocks_per_page) { zero_user_segment(page, first_hole << blkbits, PAGE_CACHE_SIZE); if (first_hole == 0) { SetPageUptodate(page); unlock_page(page); goto out; } } else if (fully_mapped) { SetPageMappedToDisk(page); } if (fully_mapped && blocks_per_page == 1 && !PageUptodate(page) && cleancache_get_page(page) == 0) { SetPageUptodate(page); goto confused; } /* * This page will go to BIO. Do we need to send this BIO off first? */ if (bio && (*last_block_in_bio != blocks[0] - 1)) bio = mpage_bio_submit(READ, bio); alloc_new: if (bio == NULL) { /*一个新bio描述符,并且分别用块设备描述符地址和页中第一个块的逻辑块号blocks[0] 来初始化bi_bdev字段和bi_sector字段*/ bio = mpage_alloc(bdev, blocks[0] << (blkbits - 9), min_t(int, nr_pages, bio_get_nr_vecs(bdev)), GFP_KERNEL); if (bio == NULL) goto confused; } length = first_hole << blkbits; if (bio_add_page(bio, page, length, 0) < length) { bio = mpage_bio_submit(READ, bio); goto alloc_new; } relative_block = block_in_file - *first_logical_block; nblocks = map_bh->b_size >> blkbits; if ((buffer_boundary(map_bh) && relative_block == nblocks) || (first_hole != blocks_per_page)) bio = mpage_bio_submit(READ, bio); else *last_block_in_bio = blocks[blocks_per_page - 1]; out: return bio; confused: if (bio) bio = mpage_bio_submit(READ, bio); if (!PageUptodate(page)) block_read_full_page(page, get_block); else unlock_page(page); goto out; }
转载请注明原文地址: https://www.6miu.com/read-77388.html

最新回复(0)