Linux Kernel(2.6)の実装に関するメモ書き

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) - 汎用ページ読み込み関数
@@@@@@@

最終更新 2006/03/27 14:02:27 - kztomita
(2006/03/27 14:02:27 作成)


リンク
最近更新したページ
検索