Upgrade to Pro — share decks privately, control downloads, hide ads and more …

open()の実装を読んでみる

happynote3966
November 10, 2018

 open()の実装を読んでみる

Kernel/VM探検隊@北陸 part4で発表した資料です

happynote3966

November 10, 2018
Tweet

More Decks by happynote3966

Other Decks in Technology

Transcript

  1. open()の実装を
    読んでみる
    Kernel/VM探検隊@北陸 part4
    @happynote3966

    View full-size slide

  2. $ whoami
    はっぴーのーと(@happynote3966)
    どこかの学生
    happynote3966.hatenadiary.com
    興味のあること:OSとか仮想化とかセキュリティとか
    SecHack365というイベントでQEMUをイジイジ
    2

    View full-size slide

  3. $ whoami
    はっぴーのーと(@happynote3966)
    どこかの学生
    happynote3966.hatenadiary.com
    興味のあること:OSとか仮想化とかセキュリティとか
    SecHack365というイベントでQEMUをイジイジ
    3

    View full-size slide

  4. 動機
    OSの勉強のためにLinuxのソースコードを読んでみようとは誰もが考える
    →何度か挑んでみてもワケがわからなくなり終了する
    「Linuxのコア部分の入り口になるシステムコールなら読みやすいんじゃないか?」
    →「じゃあやってみよう!とりあえず open()からやってみるか!」
    ユーザーモードアプリケーション
    ライブラリ
    システムコール(open)
    い ろ い ろ

    View full-size slide

  5. Linuxのバージョン
    commit 31130a16d459de809cd1c03eabc9567d094aae6a
    4.19-rc1(だと思う)

    View full-size slide

  6. どこにopen()があるのか?

    View full-size slide

  7. システムコールの呼び出し場所
    fs/open.c内の1074行目

    View full-size slide

  8. 呼び出しの前提
    int fd = open(“file.txt”,O_RDONLY)

    View full-size slide

  9. 読む時に意識すること
    likelyマクロとunlikelyマクロを利用する(※)
    likelyマクロは条件が真になりやすく、unlikelyマクロは条件が偽になりやすい
    →likelyの部分はなるべく読んで、unlikelyの部分はなるべく読まない
    errnoの存在を利用する
    今回はopen()システムコールが無事に成功することを前提にする
    →errnoが代入されたりするようなところはなるべく読まない
    (※)https://qiita.com/kaityo256/items/8a0c5376fad17907e1f6

    View full-size slide

  10. build_open_flags()周辺を読む

    View full-size slide

  11. ソースコード

    View full-size slide

  12. やっていること
    開きたいファイルのopen_flags構造体を初期化している
    関数自体は0を返すので、呼び出し元の次のif文は実行されない
    struct open_flags{
    int open_flag;
    umode_t mode;
    int acc_mode;
    int intent;
    int lookup_flags;
    }

    View full-size slide

  13. open_flags
    open_flag = 0
    mode = 0
    acc_mode = 004
    intent = 0x100 (LOOKUP_OPEN)
    lookup_flags = 0x001 (LOOKUP_FOLLOW)

    View full-size slide

  14. tmp = getname(filename)周辺を読む
    (getname_flags(filename,0,NULL))

    View full-size slide

  15. ソースコード

    View full-size slide

  16. やっていること
    開きたいファイルの名前に関するfilename構造体を
    スラブアロケータから取得して初期化している
    その後、初期化したfilename構造体へのポインタを返す
    struct filename{
    const char *name;
    const __user char *uptr
    int refcnt;
    struct audit_names *aname;
    const char iname[];
    }

    View full-size slide

  17. open_flags
    open_flag = 0
    mode = 0
    acc_mode = 004
    intent = 0x100 (LOOKUP_OPEN)
    lookup_flags = 0x001 (LOOKUP_FOLLOW)
    filename
    name = iname
    uptr = “file.txt”(userland)
    refcnt = 1
    aname = NULL
    iname[] = “file.txt”

    View full-size slide

  18. fd = get_unused_fd_flags(flags)周辺を読む
    (__alloc_fd(current->files,0,rlimit(RLIMIT_NOFILE),flags))

    View full-size slide

  19. ソースコード

    View full-size slide

  20. やっていること
    開きたいファイルのファイルディスクリプタを割り当ててもらう
    今割り当てられているファイルディスクリプタの中から空いているものを選ぶ
    (今回だと3になる)
    struct fdtable{
    unsigned int max_fds;
    struct file __rcu **fd;
    unsigned long *close_on_exec;
    unsigned long *open_fds;
    unsigned long *full_fds_bits;
    struct rcu_head rcu;
    }

    View full-size slide

  21. イメージ
    struct fdtable{
    unsigned int max_fds;
    struct file __rcu **fd;
    unsigned long *close_on_exec;
    unsigned long *open_fds;
    unsigned long *full_fds_bits;
    struct rcu_head rcu;
    }
    kvmallocで割り当てられた領域
    open_fdsの領域
    11100000 00000000 00000000
    00000000 00000000 00000000
    00000000 00000000 00000000
    00000000 00000000 00000000
    close_on_execの領域
    full_fds_bitsの領域

    View full-size slide

  22. do_filp_openを読む

    View full-size slide

  23. ソースコード

    View full-size slide

  24. set_nameidata()での処理
    パス名の検索処理に関連する構造体であるnameidata構造体を初期化する
    struct nameidata{
    struct path path;
    struct qstr last;
    struct path root;
    struct inode *inode;
    unsigned int flags;
    unsigned seq, m_seq;
    int last_type;
    unsigned depth;
    int total_link_count;
    struct saved {
    struct path link;
    struct delayed_call done;
    const char *name;
    unsigned seq;
    } *stack,
    internal[EMBEDDED_LEVELS];
    struct filename *name;
    struct nameidata *saved;
    struct inode *link_inode;
    unsigned root_seq;
    int dtf;
    }

    View full-size slide

  25. open_flags
    open_flag = 0
    mode = 0
    acc_mode = 004
    intent = 0x100 (LOOKUP_OPEN)
    lookup_flags = 0x001 (LOOKUP_FOLLOW)
    filename
    name = iname
    uptr = “file.txt”(userland)
    refcnt = 1
    aname = NULL
    iname[] = “file.txt”
    nameidata
    path
    last
    root
    inode
    flags
    seq
    m_seq
    last_type
    depth
    total_link_count = old->total_link_count;
    stack
    internal[EMBEDDED_LEVELS]
    name
    saved = current->nameidata
    link_inode
    root_seq
    dfd = AT_FDCWD

    View full-size slide

  26. path_openat()

    View full-size slide

  27. alloc_empty_file()での処理
    オープンされているファイルとプロセスとのやり取りに関する
    file構造体を作成している
    struct file{
    union {
    struct llist_node fu_llist;
    struct rcu_head fu_rcuhead;
    }f_u;
    struct path fpath;
    struct inode *f_inode;
    const struct file_operations *f_op;
    spinlock_t f_lock;
    enum rw_hint f_write_hint;
    atomic_long_t f_count;
    unsigned int f_flags;
    fmode_t f_mode;
    struct mutex f_pos_lock;
    loff_t f_pos;
    struct fown_struct f_owner;
    const struct cred *f_cred;
    struct file_ra_state f_ra;
    u64 f_verson;
    void *f_security;
    void *private_data;
    struct list_head f_ep_links;
    struct list_head f_tfile_lliink;
    struct address_space *f_mapping;
    errseq_t f_wb_err;
    }

    View full-size slide

  28. open_flags
    open_flag = 0
    mode = 0
    acc_mode = 004
    intent = 0x100 (LOOKUP_OPEN)
    lookup_flags = 0x001 (LOOKUP_FOLLOW)
    filename
    name = iname
    uptr = “file.txt”(userland)
    refcnt = 1
    aname = NULL
    iname[] = “file.txt”
    nameidata
    path
    last
    root
    inode
    flags
    seq
    m_seq
    last_type
    depth
    total_link_count = old->total_link_count;
    stack
    internal[EMBEDDED_LEVELS]
    name
    saved = current->nameidata
    link_inode
    root_seq
    dfd = AT_FDCWD
    file
    f_u;
    fpath;
    f_inode;
    f_op;
    f_lock;
    f_write_hint;
    f_count = 1
    f_flags = 0
    f_mode = 1(FMODE_READ)
    f_pos_lock;
    f_pos;
    f_owner;
    f_cred = current->cred
    f_ra;
    f_verson;
    f_security;
    private_data;
    f_ep_links;
    f_tfile_lliink;
    f_mapping;
    f_wb_err;

    View full-size slide

  29. path_init(一部省略)

    View full-size slide

  30. open_flags
    open_flag = 0
    mode = 0
    acc_mode = 004
    intent = 0x100 (LOOKUP_OPEN)
    lookup_flags = 0x001 (LOOKUP_FOLLOW)
    filename
    name = iname
    uptr = “file.txt”(userland)
    refcnt = 1
    aname = NULL
    iname[] = “file.txt”
    nameidata
    path = current->fs->pwd
    last = (.hash_len = hash_len,.name = )
    root
    inode =
    flags = LOOKUP_FOLLOW |
    LOOKUP_RCU | LOOKUP_PARENT
    seq
    m_seq
    last_type = LAST_NORM
    depth = 0
    total_link_count = old->total_link_count;
    stack
    internal[EMBEDDED_LEVELS]
    name
    saved = current->nameidata
    link_inode
    root_seq
    dfd = AT_FDCWD
    file
    f_u;
    fpath;
    f_inode;
    f_op;
    f_lock;
    f_write_hint;
    f_count = 1
    f_flags = 0
    f_mode = 1(FMODE_READ)
    f_pos_lock;
    f_pos;
    f_owner;
    f_cred = current->cred
    f_ra;
    f_verson;
    f_security;
    private_data;
    f_ep_links;
    f_tfile_lliink;
    f_mapping;
    f_wb_err;
    dentry
    struct inode *d_inode;
    inode
    const struct file_operations i_fop;
    file_operations
    (ext4_file_operations)

    View full-size slide

  31. link_path_walk()の処理
    dエントリキャッシュのハッシュテーブルを探すために
    ハッシュ値を計算するらしい
    (詳しいハッシュの計算などは分からなかった)

    View full-size slide

  32. do_last()(※抜粋)

    View full-size slide

  33. do_dentry_open()(※抜粋)

    View full-size slide

  34. open_flags
    open_flag = 0
    mode = 0
    acc_mode = 004
    intent = 0x100 (LOOKUP_OPEN)
    lookup_flags = 0x001 (LOOKUP_FOLLOW)
    filename
    name = iname
    uptr = “file.txt”(userland)
    refcnt = 1
    aname = NULL
    iname[] = “file.txt”
    nameidata
    path = current->fs->pwd
    last = (.hash_len = hash_len,.name = )
    root
    inode =
    flags = LOOKUP_FOLLOW |
    LOOKUP_RCU | LOOKUP_PARENT
    seq
    m_seq
    last_type = LAST_NORM
    depth = 0
    total_link_count = old->total_link_count;
    stack
    internal[EMBEDDED_LEVELS]
    name
    saved = current->nameidata
    link_inode
    root_seq
    dfd = AT_FDCWD
    file
    f_u;
    fpath;
    f_inode;
    f_op;
    f_lock;
    f_write_hint;
    f_count = 1
    f_flags = 0
    f_mode = 1(FMODE_READ)
    f_pos_lock;
    f_pos;
    f_owner;
    f_cred = current->cred
    f_ra;
    f_verson;
    f_security;
    private_data;
    f_ep_links;
    f_tfile_lliink;
    f_mapping;
    f_wb_err;
    dentry
    struct inode *d_inode;
    inode
    const struct file_operations i_fop;
    file_operations
    (ext4_file_operations)

    View full-size slide

  35. file_operations構造体(※抜粋)
    struct file_operations{
    struct module *owner;
    loff_t (*llseek) (struct file *,loff_t, int);
    ssize_t (*read) (struct file *, char __user *,size_t,loff_t *);
    ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
    ssize_t (*read_iter) (struct kiocb *, struct iov_iter *);
    ssize_t (*write_iter) (struct kiocb *, struct iov_iter *);
    int (*iterate) (struct file *, struct dir_context *);
    int (*iterate_shared) (struct file *, struct dir_context *);
    __poll_t (*poll) (struct file *, struct poll_table_struct *);
    long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
    long (*compat_ioctl) (struct file *, unsigned int, unsigned long);
    int (*mmap) (struct file*, struct vm_area_struct *);
    unsigned long mmap_supported_flags;
    int (*open) (struct inode *, struct file *);
    int (*flush) (struct file *, fl_owner_t id);
    int (*release) (struct inode *, struct file *);
    int (*fsync) (struct file *, loff_t, loff_t, int datasync);

    View full-size slide

  36. ファイルシステムの確認
    ファイルシステムの種類を確認するにはdf -Tコマンドを使う

    View full-size slide

  37. ext4のfile_operations
    fs/ext4/inode.cにある

    View full-size slide

  38. まとめ
    ①ファイルの権限やモードに関するopen_flags構造体を作成し

    View full-size slide

  39. まとめ
    ①ファイルの権限やモードに関するopen_flags構造体を作成し
    ②ファイルの名前に関するfilename構造体を作成して

    View full-size slide

  40. まとめ
    ①ファイルの権限やモードに関するopen_flags構造体を作成し
    ②ファイルの名前に関するfilename構造体を作成して
    ③空いているファイルディスクリプタを探して

    View full-size slide

  41. まとめ
    ①ファイルの権限やモードに関するopen_flags構造体を作成し
    ②ファイルの名前に関するfilename構造体を作成して
    ③空いているファイルディスクリプタを探して
    ④指定されたファイル名が存在する
    ファイルシステムに沿ったfile_operationsのopenを実行する

    View full-size slide

  42. 今後の課題や感想
    [課題]もっと色んな引数のパターンを想定したソースコードリーディングを行う
    すごく単純な場合で読んだために「特殊な場合」を読めていない
    [課題]カーネルデバッグをしながら読むべきだった
    実際の値を確認していないからどこか間違っている可能性大

    View full-size slide

  43. 今後の課題や感想
    [課題]もっと色んな引数のパターンを想定したソースコードリーディングを行う
    すごく単純な場合で読んだために「特殊な場合」を読めていない
    [課題]カーネルデバッグをしながら読むべきだった
    実際の値を確認していないからどこか間違っている可能性大
    [感想]Linuxの一部を理解するだけでも難しかったのに
    Linux & 人の気持ちを完全に理解しているLinusはスゴイ

    View full-size slide

  44. Thank you for your attention!

    View full-size slide