block_prepare_write()
Rev.10を表示中。最新版はこちら。
1. 概要
ファイルのWrite時は、ページキャッシュに対してprepare_writeで書き込みの準備をした後、commit_writeするように手順がわかれている。prepare_write処理のなかで使われるblock_prepare_write()の解説。
2. block_prepare_write()が行うこと
block_prepare_write()は、これからWriteをしようとするページキャッシュのページを引数に受け取り、Bufferの準備処理を行う。主な処理は以下のとおり。
2.1 Bufferの割り当て
渡されたページに、まだBufferが存在しない場合は、ここでBufferを割り当てる。ページにBufferを割り当てると図1に示すような状態になる。図1 Bufferの確保
2.2 Bufferのディスクブロックへのマップ
2.1の処理により割り当てられたページの各バッファ(struct buffer_head)に対して、まだディスクブロックにマップされていないものはマップ処理を行う。
「ディスクブロックにマップされている」とはstruct buffer_headのb_bdev(デバイス構造体へのポインタ),b_blocknr(デバイス内のブロック番号)が設定され、BH_Mappedフラグがセットされている状態のこと。これはBufferのデータがディスク上のどのブロックに対応しているか確定していることを意味する(*1)。
このマップ処理はget_block()(*2)のcreate引数を1にして呼び出すことで行われる。get_block()の挙動の概要は以下のとおり。
- Writeしようとしている領域のデータが既にディスク上に存在するのならget_block()はマップ済みのブロックデバイス(b_bdev)、ブロック番号(b_blocknr)をbuffer_headに設定して返す。
- ファイルサイズが拡張されるなどして、まだ、ディスクブロックが割り当てられていない(マップされていない)場合は、get_block()の中でブロックの割り当て&マップ処理が行われる。
(*1) __block_prepare_write()の中でBufferがマップされているか否かをbuffer_mapped()で判断している。この段階でディスクブロックが確定していないというわけではない。XXXXX 編集中。
(*2) get_blockは引数で渡される関数ポインタなので、実際にこの名前の関数は存在しない。実際には各ファイルシステム毎のget_block関数が呼び出される(例えばext2_get_block())。
3. 処理の流れ
編集中from - ページ内の書き込み(Prepare)開始位置(Byte) to - ページ内の書き込み(Prepare)終了位置(Byte) : blocksize = 1 << inode->i_blkbits; /* ページにBufferがなかったら作成する(図1の状態にする) */ if (!page_has_buffers(page)) create_empty_buffers(page, blocksize, 0); head = page_buffers(page); : /* 各Bufferをループしてなめる */ 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; if (block_end <= from || block_start >= to) { /* このBufferはPrepare対象領域外なのでスキップ */ /* ページがUptodate状態なら * 各BufferもUptodate状態にする */ if (PageUptodate(page)) { if (!buffer_uptodate(bh)) set_buffer_uptodate(bh); } continue; } /* BH_Newをクリア */ if (buffer_new(bh)) clear_buffer_new(bh); if (!buffer_mapped(bh)) { /* Bufferがディスクブロックにマップされていない場合 */ err = get_block(inode, block, bh, 1); if (err) break; if (buffer_new(bh)) { /* get_block()で新規にディスクブロックのマップが * 作成された場合は */ : } continue; } if (!buffer_uptodate(bh) && !buffer_delay(bh) && (block_start < from || block_end > to)) { ll_rw_block(READ, 1, &bh); *wait_bh++=bh; } }