Read関連
Rev.1を表示中。最新版はこちら。
--------------------------------- VirtualFileSystem sys_read(fd, buf, count) fdからfile構造体を取得 vfs_read() file->f_op->read() - ext2ならgeneric_file_read() --------------------------------- FileSystem - Ext2 Page読み込み関数 ext2_readpage() mpage_readpage(page, ext2_get_block) (inode,オフセット(Block数)) -> ブロック番号変換ルーチン ext2_get_block() - bhにBlock番号を設定して返す ext2_block_to_path() ファイル先頭からのオフセット(Block#)から i_block[]のどこをたどればよいかをoffsetsに格納する。 reread: ext2_get_branch() offsetsが指しているi_block[]に格納されているBlock番号をchainに格納していく。 Indirectアクセス(オフセットBlock数が12以上)なら sb_bread()でi_block[]のあるBlockを読み込んでたどっていく。 <--内部はbread()。デバイスの指定Blockの読み込み。 BufferCacheにCacheされる。(既にCacheにあればディスクアクセスはしない) chain[]の最後のエントリに格納されたBlock番号が取得したかったBlock番号 if (!partial) { <-- Blockが見つかった got_it: map_bh() BH_Mappedセット bh->b_bdev = sb->s_bdev; bh->b_blocknr = block; : goto cleanup } if (!create || err == -EIO) { <-- Blockが見つからなかった(新規割り当ても不要) or エラー cleanup: brelse()して終了 } if (err == -EAGAIN) goto changed - やり直し 以下、新規割り当て処理 ext2_find_goal() ext2_alloc_branch() ext2_splice_branch() BH_Newをセット goto got_it changed: 読みかけのバッファをbrelse()してgoto rereadでやり直し。 struct file_operations struct inode_operations struct address_space_operations --------------------------------- generic_file_read() init_sync_kiocb() __generic_file_aio_read() : do_generic_file_read() do_generic_mapping_read(filp->f_mapping,...) <--Pageキャッシュから読み込み リターンが-EIOCBQUEUEなら wait_on_sync_kioc() <--読み込みまち? do_generic_mapping_read() page_cache_readahead() find_page: find_get_page(mapping,index)でPegaCache検索 PageCacheがなければno_cached_pageへ PageCacheがあってもUptodate状態でなければpage_not_up_to_dateへ page_ok: actor() - (file_read_actor()) Pageのデータをユーザバッファにコピーする break; page_not_up_to_date: lock_page() - PG_lockセット(I/O完了時にunlock) : readpage: mapping->a_ops->readpage() - (ext2ならext2_readpage()) Pageへの読み込み処理 読み込んだPageがUptodate状態ならpage_okへ wait_on_page_locked() - PG_lockされていたらBlock(Read I/O完了待ち) 読み込んだPageがUptodate状態ならpage_okへ UptodateでなければEIO no_cached_page: PageCacheを割り当ててLRUCacheに登録する readpageへ mpage.c <--複数PageのI/O処理用? mpage_readpage(page, get_block) do_mpage_readpage() mpage_bio_submit() <-- do_mpage_readpage()でbioが返されればここで処理 do_mpage_readpage() - mpage_readpage(), mpage_readpages()からコール Page内のBufferについて以下を実行 (Page -> Blockへの分解) get_block(inode, block_in_file, &bh, 0) - (ext2ならext2_get_block()) (Readなのでcreate=0) inodeでファイルを指定。 block_in_fileでファイル先頭からのオフセット(Block#)を指定。 デバイス内のBlock番号に変換する bh->b_bdevにデバイス、bh->b_blocknrにBlock#が返される。 BH_Mappedがセットされていなければ、Blockの場所を保存して 次のBufferへ BH_Mappedが立っているということは、 BufferがDiskにマップされている(bh->b_bdev,bh->b_blocknrが 設定されている)ということ。 Pageの途中でEOFになっているとここでUnmappedが見つかる : if (BufferのBlock#が非連続になった) goto confused if (Page内のBufferでMapされていないものがある) { Page内の最初のUnmapBuffer以降を0クリア if (Page内の最初のBlockがMapされていない) { SetPageUptodate() goto out } } else { SetPageMappedToDisk(page) - page->flagsにPG_mappedtodiskを設定 } if (bio && (*last_block_in_bio != blocks[0] - 1)) bio = mpage_bio_submit(READ, bio); mpage_readpages()からの繰り返し呼出用の処理? 前回の呼出からBlock番号が非連続ならここでI/O要求をだしているみたい alloc_new: - ここに来たということはPageはDisk上で 連続Blockだった(物理的に連続セクタでもある) mpage_alloc() - BIOの割り当て bio->bi_sectorにPageの先頭セクタ#を設定する bio_add_page() - biovecにPageアクセスのエントリを追加 if (buffer_boundary(&bh) || FullMapされていない) mpage_bio_submit() else *last_block_in_bio = blocks[blocks_per_page - 1]; (Pageの最後のBufferのBlock番号) out: return bio confused: if (bio) bio = mpage_bio_submit(READ, bio); if (PageがUptodate状態でない) block_read_full_page(page, get_block); Buffer単位にsubmit_io()する else unlock_page(page); goto out; mpage_bio_submit(rw, bio) bio->bi_end_io = mpage_end_io_read IO終了時のハンドラ設定 submit_bio() 返り値はstruct *bioだが本関数は常にNULLリターン mpage_end_io_read() Bufferに該当するページをUptodate状態にする unlock_page() - PG_lockクリア。wait_on_page_locked()でsleepしたプロセスの起床 bio_put(bio); --------------------------------- bread(bdev, block, size) - 指定BlockデバイスからBlockを読み込み __getblk() - Bufferを検索。なければ作成して返す。 __find_get_block() - BufferをPageCacheから検索 lookup_bh_lru() - Buffer LRUリストから指定BHを高速検索 if (見つからなかった) { __find_get_block_slow()でPageCacheを地道に検索 検索して見つかればBuffer LRUリストに追加 } touch_buffer(bh) - Bufferに対応するPageがアクセスされたことを示す フラグを立てる PageCacheの廃棄処理用 if (Bufferが見つからなかった) __getblk_slow() for (;;) { __find_get_block() <-- また呼ばれるが??? if (Bufferが見つかった) Bufferを返す <-- 割り込まれてない限りは見つからないはず grow_buffers() - 指定デバイス用に新規Bufferの作成 実際にはPage単位で作成 作成失敗ならメモリ解放(free_more_memory()) } if (BufferがUptodateでない) { __bread_slow() bh->b_end_io = end_buffer_read_sync submit_bh(READ, bh); - デバイスにI/O要求発行 bio作成 submit_bio(); <-- 結局はここをコール wait_on_buffer(bh); - I/O完了待ち Bufferのwaitqにcurrentを挿入 (wakeup用関数はbh_wake_function()を指定) io_schedule() - schedule()実行。Read完了までBlock。 finish_wait() } ここに来たと言うことはUptodate状態なのでBufferを返すだけ end_buffer_read_sync() BufferをUptodate状態にする unlock_buffer() - Unlock&バッファ待ちプロセスの起動 wake_up_buffer() - Bufferのwaitqにあるプロセスを起動 __wakeup() bh_wake_function() <-- waitq挿入時に指定されるルーチン autoremove_wake_function() default_wake_function() try_to_wake_up() block_read_full_page(page, get_block) - 汎用ページ読み込み関数 @@@@@@@