criu checkのソースを読んでみる
趣旨
criu checkのソース(cr-check.c)を読んで、checkpoing/restoreに必要な機能について調べる。
criu checkを実行してみる
環境はFedora 20(kernel 3.10-rc3) + criu git master HEAD。
$ sudo /usr/local/sbin/criu check (00.011674) Error (mem.c:67): Can't reset 3122's dirty memory tracker: Invalid argument (00.012216) Error (kerndat.c:107): Dirty tracking support is OFF (00.012360) Warn (cr-check.c:516): Dirty tracking is OFF. Memory snapshot will not work. Looks good.
--msを付けるとマージされていないカーネル機能のチェックをスキップする。
$ sudo /usr/local/sbin/criu check --ms (00.001923) Warn (sockets.c:616): Skipping netling diag check (not yet merged) (00.003775) Warn (cr-check.c:473): Skipping peeking siginfos check (not yet merged) (00.003862) Warn (cr-check.c:508): Skipping dirty tracking check (not yet merged) Looks good.
とりあえず実装されている機能はすべてうまく動いていそう。
cr-check.cを読んでみる
check_tty()
ttyはよくわからん(´・ω・`)
check_map_files()
check_sock_diag()
check_ns_last_pid()
- /proc/sys/kernel/ns_last_pidの有無を調べる
- ns_last_pidは最後に生成されたプロセスのPIDを返す
- bash -c 'echo $$; read pid < /proc/sys/kernel/ns_last_pid; echo $pid'ってやると確かめられる
- またPIDを書き込んで次に生成されるプロセスのPIDを制御できる
- プロセスをrestoreするときに使われる
- echo 1111 > /proc/sys/kernel/ns_last_pid; bash -c 'echo $$; read pid < /proc/sys/kernel/ns_last_pid; echo $pid'
check_sock_peek_off()
- SO_PEEK_OFFソケットオプションが実装されているか調べる
- getsockopt(sk, SOL_SOCKET, SO_PEEK_OFF, &off, (socklen_t *)&sz);
- SO_PEEK_OFFはMSG_PEEKオプションを指定したrecv*システムコールでpeek(盗み見る)するデータのオフセットを取り出したり、変更したりするためのもの
- そもそもMSG_PEEKはソケットにあるデータの先頭からしかデータを盗み見ることはできなかった(必要なかったので
- そこでMSG_PEEKにもオフセットが用意された(SO_PEEK_OFFが追加されたとき)
- オフセットはMSG_PEEKでrecv*システムコールを呼ぶ度に盗み見たデータサイズ分増加する(ファイルポインタと同じ)
- これにより、MSG_PEEKでrecv*を何度も呼び出せば、ソケットに溜まったデータをすべて盗み見る=dumpすることが可能になる
- 疑問: この機能は必須なのだろうか?
check_kcmp()
- kcmp(2)の有無を調べる
- kcmpは2つのプロセスが資源を共有しているか調べる手段を提供する
- kernel/kcmp.cを見てみると、資源を共有している=task構造体が持つ資源毎のオブジェクトのアドレスが一致する、という判定をしていて興味深い(単純な比較をしているわけではないけれども)
check_prctl()
- prctl(2)の必要なオプションが実装されているか調べる
- PR_GET_TID_ADDRESS
- clear_tid_addressというpthread_joinに使われるアドレスを取得する
- pthread_join以外に使われているのかは未調査
- CLONE_CHILD_CLEARTIDオプション付きでclone(2)したプロセスは、プロセスが終了するときに、clear_tid_addressを0クリア+当該アドレスでfutex wakeされる
- pthread_joinする=対象スレッドのclear_tid_addressでfutex waitする、ってことなんだろう(たぶん)
- clear_tid_addressというpthread_joinに使われるアドレスを取得する
- PR_SET_MM + PR_SET_MM_BRK
- restoreするときにコード、データ、ヒープ、スタックのアドレスを指定してプロセスのメモリを復元するための機能
- PR_SET_MM_BRKは最初にPR_SET_MMが実装されたときに用意されたオプション。PR_SET_MMが実装されているかどうか判定できる
- PR_SET_MM + PR_SET_MM_EXE_FILE
- mm_struct::exe_file(/proc/pid/exe)を書き換える
- PR_SET_MM + PR_SET_MM_AUXV
- /proc/pid/auxvを書き換える
- /proc/pid/auxvにはELFインタプリタの情報が書き込まれるらしい
- 疑問: 何に使われるんだろう?
check_fcntl()
- F_GETOWNER_UIDSオプションの有無を調べる(未実装)
check_proc_stat()
- arg_start/end, env_start/end and exit_codeフィールドの有無を調べている
check_fdinfo_eventfd()
- eventfd(2)で作られるfdの/proc/pid/fdinfo/fdにeventfd-countのエントリが存在するか調べる
- eventfd-countはeventfdしたときにカーネル内部に用意されるカウンターの値
- カウンターについてはman 2 eventfdを参照
check_fdinfo_signalfd()
- signalfd(2)で作られるfdの/proc/pid/fdinfo/fdにsigmaskのエントリが存在するか調べる
- sigmaskはsignalfdを呼び出すときに指定する
check_fdinfo_eventpoll()
- epoll_create(2)で作られるfdの/proc/pid/fdinfo/fdにtfdのエントリが存在するか調べる
- epoll_ctl(2)で追加したfdのtfdが一致するか調べる
check_fdinfo_inotify()
- inotify_init1(2)で作られるfdの/proc/pid/fdinfo/fdにinotify wdのエントリが存在するか調べる
- inotify_add_watch(2)で追加したfdがinotify wdと一致するか調べる
- 疑問: 上記パッチでfanotifyのエントリも追加されているけど、それに対するチェックがない
check_unaligned_vmsplice()
check_so_gets()
- getsockopt(2)のSO_GET_FILTERとSO_BINDTODEVICEオプションの有無を調べる
- SO_GET_FILTER: SO_ATTACH_FILTERで設定したフィルタを取り出す
- SO_BINDTODEVICE: SO_BINDTODEVICEで設定したデバイス名(インタフェース名)を取り出す
check_ipc()
- /proc/sys/kernel/sem_next_idの有無を調べる
- ns_last_pidと同じように、次に割り当てられるIPCオブジェクトIDを制御するためのもの
check_sigqueuinfo()
check_ptrace_peeksiginfo()
- ptraceの新たな機能(リクエスト)PTRACE_PEEKSIGINFOの有無を調べる
- PTRACE_PEEKSIGINFOを使うとプロセスのシグナルを盗み見ることができる
- 指定offsetから指定数だけ盗み見れる
- 元々PTRACE_GETSIGINFOという機能はあったけど、これは最後のシグナルの情報しか取れなかった
check_mem_dirty_track()
- kerndat_get_dirty_track()@kerndat.cを呼んで機能が有効になっているか調べる
- ここで言ってるdirty trackとは、VMのライブマイグレーションでマイグレーション中に更新されたメモリ(ページ)だけ再度転送するためにやってることのプロセス版で、soft dirty trackingのこと(まだマージされていない)
- soft dirty tracking
- 機能要件
- 必要なときだけonにしてoffのときにはオーバヘッドがないようにする
- ユーザレベルからdirty pageを取得できるようにする
- ユーザレベルからdirty bitをクリアできるようにする(この要件があるのでハードウェアのdirty bitは使えない)
- PTE内にハードウェアのdirty bitの他にソフトウェアで管理するsoft dirty bitを用意する
- /proc/self/pagemap経由でsoft dirty bit読み出せるようにする
- echo 4 > /proc/pid/clear_refsで全ページのsoft dirty bitをクリアするとともに、ページをreadonlyにして書き込み時にページフォールトが起きるようにする
- ページフォールトが起きた時にsoft dirty bitが立つようにする
- これによりclear_refsに書き込んだ時点からのdirtyページを取得することができる
- 機能要件
- kerndat_get_dirty_trackは実際にsoft dirty bitが立つかどうか試している
- /proc/pid/pagemapの前に/proc/pid/pagemap2を試している
- 疑問: 結局pagemap2って追加されるの?