generic_file_read()
Rev.1を表示中。最新版はこちら。
1. 概要
ファイルシステム上のファイルをReadする汎用ルーチンとしてgeneric_file_read()がある。
generic_file_read()は、、、
- Virtual File Systemのレイヤから呼び出されファイルを読み込む。(*1)
- ext2,ext3などのほとんどのファイルシステムで使用される。(NFSでは使用されない)
- PageCache(ディスクキャッシュ)を介してReadを行う
- PageCacheがなかった場合は、各ファイルシステムのReadルーチンを呼び出してReadを行う。
(*1) 各ファイルシステムのstruct file_operationsのreadに本関数が登録されており、Virtual File Systemからfile->f_op->read()のようにして呼び出される。
VFS,PageCache,各FileSystemとの関係を図1に示す。
図1 generic_file_readの位置づけ
2. 処理概要
2.1 generic_file_read()
ファイルリード処理の大元の関数。
generic_file_read(struct file *filp, char __user *buf, size_t count, loff_t *ppos)
表1 generic_file_read()の引数
引数 |
意味 |
---|---|
*filp |
読み込み対象ファイル |
*buf |
Readデータを格納するバッファ |
count |
バッファサイズ |
*ppos |
読み込み開始オフセット |
generic_file_read()の処理概要
/* Read先バッファのI/Oベクタ作成 */
local_iov = { .iov_base = buf, .iov_len = count };
init_sync_kiocb(&kiocb, filp);
ret = __generic_file_aio_read(&kiocb, &local_iov, 1, ppos);
if (-EIOCBQUEUED == ret)
ret = wait_on_sync_kiocb(&kiocb);
return ret;
2.2 __generic_file_aio_read()
__generic_file_aio_read(struct kiocb *iocb, const struct iovec *iov, unsigned long nr_segs, loff_t *ppos)
引数 |
意味 |
---|---|
*iocb |
非同期I/Oを管理する構造体 generic_file_read()から呼び出される場合は同期型のkiocbが渡される。 |
*iov |
Read先バッファを示すI/Oベクタ |
nr_segs |
I/Oベクタのエントリ数 |
*ppos |
読み込み開始オフセット |
for (seg = 0; seg < nr_segs; seg++) { /* iovの中身のチェック */ } if (filp->f_flags & O_DIRECT) { /* O_DIRECT付きでopenしたファイルのRead処理 */ : } if (count) { for (seg = 0; seg < nr_segs; seg++) { /* I/OベクタからReadディスクリプタ作成 */ desc.written = 0; desc.arg.buf = iov[seg].iov_base; /* Readデータ格納アドレス */ desc.count = iov[seg].iov_len; /* ReadBufferサイズ */ : do_generic_file_read() } }
2.3 do_generic_file_read()
マクロ。filpからmappingと_raを取り出してdo_generic_mapping_read()を呼び出すだけ。
void do_generic_file_read(struct file * filp, loff_t *ppos, read_descriptor_t * desc, read_actor_t actor)
引数 |
意味 |
---|---|
*filp |
読み込み対象ファイル |
*ppos |
読み込み開始オフセット |
*desc |
Read Descriptor ReadBufferのどこにデータを書き込むか、何Byte残っているかを管理する。 |
actor |
Read完了時のハンドラ。 このルーチンでReadデータをKernel→User空間へコピーして、Read Descritorを更新する。 |
2.4 do_generic_mapping_read()
PageCacheからページを探し、最新版(Uptodate状態)のページがあった場合はそれを返す。PageCacheが最新状態でなければ、ファイルシステムのReadルーチンを呼び出してReadを行う(*1)。
(*1) mapping->a_ops->readpage()で対応するファイルシステムのReadルーチンを呼び出す。
void do_generic_mapping_read(struct address_space *mapping, struct file_ra_state *_ra, struct file *filp, loff_t *ppos, read_descriptor_t *desc, read_actor_t actor)
引数 |
意味 |
---|---|
*mapping |
ファイルのアドレス空間(*2) |
*_ra |
ファイルの先読み(readahead)管理用データ |
*filp |
読み込み対象ファイル |
*ppos |
読み込み開始オフセット |
*desc |
Read Descriptor |
actor |
Read完了時のハンドラ |
do_generic_mapping_read()の処理概要
/* ファイルのi-node */ inode = mapping->host; /* Read開始位置をファイル先頭からのページ数に変換 */ index = *ppos >> PAGE_CACHE_SHIFT; /* ページ内でのオフセット */ offset = *ppos & ~PAGE_CACHE_MASK; /* ファイルサイズ */ isize = i_size_read(inode); /* ファイルの最後のページのインデックス */ end_index = (isize - 1) >> PAGE_CACHE_SHIFT; /* 1ページずつ読み込んでいく */ for (;;) { nr = PAGE_CACHE_SIZE; if (index >= end_index) { /* 最後のページに到達した */ if (index > end_index) goto out; /* nr:1〜PAGE_CACHE_SIZE */ nr = ((isize - 1) & ~PAGE_CACHE_MASK) + 1; if (nr <= offset) { goto out; } } nr = nr - offset; if (index == next_index) next_index = page_cache_readahead(mapping, &ra, filp, index, last_index - index); find_page: /* PageCacheからページを取得 */ page = find_get_page(mapping, index); if (unlikely(page == NULL)) { /* 指定ページがPageCacheになかった。 * no_cached_pageへ飛んでPageCacheを作成する。 */ handle_ra_miss(mapping, &ra, index); goto no_cached_page; } if (!PageUptodate(page)) goto page_not_up_to_date; /* ページはあったが最新でない */ /* 取得したページキャッシュが最新状態 */ page_ok: /* 最新データ格納されたページが手に入った時の処理。 * (最新のPageCacheが見つかった or Readした) */ : /* actorは引数で指定される。 * generic_file_read()から呼び出される場合は、 * actorはfile_read_actor()となる。 * この関数はページの内容をユーザ空間にコピーする。 */ ret = actor(desc, page, offset, nr); /* 読み込んだ分だけindex,offsetを進める */ offset += ret; index += offset >> PAGE_CACHE_SHIFT; offset &= ~PAGE_CACHE_MASK; /* actorが正常終了してまだReadするデータが * 残っているのなら続ける */ if (ret == nr && desc->count) continue; goto out; page_not_up_to_date: /* 見つかったページの内容が最新(Uptodate状態) * でなかった場合の処理 */ : /* 別コンテキストでReadされている可能性もあるので * 再度、PageCacheが最新状態かチェック */ if (PageUptodate(page)) { goto page_ok; } /* それでもUptodate状態でなければ、以下のRead処理へ */ readpage: /* PageCacheへのRead処理を行う。 */ /* 該当FileSystemのRead処理を呼び出す。 * mapping->a_opsには対応するFileSystemの * address_space_operationsが登録されている。 * 例えばext3上のファイルならext3_ordered_aops * が登録されておりext3_readpage()を呼び出すことになる。 */ error = mapping->a_ops->readpage(filp, page); : if (!PageUptodate(page)) { } : : goto page_ok; readpage_error: /* Readエラー */ desc->error = error; goto out; no_cached_page: /* PageCacheがなかった時の処理 * ページを割り当ててPageCacheに登録して * Read処理へ飛ぶ。 */ /* PageCache用のページを新規に取得 */ if (!cached_page) { cached_page = page_cache_alloc_cold(mapping); : } /* PageCacheに登録 */ error = add_to_page_cache_lru(cached_page, mapping, index, GFP_KERNEL); : /* Read処理へ */ goto readpage; } out: /* Readしたところまでオフセットを進める */ *ppos = ((loff_t) index << PAGE_CACHE_SHIFT) + offset;
関連ページ
Virtual File SystemPageCache