📂

Linuxカーネルシステムコール解説【Open編】

に公開

はじめに

こんにちは。Linuxカーネル研究のために無職になった人です。
今、以下のようなLinuxカーネルを解説するサイトを作っているのですが、ちょっと説明がわかりづらい気もしたので、追加説明という意味で記事を書きました。

https://linux.tokyo

今後は linux.tokyo に登録するのみならず、1つ1つの linux.tokyo で登録した内容を記事の形で説明していこうと思っています。
そこで、今回は「Open」システムコールから始めようと思います。ちょっと最初の方にやった「Write」とか「Exec」は内容的に未熟である可能性があり、心配なので...
ちょっと記事にlinux.tokyoへのリンクが多くなってしまっているのですが、ご容赦ください。

また、何か間違いがありましたらお気軽にコメントをくださいmm

Openシステムコールの内容

1. do_sys_open

エントリーポイントになる関数。

https://linux.tokyo/trees/open_1_do_open__do_sys_open_to_do_dentry_open

do_sys_openの中身の構造
do_sys_open
    get_unused_fd_flags
        alloc_fd
            expand_file ⭐︎1
            __set_open_fd ⭐︎2
    do_filp_open
        init_file ⭐︎3
        path_init ⭐︎4
        link_path_walk
            lookup_fast ⭐︎5
            lookup_slow ⭐︎6
        do_dentry_open ⭐︎7
            f->f_op->open ⭐︎8
        set_nameidata
            __set_nameidata ⭐︎9
    fd_install
        files_fdtables ⭐︎10

◾️大枠の流れ

大枠では以下の流れになります。

一:get_unused_fd_flags
未使用の FD 番号を確保。リンク
二:do_filp_open
struct fileの作成。リンク
三:fd_install
確保済み fd と生成された struct file をプロセスのファイルディスクリプタテーブルに結び付け、のちに参照可能にする。リンク

↓ fd確保

1:get_unused_fd_flags > alloc_fd > expand_file
一でfdが足りない場合に、fdtableを動的に拡張し、より多く確保できるようにする。リンク
2:get_unused_fd_flags > alloc_fd > __set_open_fd
一で最終的に「このFDを占有する」と言う状態にする。リンク

↓ struct file確保

3:do_filp_open > init_file
struct fileを初期化する。リンク

↓ パス確保

4:do_filp_open > path_init
起点パス (cwd か root 等) を決定してパス走査の準備を整える。リンク
5:do_filp_open > link_path_walk > lookup_fast
高速でパス解決を最短経路で完了させる試みを行う。リンク
6:do_filp_open > link_path_walk > lookup_slow
lookup_fastでキャッシュ未ヒット時に、パス解決を再度行う。リンク

↓ block層確保

7:do_filp_open > do_dentry_open
struct fileへの情報追加と、block層の確保の入り口。リンク
8:do_filp_open > do_dentry_open > f->f_op->open
ファイルシステム固有のopenハンドラを呼び出し、デバイスやファイルシステム特有の初期化処理を実行する。ext4_file_open等の実装が呼ばれる。リンク

↓ nameidataの確保

9:do_filp_open > set_nameidata > __set_nameidata
nameidata構造体にデータを入れ、名前解決のコンテキストを初期化する。リンク

↓ 最終的な紐付け

10:fd_install > fd_install
確保済み fd と生成された struct file をプロセスのファイルディスクリプタテーブルに結び付け、のちに参照可能にする。リンク

2. ext4_file_open

上記のdo_sys_openの8のf->f_op->openの中身の実装を説明します。

ext4_file_openの中身の構造
ext4_file_open
    ext4_inode_attach_jinode
        jbd2_alloc_inode
        jbd2_journal_init_jbd_inode

◾️実装の流れ

以下の流れになります。

1:jbd2_alloc_inode
ジャーナリング層(JBD2)用のjbd2_inode構造体をメモリ確保し取得する。リンク
2:jbd2_journal_init_jbd_inode
確保したjbd2_inodeとVFS inodeを結び付け、ジャーナルに対して当該inodeを登録する初期化処理。リンク

3. ext4_lookup

上記のdo_sys_openの lookup_slow で lookupをしているが、そのlookupがext4などのファイルシステム依存なので、ここでそのlookupを説明する。

ext4_lookupの中身の構造
ext4_lookup
    ext4_find_inline_entry
        submit_bio ⭐︎1
        ext4_match ⭐︎2
    ext4_dx_find_entry
        ext4_map_blocks ⭐︎3
        find_get_block_common ⭐︎4
        ext4_match ⭐︎5

◾️大枠の流れ

大枠、以下の流れになります。

一:ext4_find_inline_entry
小規模ディレクトリの検索で使われる検索方法。リンク
二:ext4_dx_find_entry
ハッシュインデックス(dx)を有効にしているときに、B-tree 風のインデックスを用いてエントリを高速検索する検索方法。リンク

↓ inline検索

1:ext4_find_inline_entry > submit_bio
準備した BIO をブロック層へ送出し、ディスク読み取りを開始する。リンク
2:ext4_find_inline_entry > ext4_match
ディレクトリエントリの名前が一致するかの確認。リンク

↓ dx検索

3:ext4_dx_find_entry > ext4_map_blocks
論理ブロック番号を物理ブロックに変換し、必要に応じて新規割当も行う。リンク
4:ext4_dx_find_entry > find_get_block_common
指定ブロック番号に対応するbuffer_headをページキャッシュから検索し、保持参照を返す。リンク
5:ext4_dx_find_entry > ext4_match
ディレクトリエントリの名前が一致するかの確認。リンク

結び

ここまで「Open」システムコールを説明しました。
大まかな流れとしては、do_sys_openから「fd確保→struct file確保→パス確保(ext4_lookup)→ブロック層確保(ext4_file_open)→nameidata確保→最終的な紐付け」の流れでした。
次回は長くなりますが「Read」を解説していきます。

Discussion