从上一篇的exfat文件系统(五)中,我们可以了解到exfat文件系统的读写都需要一个重要的函数
exfat_get_block,而对exfat_get_block的功能的作用需要从
mpage_readpage的函数来理解,从学习中,我们可以发现exfat_get_block的主要功能是
检查page上所有的物理块是否连续,然后配合mpage_readpage提交bio操作。以下详细分析学习和研究过程。
在学习
mpage_readpage函数时,发现网络上有一篇文章很好的介绍了该函数的功能。
转摘博客:
http://blog.chinaunix.net/uid-28236237-id-4028521.html
mpage_readpage调用到do_mpage_readpage
do_mpage_readpage详解如下:
/*
* This is the worker routine which does all the work of mapping the disk
* blocks and constructs largest possible bios, submits them for IO if the
* blocks are not contiguous on the disk.
*
* We pass a buffer_head back and forth and use its buffer_mapped() flag to
* represent the validity of its disk mapping and to decide when to do the next
* get_block() call.
*/
/*这个函数试图读取文件中的一个page大小的数据,最理想的情况下就是这个page大小
的数据都是在连续的物理磁盘上面的,然后函数只需要提交一个bio请求就可以获取
所有的数据,这个函数大部分工作在检查page上所有的物理块是否连续,检查的方法
就是调用文件系统提供的get_block函数,如果不连续,需要调用block_read_full_page
函数采用buffer 缓冲区的形式来逐个块获取数据*/
/*
1、调用get_block函数检查page中是不是所有的物理块都连续
2、如果连续调用mpage_bio_submit函数请求整个page的数据
3、如果不连续调用block_read_full_page逐个block读取
*/
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;
if
(page_has_buffers(page))
goto
confused;
/*
block_in_file 本page中的第一个block number
last_block 本page中最后一个block 的大小
last_block_in_file 文件大小求出文件的最后一个block 大小*/
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;
/*last_block 最后等于本次对这个page操作的最后一个block大小*/
if
(last_block > last_block_in_file)
last_block = last_block_in_file;
page_block =
0
;
/*
* Map blocks using the result from the previous get_blocks call first.
*/
nblocks = map_bh->b_size >> blkbits;
/*对于普通情况mpage_readpage调用下,map_bh只是一个临时变量不会走到
下面的分支*/
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;
/*这个循环是比较关键的路径,理解这个函数至关重要
1、page_block从0开始循环,它表示在这个page内的block大小
2、调用get_block 函数查找对应逻辑块的物理块号是多少
3、如果遇到了文件空洞、page上的物理块不连续就会跳转到confused
4、将这个page中每个逻辑块对应的物理块都保存到临时的数组blocks[] 中*/
while
(page_block < blocks_per_page)
{
map_bh->b_state =
0
;
map_bh->b_size =
0
;
//遍历页面中的块数
if
(block_in_file < last_block)
{
map_bh->b_size = (last_block – block_in_file) << blkbits;
//将文件中的块号转换成bh
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 */
/* Contiguous blocks? */
//判断请求的块缓存是不是连续的。如果不连续,就跳转到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;
}
/*如果发现文件中有洞,将整个page清0,因为文件洞的区域
物理层不会真的去磁盘上读取,必须在这里主动清零,否则
文件洞区域内容可能随机*/
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);
}
/*
* This page will go to BIO. Do we need to send this BIO off first?
*/
/*bio 为NULL,目前分析的场景可以跳过去*/
if
(bio && (*last_block_in_bio != blocks[
0
] –
1
))
bio = mpage_bio_submit(READ, bio);
alloc_new:
if
(bio == NULL)
{
/*重新分配一个bio结构体
blocks[0] << (blkbits – 9) 这个是page中第一个逻辑块的物理块号,
转换成物理扇区号*/
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:
/*一切顺利,整个page中的物理块是相连的,返回一个bio*/
return
bio;
confused:
if
(bio)
bio = mpage_bio_submit(READ, bio);
/*page 中的物理块不相连,没有办法一个一个buffer去读取出来
*/
if
(!PageUptodate(page))
block_read_full_page(page, get_block);
else
unlock_page(page);
goto
out;
}
网络上找到一个介绍缓存的说明如下:
linux在读写文件的时候会先将文件逻辑映射到页面逻辑,然后会在页高速缓存中寻找文件的数据,如果找到,那么对于读操作,那么就将数据直接返回给用户,如果对于缓冲写操作,那么会将数据写到这个页高速缓存中的页面,如果没有在页高速缓存找到页面,那么会分配一个页面加入到页高速缓存,然后着手IO操作,即使不是没有在页高速缓存找到页面而是找到的页面不是uptodate的页面,那么也要着手IO,怎么IO呢,这就是涉及到2.6内核的一个新特性的新实现,这就是mpage,其实就是页面的io操作,具体实现就要用到bio结构了,vfs子系统会在页高速缓存的需要io的页面上循环进行操作,怎么操作呢,就是将一个页面分割为多个“逻辑块”,然后构造一个bio,将一个页面加入到这个bio,这样循环中的每一个页面都会加入到这个bio,最后,统一用mpage_bio_submit来提交io操作
exfat_get_block就是现实“
linux在读写文件的时候会先将文件逻辑映射到页面逻辑,然后会在页高速缓存中寻找文件的数据,如果找到,那么对于读操作,那么就将数据直接返回给用户,如果对于缓冲写操作,那么会将数据写到这个页高速缓存中的页面,如果没有在页高速缓存找到页面,那么会分配一个页面加入到页高速缓存
”的操作(简单的就是说,该函数将相对于文件起始的块号转换为文件系统的逻辑块号)
。这样继续看exfat_get_block的代码就比较明白了。
exfat_get_block的详细流程图如下:
在学习exfat_get_block的代码中,我采用测试程序读方式来跟踪log,简单的读数据时,ffsMapCluster的debug log如下:
enter [ffsMapCluster] fid->flags = 0x 3 clu_offset = 0, num_clusters = 1 now *clu = 3723
进入文件管家,播放一段视频(其中有暂停,快进和循环播放操作),log(部分)如下:
<6>[ 2534.253607] enter [ffsMapCluster] fid->flags = 0x 3 clu_offset = 0, num_clusters = 1 now *clu = 3723
<6>[ 2729.367136] enter [ffsMapCluster] fid->flags = 0x 3 clu_offset = 0, num_clusters = 652 now *clu = 3071
<6>[ 2729.972564] enter [ffsMapCluster] fid->flags = 0x 3 clu_offset = 0, num_clusters = 652 now *clu = 3071
<6>[ 2729.972739] enter [ffsMapCluster] fid->flags = 0x 3 clu_offset = 0, num_clusters = 652 now *clu = 3071
<6>[ 2729.972773] enter [ffsMapCluster] fid->flags = 0x 3 clu_offset = 1, num_clusters = 652 now *clu = 3071
<6>[ 2736.333772] enter [ffsMapCluster] fid->flags = 0x 3 clu_offset = 0, num_clusters = 312 now *clu = 62722
<6>[ 2736.343226] enter [ffsMapCluster] fid->flags = 0x 3 clu_offset = 0, num_clusters = 312 now *clu = 62722
<6>[ 2736.343289] enter [ffsMapCluster] fid->flags = 0x 3 clu_offset = 1, num_clusters = 312 now *clu = 62722
<6>[ 2736.343516] enter [ffsMapCluster] fid->flags = 0x 3 clu_offset = 1, num_clusters = 312 now *clu = 62722
<6>[ 2736.343565] enter [ffsMapCluster] fid->flags = 0x 3 clu_offset = 2, num_clusters = 312 now *clu = 62722
<6>[ 2736.343627] enter [ffsMapCluster] fid->flags = 0x 3 clu_offset = 3, num_clusters = 312 now *clu = 62722
<6>[ 2736.354171] enter [ffsMapCluster] fid->flags = 0x 3 clu_offset = 3, num_clusters = 312 now *clu = 62722
<6>[ 2736.354232] enter [ffsMapCluster] fid->flags = 0x 3 clu_offset = 4, num_clusters = 312 now *clu = 62722
<6>[ 2736.354300] enter [ffsMapCluster] fid->flags = 0x 3 clu_offset = 5, num_clusters = 312 now *clu = 62722
<6>[ 2736.354361] enter [ffsMapCluster] fid->flags = 0x 3 clu_offset = 6, num_clusters = 312 now *clu = 62722
<6>[ 2736.354427] enter [ffsMapCluster] fid->flags = 0x 3 clu_offset = 7, num_clusters = 312 now *clu = 62722
<6>[ 2736.357080] enter [ffsMapCluster] fid->flags = 0x 3 clu_offset = 7, num_clusters = 312 now *clu = 62722
<6>[ 2736.357145] enter [ffsMapCluster] fid->flags = 0x 3 clu_offset = 8, num_clusters = 312 now *clu = 62722
<6>[ 2736.357215] enter [ffsMapCluster] fid->flags = 0x 3 clu_offset = 9, num_clusters = 312 now *clu = 62722
<6>[ 2736.357276] enter [ffsMapCluster] fid->flags = 0x 3 clu_offset = 10, num_clusters = 312 now *clu = 62722
<6>[ 2736.357338] enter [ffsMapCluster] fid->flags = 0x 3 clu_offset = 11, num_clusters = 312 now *clu = 62722
<6>[ 2736.374826] enter [ffsMapCluster] fid->flags = 0x 3 clu_offset = 11, num_clusters = 312 now *clu = 62722
<6>[ 2736.374894] enter [ffsMapCluster] fid->flags = 0x 3 clu_offset = 12, num_clusters = 312 now *clu = 62722
<6>[ 2736.374969] enter [ffsMapCluster] fid->flags = 0x 3 clu_offset = 13, num_clusters = 312 now *clu = 62722
<6>[ 2736.375037] enter [ffsMapCluster] fid->flags = 0x 3 clu_offset = 14, num_clusters = 312 now *clu = 62722
<6>[ 2736.375100] enter [ffsMapCluster] fid->flags = 0x 3 clu_offset = 15, num_clusters = 312 now *clu = 62722
<6>[ 2736.393375] enter [ffsMapCluster] fid->flags = 0x 3 clu_offset = 15, num_clusters = 312 now *clu = 62722
<6>[ 2736.393443] enter [ffsMapCluster] fid->flags = 0x 3 clu_offset = 16, num_clusters = 312 now *clu = 62722
<6>[ 2736.393510] enter [ffsMapCluster] fid->flags = 0x 3 clu_offset = 17, num_clusters = 312 now *clu = 62722
<6>[ 2736.393974] enter [ffsMapCluster] fid->flags = 0x 3 clu_offset = 18, num_clusters = 312 now *clu = 62722
<6>[ 2736.394047] enter [ffsMapCluster] fid->flags = 0x 3 clu_offset = 19, num_clusters = 312 now *clu = 62722
<6>[ 2736.414981] enter [ffsMapCluster] fid->flags = 0x 3 clu_offset = 19, num_clusters = 312 now *clu = 62722
<6>[ 2736.415049] enter [ffsMapCluster] fid->flags = 0x 3 clu_offset = 20, num_clusters = 312 now *clu = 62722
<6>[ 2736.415120] enter [ffsMapCluster] fid->flags = 0x 3 clu_offset = 21, num_clusters = 312 now *clu = 62722
<6>[ 2736.415183] enter [ffsMapCluster] fid->flags = 0x 3 clu_offset = 22, num_clusters = 312 now *clu = 62722
<6>[ 2736.415246] enter [ffsMapCluster] fid->flags = 0x 3 clu_offset = 23, num_clusters = 312 now *clu = 62722
<6>[ 2736.435625] enter [ffsMapCluster] fid->flags = 0x 3 clu_offset = 23, num_clusters = 312 now *clu = 62722
<6>[ 2736.435696] enter [ffsMapCluster] fid->flags = 0x 3 clu_offset = 24, num_clusters = 312 now *clu = 62722
<6>[ 2736.435820] enter [ffsMapCluster] fid->flags = 0x 3 clu_offset = 25, num_clusters = 312 now *clu = 62722
<6>[ 2736.435885] enter [ffsMapCluster] fid->flags = 0x 3 clu_offset = 26, num_clusters = 312 now *clu = 62722
<6>[ 2736.435951] enter [ffsMapCluster] fid->flags = 0x 3 clu_offset = 27, num_clusters = 312 now *clu = 62722
<6>[ 2736.458553] enter [ffsMapCluster] fid->flags = 0x 3 clu_offset = 27, num_clusters = 312 now *clu = 62722
<6>[ 2736.458621] enter [ffsMapCluster] fid->flags = 0x 3 clu_offset = 28, num_clusters = 312 now *clu = 62722
<6>[ 2736.458691] enter [ffsMapCluster] fid->flags = 0x 3 clu_offset = 29, num_clusters = 312 now *clu = 62722
<6>[ 2736.458755] enter [ffsMapCluster] fid->flags = 0x 3 clu_offset = 30, num_clusters = 312 now *clu = 62722
<6>[ 2736.458820] enter [ffsMapCluster] fid->flags = 0x 3 clu_offset = 31, num_clusters = 312 now *clu = 62722
<6>[ 2736.478704] enter [ffsMapCluster] fid->flags = 0x 3 clu_offset = 31, num_clusters = 312 now *clu = 62722
<6>[ 2736.478773] enter [ffsMapCluster] fid->flags = 0x 3 clu_offset = 32, num_clusters = 312 now *clu = 62722
<6>[ 2736.478842] enter [ffsMapCluster] fid->flags = 0x 3 clu_offset = 33, num_clusters = 312 now *clu = 62722
<6>[ 2736.478909] enter [ffsMapCluster] fid->flags = 0x 3 clu_offset = 34, num_clusters = 312 now *clu = 62722
<6>[ 2736.478973] enter [ffsMapCluster] fid->flags = 0x 3 clu_offset = 35, num_clusters = 312 now *clu = 62722
<6>[ 2736.499001] enter [ffsMapCluster] fid->flags = 0x 3 clu_offset = 35, num_clusters = 312 now *clu = 62722
<6>[ 2736.499068] enter [ffsMapCluster] fid->flags = 0x 3 clu_offset = 36, num_clusters = 312 now *clu = 62722
<6>[ 2736.499140] enter [ffsMapCluster] fid->flags = 0x 3 clu_offset = 37, num_clusters = 312 now *clu = 62722
<6>[ 2736.499206] enter [ffsMapCluster] fid->flags = 0x 3 clu_offset = 38, num_clusters = 312 now *clu = 62722
<6>[ 2736.499273] enter [ffsMapCluster] fid->flags = 0x 3 clu_offset = 39, num_clusters = 312 now *clu = 62722
<6>[ 2736.519611] enter [ffsMapCluster] fid->flags = 0x 3 clu_offset = 39, num_clusters = 312 now *clu = 62722
<6>[ 2736.519677] enter [ffsMapCluster] fid->flags = 0x 3 clu_offset = 40, num_clusters = 312 now *clu = 62722
<6>[ 2736.519744] enter [ffsMapCluster] fid->flags = 0x 3 clu_offset = 41, num_clusters = 312 now *clu = 62722
<6>[ 2736.519809] enter [ffsMapCluster] fid->flags = 0x 3 clu_offset = 42, num_clusters = 312 now *clu = 62722
<6>[ 2736.519873] enter [ffsMapCluster] fid->flags = 0x 3 clu_offset = 43, num_clusters = 312 now *clu = 62722
<6>[ 2736.540243] enter [ffsMapCluster] fid->flags = 0x 3 clu_offset = 43, num_clusters = 312 now *clu = 62722
<6>[ 2736.540312] enter [ffsMapCluster] fid->flags = 0x 3 clu_offset = 44, num_clusters = 312 now *clu = 62722
<6>[ 2736.540388] enter [ffsMapCluster] fid->flags = 0x 3 clu_offset = 45, num_clusters = 312 now *clu = 62722
<6>[ 2736.540454] enter [ffsMapCluster] fid->flags = 0x 3 clu_offset = 46, num_clusters = 312 now *clu = 62722
<6>[ 2736.540519] enter [ffsMapCluster] fid->flags = 0x 3 clu_offset = 47, num_clusters = 312 now *clu = 62722
<6>[ 2736.560914] enter [ffsMapCluster] fid->flags = 0x 3 clu_offset = 47, num_clusters = 312 now *clu = 62722
<6>[ 2736.560982] enter [ffsMapCluster] fid->flags = 0x 3 clu_offset = 48, num_clusters = 312 now *clu = 62722
<6>[ 2736.561051] enter [ffsMapCluster] fid->flags = 0x 3 clu_offset = 49, num_clusters = 312 now *clu = 62722
<6>[ 2736.561166] enter [ffsMapCluster] fid->flags = 0x 3 clu_offset = 50, num_clusters = 312 now *clu = 62722
<6>[ 2736.561230] enter [ffsMapCluster] fid->flags = 0x 3 clu_offset = 51, num_clusters = 312 now *clu = 62722
<6>[ 2736.580953] enter [ffsMapCluster] fid->flags = 0x 3 clu_offset = 51, num_clusters = 312 now *clu = 62722
<6>[ 2736.581020] enter [ffsMapCluster] fid->flags = 0x 3 clu_offset = 52, num_clusters = 312 now *clu = 62722
<6>[ 2736.581087] enter [ffsMapCluster] fid->flags = 0x 3 clu_offset = 53, num_clusters = 312 now *clu = 62722
<6>[ 2736.581150] enter [ffsMapCluster] fid->flags = 0x 3 clu_offset = 54, num_clusters = 312 now *clu = 62722
<6>[ 2736.581214] enter [ffsMapCluster] fid->flags = 0x 3 clu_offset = 55, num_clusters = 312 now *clu = 62722
<6>[ 2736.600991] enter [ffsMapCluster] fid->flags = 0x 3 clu_offset = 55, num_clusters = 312 now *clu = 62722
<6>[ 2736.601055] enter [ffsMapCluster] fid->flags = 0x 3 clu_offset = 56, num_clusters = 312 now *clu = 62722
<6>[ 2736.601120] enter [ffsMapCluster] fid->flags = 0x 3 clu_offset = 57, num_clusters = 312 now *clu = 62722
<6>[ 2736.601185] enter [ffsMapCluster] fid->flags = 0x 3 clu_offset = 58, num_clusters = 312 now *clu = 62722
<6>[ 2736.601245] enter [ffsMapCluster] fid->flags = 0x 3 clu_offset = 59, num_clusters = 312 now *clu = 62722
<6>[ 2736.620800] enter [ffsMapCluster] fid->flags = 0x 3 clu_offset = 59, num_clusters = 312 now *clu = 62722
<6>[ 2736.620867] enter [ffsMapCluster] fid->flags = 0x 3 clu_offset = 60, num_clusters = 312 now *clu = 62722
<6>[ 2736.620989] enter [ffsMapCluster] fid->flags = 0x 3 clu_offset = 61, num_clusters = 312 now *clu = 62722
<6>[ 2736.621053] enter [ffsMapCluster] fid->flags = 0x 3 clu_offset = 62, num_clusters = 312 now *clu = 62722
<6>[ 2736.621121] enter [ffsMapCluster] fid->flags = 0x 3 clu_offset = 63, num_clusters = 312 now *clu = 62722
<6>[ 2736.641751] enter [ffsMapCluster] fid->flags = 0x 3 clu_offset = 63, num_clusters = 312 now *clu = 62722
<6>[ 2736.641824] enter [ffsMapCluster] fid->flags = 0x 3 clu_offset = 64, num_clusters = 312 now *clu = 62722
<6>[ 2736.641894] enter [ffsMapCluster] fid->flags = 0x 3 clu_offset = 65, num_clusters = 312 now *clu = 62722
<6>[ 2736.641958] enter [ffsMapCluster] fid->flags = 0x 3 clu_offset = 66, num_clusters = 312 now *clu = 62722
<6>[ 2736.642032] enter [ffsMapCluster] fid->flags = 0x 3 clu_offset = 67, num_clusters = 312 now *clu = 62722
到此为止,基本理解exfat读过程。