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

generic_file_read()


Rev.2を表示中。最新版はこちら

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)

表2 __generic_file_aio_read()の引数
引数
意味
*iocb 非同期I/Oを管理する構造体
generic_file_read()から呼び出される場合は同期型のkiocbが渡される。
*iov
Read先バッファを示すI/Oベクタ
nr_segs
I/Oベクタのエントリ数
*ppos
読み込み開始オフセット


__generic_file_aio_read()の処理概要
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)


表3 do_generic_file_read()の引数
引数
意味
*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)

表4 do_generic_mapping_read()の引数
引数
意味
*mapping
ファイルのアドレス空間(*2)
*_ra
ファイルの先読み(readahead)管理用データ
*filp
読み込み対象ファイル
*ppos
読み込み開始オフセット
*desc
Read Descriptor
actor
Read完了時のハンドラ
(*2) 各々のファイルは先頭から0ページ目、1ページ目、、、というように物理ページサイズ単位に区切られてディスクキャッシュされる(PageCache)。mappingはファイルのディスクキャッシュを管理するためのもの。詳細はPageCache参照。


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 System
PageCache


最終更新 2007/02/06 20:39:06 - kztomita
(2007/02/06 20:36:32 作成)
添付ファイル
generic_file_read.png - kztomita


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