block_prepare_write()
1. 概要
ファイルのWrite時は、ページキャッシュに対してprepare_writeで書き込みの準備をした後、commit_writeするように手順がわかれている。prepare_write処理のなかで使われるblock_prepare_write()の解説。
2. block_prepare_write()が行うこと
block_prepare_write()は、これからWriteをしようとするページキャッシュのページを引数に受け取り、Bufferの準備処理を行う。主な処理は以下のとおり。
2.1 Bufferの割り当て
渡されたページに、まだBufferが存在しない場合は、create_empty_buffers()でBufferを割り当てる。ページにBufferを割り当てると図1に示すような状態になる。create_empty_buffers()で割り当てられたBufferはb_bdev,b_blocknrなどはまだ設定されていない。Bufferのディスクブロックへのマップはこの後行われる。図1 Bufferの確保
2.2 Bufferのディスクブロックへのマップ
2.1の処理により割り当てられたページの各バッファ(struct buffer_head)に対して、まだディスクブロックにマップされていないものはマップ処理を行う。
「ディスクブロックにマップされている」とはstruct buffer_headのb_bdev(デバイス構造体へのポインタ),b_blocknr(デバイス内のブロック番号)が設定され、BH_Mappedフラグがセットされている状態のこと。これはBufferのデータがディスク上のどのブロックに対応しているか確定していることを意味する(*1)。Bufferがマップされているかどうかは、buffer_mapped()で調べることができる。
2.1でcreate_empty_buffers()でからBufferを作成されていた場合は、まだディスクへのマップは設定されていないのでここでマップを設定される。マップ処理はget_block()(*1)によって行われる。get_block()は以下のような動作をする。
- Writeしようとしている領域のデータが既にディスク上に存在するのならget_block()はマップ済みのブロックデバイス(b_bdev)、ブロック番号(b_blocknr)をbuffer_headに設定して返す。
- ファイルへの新規書込みなどで、Writeしようとしている領域のデータがまだディスク上に存在しない場合、get_block()の中でブロックの割り当て&マップ処理が行われる。(*2)
(*1) get_blockは引数で渡される関数ポインタなので、実際にこの名前の関数は存在しない。実際には各ファイルシステム毎のget_block関数が呼び出される(例えばext2_get_block())。
(*2) get_block()をcreate=1にして呼び出しているため。
(*3)書き込むべきディスクブロックが確定している状態。
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がディスクブロックにマップされていない場合 */ /* create_empty_buffers()でBufferが作られた場合も * まだマップ情報が設定されていないのでこちらに来る */ /* get_block()でbhにマップ情報を設定する */ /* inodeファイルのblockブロックがディスク上の * どこにあるのかを調べてbh(.b_bdev,b_blocknr)に * 設定して返す。 * まだ、ディスク上にデータがない場合は、create=1で * 呼び出しているので、新しいブロックを割り当てて返す。*/ 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; } } : :