Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
open()の実装を読んでみる
Search
happynote3966
November 10, 2018
Technology
1
850
open()の実装を読んでみる
Kernel/VM探検隊@北陸 part4で発表した資料です
happynote3966
November 10, 2018
Tweet
Share
More Decks by happynote3966
See All by happynote3966
QEMUを用いた自動バイナリ防御機構の開発_公開版.pdf
happynote3966
6
3.7k
CWEから学ぶ脆弱性
happynote3966
3
1.3k
Other Decks in Technology
See All in Technology
AIエージェントの活用に重要な「MCP (Model Context Protocol)」とは何か
masayamoriofficial
0
230
まだ間に合う! StrandsとBedrock AgentCoreでAIエージェント構築に入門しよう
minorun365
PRO
10
600
モダンフロントエンド 開発研修
recruitengineers
PRO
9
5.7k
AI時代に非連続な成長を実現するエンジニアリング戦略
sansantech
PRO
3
840
ドキュメントはAIの味方!スタートアップのアジャイルを加速するADR
kawauso
3
470
衝突して強くなる! BLUE GIANTと アジャイルチームの共通点とは ― いきいきと活気に満ちたグルーヴあるチームを作るコツ ― / BLUE GIANT and Agile Teams
naitosatoshi
0
270
実践AIガバナンス
asei
3
250
自社製CMSからmicroCMSへのリプレースがプロダクトグロースを加速させた話
nextbeatdev
0
380
TypeScript入門
recruitengineers
PRO
33
11k
実践データベース設計 ①データベース設計概論
recruitengineers
PRO
4
1.8k
実践アプリケーション設計 ②トランザクションスクリプトへの対応
recruitengineers
PRO
4
1.1k
新規案件の立ち上げ専門チームから見たAI駆動開発の始め方
shuyakinjo
0
610
Featured
See All Featured
Unsuck your backbone
ammeep
671
58k
Product Roadmaps are Hard
iamctodd
PRO
54
11k
Become a Pro
speakerdeck
PRO
29
5.5k
Cheating the UX When There Is Nothing More to Optimize - PixelPioneers
stephaniewalter
284
13k
Code Review Best Practice
trishagee
70
19k
Being A Developer After 40
akosma
90
590k
YesSQL, Process and Tooling at Scale
rocio
173
14k
Practical Orchestrator
shlominoach
190
11k
Fashionably flexible responsive web design (full day workshop)
malarkey
407
66k
I Don’t Have Time: Getting Over the Fear to Launch Your Podcast
jcasabona
33
2.4k
Distributed Sagas: A Protocol for Coordinating Microservices
caitiem20
333
22k
Agile that works and the tools we love
rasmusluckow
330
21k
Transcript
open()の実装を 読んでみる Kernel/VM探検隊@北陸 part4 @happynote3966
$ whoami はっぴーのーと(@happynote3966) どこかの学生 happynote3966.hatenadiary.com 興味のあること:OSとか仮想化とかセキュリティとか SecHack365というイベントでQEMUをイジイジ 2
$ whoami はっぴーのーと(@happynote3966) どこかの学生 happynote3966.hatenadiary.com 興味のあること:OSとか仮想化とかセキュリティとか SecHack365というイベントでQEMUをイジイジ 3
動機 OSの勉強のためにLinuxのソースコードを読んでみようとは誰もが考える →何度か挑んでみてもワケがわからなくなり終了する 「Linuxのコア部分の入り口になるシステムコールなら読みやすいんじゃないか?」 →「じゃあやってみよう!とりあえず open()からやってみるか!」 ユーザーモードアプリケーション ライブラリ システムコール(open) い
ろ い ろ
Linuxのバージョン commit 31130a16d459de809cd1c03eabc9567d094aae6a 4.19-rc1(だと思う)
どこにopen()があるのか?
システムコールの呼び出し場所 fs/open.c内の1074行目
do_sys_open
do_sys_open
呼び出しの前提 int fd = open(“file.txt”,O_RDONLY)
読む時に意識すること likelyマクロとunlikelyマクロを利用する(※) likelyマクロは条件が真になりやすく、unlikelyマクロは条件が偽になりやすい →likelyの部分はなるべく読んで、unlikelyの部分はなるべく読まない errnoの存在を利用する 今回はopen()システムコールが無事に成功することを前提にする →errnoが代入されたりするようなところはなるべく読まない (※)https://qiita.com/kaityo256/items/8a0c5376fad17907e1f6
build_open_flags()周辺を読む
ソースコード
やっていること 開きたいファイルのopen_flags構造体を初期化している 関数自体は0を返すので、呼び出し元の次のif文は実行されない struct open_flags{ int open_flag; umode_t mode; int
acc_mode; int intent; int lookup_flags; }
open_flags open_flag = 0 mode = 0 acc_mode = 004
intent = 0x100 (LOOKUP_OPEN) lookup_flags = 0x001 (LOOKUP_FOLLOW)
tmp = getname(filename)周辺を読む (getname_flags(filename,0,NULL))
ソースコード
やっていること 開きたいファイルの名前に関するfilename構造体を スラブアロケータから取得して初期化している その後、初期化したfilename構造体へのポインタを返す struct filename{ const char *name; const
__user char *uptr int refcnt; struct audit_names *aname; const char iname[]; }
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”
fd = get_unused_fd_flags(flags)周辺を読む (__alloc_fd(current->files,0,rlimit(RLIMIT_NOFILE),flags))
ソースコード
やっていること 開きたいファイルのファイルディスクリプタを割り当ててもらう 今割り当てられているファイルディスクリプタの中から空いているものを選ぶ (今回だと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; }
イメージ 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の領域
do_filp_openを読む
ソースコード
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; }
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
path_openat()
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; }
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;
path_init(一部省略)
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)
link_path_walk()の処理 dエントリキャッシュのハッシュテーブルを探すために ハッシュ値を計算するらしい (詳しいハッシュの計算などは分からなかった)
do_last()(※抜粋)
vfs_open()
do_dentry_open()(※抜粋)
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)
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);
ファイルシステムの確認 ファイルシステムの種類を確認するにはdf -Tコマンドを使う
ext4のfile_operations fs/ext4/inode.cにある
まとめ ①ファイルの権限やモードに関するopen_flags構造体を作成し
まとめ ①ファイルの権限やモードに関するopen_flags構造体を作成し ②ファイルの名前に関するfilename構造体を作成して
まとめ ①ファイルの権限やモードに関するopen_flags構造体を作成し ②ファイルの名前に関するfilename構造体を作成して ③空いているファイルディスクリプタを探して
まとめ ①ファイルの権限やモードに関するopen_flags構造体を作成し ②ファイルの名前に関するfilename構造体を作成して ③空いているファイルディスクリプタを探して ④指定されたファイル名が存在する ファイルシステムに沿ったfile_operationsのopenを実行する
今後の課題や感想 [課題]もっと色んな引数のパターンを想定したソースコードリーディングを行う すごく単純な場合で読んだために「特殊な場合」を読めていない [課題]カーネルデバッグをしながら読むべきだった 実際の値を確認していないからどこか間違っている可能性大
今後の課題や感想 [課題]もっと色んな引数のパターンを想定したソースコードリーディングを行う すごく単純な場合で読んだために「特殊な場合」を読めていない [課題]カーネルデバッグをしながら読むべきだった 実際の値を確認していないからどこか間違っている可能性大 [感想]Linuxの一部を理解するだけでも難しかったのに Linux & 人の気持ちを完全に理解しているLinusはスゴイ
Thank you for your attention!