VERSION:linux 2.6.35
/*from和to分别指定页面内将要操作的段的起点和终点 */
static int __block_prepare_write(struct inode *inode, struct page *page,
unsigned from, unsigned to, get_block_t *get_block)
unsigned block_start, block_end;
int err = 0;
struct buffer_head *bh, *head, *wait[2], **wait_bh=wait;
BUG_ON(!PageLocked(page));
BUG_ON(from > PAGE_CACHE_SIZE);
BUG_ON(to > PAGE_CACHE_SIZE);
/*块大小,也即页内缓冲区大小*/
blocksize = 1 << inode->i_blkbits;
/*
*调用create_empty_buffers为该页建立缓冲区队列,然后对队列进行初始化。
*没有涉及bh的state标志,调用的create_buffers设置bh->state=0
*且把新分配的缓冲区放入BUF_CLEAN链表(BH_dirty标志为0)
*缓冲区头赋给page->buffer
*/
if (!page_has_buffers(page))
create_empty_buffers(page, blocksize, 0);
head = page_buffers(page);
/*以bit表示的块缓冲区大小,如1K大小的块缓冲区,用10bit表示*/
/*当前页的逻辑块号(相对文件起始位置)*/
block = (sector_t)page->index << (PAGE_CACHE_SHIFT - bbits);
/*
*对当前页的每个块缓冲区对应的bh和受写影响的每个bh
*block_start记录循环写入的总的块大小
*/
for(bh = head, block_start = 0; bh != head || !block_start;
block++, block_start=block_end, bh = bh->b_this_page) {
/*累计写入块大小*/
block_end = block_start + blocksize;
/*页内不在from~to之间的区域*/
if (block_end <= from || block_start >= to) {
if (PageUptodate(page)) {
if (!buffer_uptodate(bh))
}
continue;
}
/*对页内form~to之间的区域,进行下列转换、检查或设置*/
/*清BH_New标志(如果相应的文件块正好被分配但从未被访问)*/
if (buffer_new(bh))
/*
*检查BH_Mapped标志,未设置时,
*调用get_block完成从文件逻辑块号到磁盘逻辑块号的转换,
*磁盘逻辑块号存放在bh->b_blocknr字段,且设置BH_Mapped标志
*/
if (!buffer_mapped(bh)) {
WARN_ON(bh->b_size != blocksize);
err = get_block(inode, block, bh, 1);
if (err)
break;
/*
*检查BH_New标志,如果被设置,
*调用unmap_underlying_metadata来检查页高速缓存的某个块设备缓冲区页是否包含指向磁盘同一块的缓冲区,如果找到一块,BH_Dirty清0并等待,直到该缓冲区的I/O数据传输完毕。
*/
if (buffer_new(bh)) {
unmap_underlying_metadata(bh->b_bdev,
if (PageUptodate(page)) {
continue;
}
if (block_end > to || block_start < from)
block_start, from);
continue;
}
}
/*
如果page的读操作完成,PG_uptodate标志被设置,则将其缓冲区的BH_uptodate也设置
*/
if (PageUptodate(page)) {
if (!buffer_uptodate(bh))
continue;
}
/*
*如果不对整个缓冲区进行重写,且它的BH_Delay和BH_Uptodate标志未置位(即块缓冲区没有有效数据的影响),调用ll_rw_block函数从磁盘读入块的内容。Ll_rw_block函数中定义了I/O完成后的处理函数end_buffer_io_sync
*/
if (!buffer_uptodate(bh) && !buffer_delay(bh) &&
!buffer_unwritten(bh) &&
(block_start < from || block_end > to)) {
ll_rw_block(READ, 1, &bh);
}
}
/*
*阻塞当前进程,直到for循环中的ll_rw_block读操作全部完成
*/
wait_on_buffer(*--wait_bh);
if (!buffer_uptodate(*wait_bh))
}
page_zero_new_buffers(page, from, to);
return err;
}
本文详细解析了Linux文件系统中block_prepare_write函数的工作原理,包括如何为指定页面建立缓冲区队列、如何处理块缓冲区的更新及检查块设备的映射情况。
1602

被折叠的 条评论
为什么被折叠?



