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って追加されるの?
CRIU(crtools)関連のリンク
- CRIU 公式サイト
- Upstream kernel commits - CRIU カーネルへのパッチのマージ状況
- http://git.criu.org/?p=crtools.git;a=summary GRIU gitレポジトリ
- http://git.kernel.org/cgit/linux/kernel/git/gorcunov/linux-cr.git linux-cr gitレポジトリ
- linux gitレポジトリ
- Checkpoint/Restore mostly in Userspace 開発者のプレゼン資料
- Features/Checkpoint Restore - Fedora Project Wiki Fedora対応状況
- Overview of Checkpoint and Restore - live-migrating processes on a Linux system - AWS Managed Services by Anchor 解説記事
- LWNの解説記事
- CRIU (1) - TenForward CRIU (2) - TenForward @ten_forwardさんの試用レポート
- TCP connectionの保存と復元 @mittyorzさんのLTプレゼン資料
- checkpoint.c lxc_checkpoint.c LXCのソースコード(未実装)
Androidアプリ作成時に参考にしたサイト
初めてAndroidアプリを作って、Android Marketで公開するまでに参照したサイトをまとめてみました。
- Download Android Studio and SDK tools
- textdrop.net - このウェブサイトは販売用です! - リソースおよび情報
- Eclipse Downloads | The Eclipse Foundation
ハードウェアスペックは?(HTC Desire)
Eclipseでの開発の基礎など
- サンプルソースコードの実行手順 - ひつじのにっき
- Waiting for HOME ('android.process.acore') to be launched - Stack Overflow
- 謎のエラーへの対処
データを保存する方法にどんなものがあるのか?
Intentについて
- Intents and Intent Filters | Android Developers
- 暗黙のIntentを投げてみた - minghaiの日記
- http://www.xn--rhq6sw9f0w7aevaf9ak89m.jp/android/androidLecture/Intent2/Intent2.html
- http://techbooster.jpn.org/andriod/application/1388/#more-1388
- Android 暗黙的 Intent で Twitter や Evernote と連携する - グロブ
- ブラウザの「ページを共有」からURLを取り出してみる - UpDownG
- 実際のIntentの中身
Activityについて
- Activities | Android Developers
- Activity | Android Developers
- Androidアプリ作成の基本“Activity”とは何か? (2/2):Androidで動く携帯Javaアプリ作成入門(2) - @IT
- 第4回 Androidの肝,アクティビティ:世界を目指せ!Androidアプリ開発入門|gihyo.jp … 技術評論社
ListViewとListActivityについて
- ListView の基本的な使い方 - Android 開発入門
- http://www.vogella.de/articles/AndroidListView/article.html
- AndroidのListActivity: おちエンのブログ
- ListViewとListActivity(3)-応用編 - 愚鈍人
- http://d.hatena.ne.jp/horafuki_taka/20110301/1298983624
Layoutや外観など
- Linear Layout | Android Developers
- Android SDK で, LinearLayout の動作を確認してみた.:新適当マイコン電子工作研究所:So-net blog
- Y.A.M の 雑記帳: Android Localeに対応した曜日や月の表記一覧を取得する, Androidアプリの使いやすさを左右する5つのレイアウト (1/3):Androidで動く携帯Javaアプリ作成入門(5) - @IT
- TableLayout
- Y.A.M の 雑記帳: Android SimpleCursorAdapter で ViewBinder を使う
- 色を変えたい
- Color | Android Developers
- 文字の色をかえる - Androidプログラマへの道 〜 Moonlight 明日香 〜
- CSS/HTML色見本
- What is the default text size on Android? - Stack Overflow
- View | Android Developers
- 外観に関係する属性一覧
- [android-beginners] Re: vertical scrolling TableLayout
- TableLayoutをスクロールさせたい
SQLiteを使いたい
- http://www.vogella.de/articles/AndroidSQLite/article.html
- これを読めば基本的な使い方は大体わかる
- http://clearmobile.googlecode.com/svn-history/r159/trunk/clients/android/src/org/clear/data/ClearDatabase.java
- バイト列をDBに保存したい
- How to insert a SQLite record with a datetime set to 'now' in Android application? - Stack Overflow
- datetimeの扱い
- adbコマンドを使ったシェルの起動 - Androidプログラミング入門 | JavaDrive
- シェルでDBを触る方法
- ストレージ関連 | mucchinのAndroid戦記, arraylist - How can I create a list Array with the cursor data in Android - Stack Overflow
- Cursorをイテレーションしたかった
- CursorAdapterの基本 - Boring Days as tech
- SimpleCursorAdapterでListViewのデータを更新したときに呼ぶのは?
長押しをハンドリングしたい
ダイアログ
その他
- How to use Parcel in Android? - Stack Overflow
- Parcel
- Y.A.M の 雑記帳: Android Bundle で状態を保存
- Bundle
- Manifest.permission | Android Developers
アプリ公開
- Product icons - Material Design
- アイコン作成規定
- http://www.quirco.com/iPhoneIcon/
- iPhoneアプリぽいアイコンに自動整形
- InkScape入門 ハニカムビーを描く - Firespeed
- http://android-ui-utils.googlecode.com/hg/asset-studio/dist/index.html
- Android向けアイコンを自動生成
- Publish your app | Android Developers, debugging - How Do I Create A Certificate For My Android Market APK? - Stack Overflow
- アプリに署名する
- Set up parental controls on Google Play - Google Play Help
- Content Ratingとは?
- Redirecting...
- マーケットの自分のホームページ
- 「はじめての Android」開発環境 〜Eclipse 編〜 (5) : Android プロジェクトの署名&パッケージング - 倭マン's BLOG
- 既存のキーストアを使用して署名&パッケージング
systemdをいじったときのメモ
はじめに
How to boot up Fedora 15 with systemd inside Libvirt LXC · GitHubを書いたときに知ったsystemdについてのメモです。
systemdが起動していないときにファイルをいじる方法が主で、systemctlコマンドの使い方についてはあまり書かれていません。あしからず。
Systemdのバージョンは26(Fedora 15のもの)です。
systemdとは?
コマンドやファイル
- /sbin/systemctl: 起動タスクの制御、状態の表示、依存関係のグラフ化などいろんなことができるコマンド
- これ一つだけで大体できる
- sysvinit互換のservice/chkconfigコマンドもある
- /etc/systemd/system.conf: systemd自身の設定ファイル
- デバッグ出力の設定とか
- /lib/systemd/system/: 起動タスクの定義ファイルがある場所
- 基本的に触らない
- シンボリックリンクを張るか、コピーして使う
- /etc/systemd/system/: システムローカルの起動タスクの定義ファイルがある場所
- 起動タスクの挙動を変えたい時はこっちをいじる
- /etc/systemd/system/default.target: sysvinitでいうところのrunlevelの設定。/lib/systemd/system/のファイルへのシンボリックリンク
- デフォルトは/lib/systemd/system/graphical.target (runlevel=5)
- runlevel=3にしたければ、ln -sf /lib/systemd/system/multi-user.target /etc/systemd/system/default.target
- man systemd: マニュアル
- 他にもsystemd.*ってマニュアルがいっぱいあるので一通り目を通しておくと良い
起動タスク/資源の定義ファイル
起動タスクや資源にはいろいろ種類があるけど、ひっくるめてunitと呼んでいる。
- service: コマンドを実行したり、デーモンを起動したりする
- target: 自身では何も実行しない。unitのグループ化などに用いられる
- mount, automount: マウントポイントを表わす定義ファイル。automountには対応するmountが必要
- mountには何をどこにマウントするかを記述する。automountに記述するか、依存関係の結果によりブート時に自動的にマウントされる
- automountはその名の通りブート時にマウントさせたいときに使う。依存関係にないが、自動的にマウントさせたいときに使う(たぶん)
- socket: UNIXドメインソケットやFIFOやIPアドレス&ポート番号などのIPC/通信資源を表わす
- 接続要求があったら対応するserviceを起動する
- serviceを永続的に起動しておく場合(D-BUSやsyslogデーモン)と、接続要求ごとに起動する場合(xinetdのようなもの)がある
- path: ファイルやディレクトリ資源を監視して、状態に合わせてserviceを起動/有効化する
- 指定ファイルに変化があったらserviceを起動するとか、指定ディレクトリが空でない場合(≒ファイルが存在する限り)対応するserviceを起動しておくとか
依存関係などの記述方法
Unitによって記述できる内容は違う。詳しくはman systemd.*参照(例えば、systemd.targetやsystemd.service)
サンプルとしてfedora-autorelabel.serviceを載せる。
[Unit] Description=Relabel all filesystems, if necessary DefaultDependencies=no Requires=local-fs.target Conflicts=shutdown.target After=local-fs.target Before=sysinit.target shutdown.target ConditionSecurity=selinux ConditionKernelCommandLine=|autorelabel ConditionPathExists=|/.autorelabel [Service] ExecStart=/lib/systemd/fedora-autorelabel Type=oneshot TimeoutSec=0 RemainAfterExit=yes StandardInput=tty
- Requires: 当該unitが動いているときは指定したunitも起動しなければならない(動いていなければならない)
- 順序関係はない。つまり、並行起動可能である(ただし、後述のBefore/Afterが指定されていない場合)
- 後に起動したunitが何らかの理由で起動しなかった場合には、当該unitも起動しない(すでに起動していた場合は終了する)
- Conflicts: 当該unitが動いているときは指定したunitは起動してはならない(動いていてはならない)
- 使い方の一つは終了処理。shutdown.targetを入れておいて、シャットダウン時にshutdown.targetを強制起動させると、依存関係により当該unitを停止させようとする=終了処理が実行される
- Wants: 当該unitが起動さえるとき、指定したunitの起動も試みる
- Before: 当該unitは指定unitが起動する前に起動しなければならない
- After: 当該unitは指定unitが起動した後に起動しなければならない
- Condition*: 指定した条件を満たした場合のみ起動する
詳しくはman systemd.unitを参照。
知っておくと良いかもしれないこと
- unitを無効にしたい場合は/etc/systemd/systemディレクトリ内に当該定義ファイル名の/dev/nullへのシンボリックリンクを作れば良い
- 例: ln -sf /dev/null /etc/systemd/system/sysinit.service
- 無効にすると当該unitをRequiresしているunitは当該unitを無視して起動する
- もちろん、その結果、依存する処理が実行されず起動に失敗するかもしれないので取り扱いには注意
- systemdは定義ファイルにないmountを行なう
- /sysや/runなど
- systemdは暗黙的に依存関係を追加している
- basic.targetはsysvサービスをRequires/Afterしている
- 詳しくはman systemd.special参照
- socket unitを遅延起動するためにsystemdは当該資源をepollで監視してる
- 当該資源にデータが届いたら、オンデマンドに当該serviceを起動する
- 起動するgettyを減らしたい時は/etc/systemd/system/getty.target.wants/以下のシンボリックリンクを削除すれば良い
- systemctl dotで依存関係のグラフ(Grapvizのdotコマンドで扱える形式)を生成することができる
- 依存関係はsystemctl show -p Requires basic.targetなどで調べることができるけど、systemctl dot |grep basic.targetの方がわかりやすい場合がある
cgroupsとしばらく一緒に過ごしてみた
この記事はhttp://atnd.org/events/10701向けに書かれました。
はじめに
サーバ側ではかなり前からcgroupsを使っていましたが、クライアント側では使ったことがなかったので、頑張ってノートPCで使ってみることにしました。12月から使い始めて、1月半ぐらい使い続けてます。
サーバ側では独自スクリプトを書いて設定していましたが、今回は既存のツール(libcgroup)を使っています。とはいえ、libcgroupには資源利用状況を確認する機能がなかったので、独自スクリプト(cgtree)を作りました。もしよかったら使ってみてね!
今回cgroupsでやりたかったことは、
- 資源を大量に消費するプロセスの制限
- バックグラウンドプロセスの制限
などです。限られた資源を無駄に使うプロセスに鉄槌を。
注意
この記事で紹介する設定は、自分の環境に合わせてちまちまとチューニングしてきた結果ですので、万人向けではありません。私の設定は参考程度にして、自分の要件に合わせてチューニングしましょう。
定量的に評価してないので、著者の気のせいかもしれません。I _feel_ that the user experience is better than so far!
禁句:良いスペックのマシン買えば?メモリ増やせば?
基礎知識
cgroupsとは?
Linuxに実装された、プロセスをグループに分けて資源使用量を調節する機能です。個々の資源毎にサブシステムが用意されていて、個別にon/offできます。今回はcpu, cpuacct, memory, blkio, freezerサブシステムを使います。
何も設定していなければ、すべてのプロセスはどのグループにも属さないことになります。(rootグループに入っているとも言う。)
詳しくはRed Hatの平さんのプレゼン資料 (pdf)を読めばいいと思うよ!
libcgroupとは?
cgroupsの設定や操作を簡単に使うためのツールです。いろいろなデーモンやコマンドラインツールがありますが、今回主に使うのはcgconfigサービスとcgredサービス(cgrulesengdデーモン)です。cgconfigは/etc/cgconfig.confに設定されたとおりにグループ階層を作り(mountし)各種パラメタを設定します。cgredはfork/execなどを監視し、設定されたとおりにプロセスをグループに入れます(詳しい動作は後述)。
詳しくはRed Hatの平さんのプレゼン資料 (pdf)を読めばいいと思うよ!
cgtreeとは?
cgtreeは拙作のcgroupsグループ単位で資源利用状況を表示してくれるツールです。
[使用例]
$ cgtree -o memory # Total=1434.7MB, Used(w/o buffer/cache)=699.8MB, SwapUsed=139.0MB # Legend: # of procs (TotalUsed, RSS, SwapUsed): 103 procs (636.3MB, 343.3MB, 118.9MB) bgjob: 10 procs (42.3MB, 31.7MB, 31.7MB) noswap: 2 procs (29.9MB, 6.0MB, 1.1MB) browser: 1 procs (351.8MB, 201.5MB, 0.0MB) flash: 1 procs (13.1MB, 7.2MB, 0.0MB)
詳しい使い方はREADMEを読むかcgtree -hでオプションを確認してね!
環境
要件
ここでは具体的な要件(私がやりたいこと)をまとめます。
具体的な設定
主に/etc/cgconfig.confと/etc/cgrules.confをいじっていますが、cgrules.confの方は見たまんまなのでcgconfig.confの方を説明していきます。
完成品
https://gist.github.com/802907 に置いておきますね。
ブラウザ編
作ったグループは2つ。
使ったサブシステムも2つ。
- cpu
- memory
(cpuacctはcpuとペアで使うのでカウントしてないです。)
group browser { cpu { cpu.shares = 500; } cpuacct { } memory { memory.soft_limit_in_bytes = 400M; memory.limit_in_bytes = 500M; } } group browser/flash { cpu { cpu.shares = 200; } cpuacct { } memory { memory.soft_limit_in_bytes = 200M; memory.limit_in_bytes = 300M; } }
[cpu]
browserグループのcpu.shareは500にしてあり、rootグループのプロセスの半分しかCPUが割り当たらないようにしています。最適な値かどうかわからないですが、今のところ問題なさそうです。
Firefox-4.0はfirefox-binとplugin-container(Flash)という2つのプロセスを生成します。同一グループに両プロセスを入れた場合、Firefox本体とFlashでCPU割当て比が1:1になり、FlashがCPUを大量に消費すると本体の動画がもっさりします。そこでbrowser/flash/をcpu.share=200に設定して比率を5:1になるようにしました。
ちなみにGoogle Chromeの場合は、本体、タブ、拡張機能がそれぞれ別プロセスになるため、Flashと同じグループに置いておいてもcpu割当て量は1:1になりません(N:1になる)。なのでbrowser/flashグループは必要ないと思います。
[memory]
本体を400MB、Flashに200MB割り当たるように設定しました。日々調整しながら決まった値なので、環境依存です。また変えるかも。
階層構造を作っていますが、use_hierarchy=1にしていないので、制限は独立しています(つまり400MBを本体とFlashで取り合うわけではない)。
soft_limit_in_bytesとlimit_in_bytesについては後述。
バックグラウンドプロセス編
作ったグループは2つ。
使ったサブシステムは4つ。
- cpu
- memory
- blkio
- freezer
group bgjob { task { uid = root; gid = peo3; } admin { uid = root; gid = root; } cpu { cpu.shares = 10; } cpuacct { } blkio { blkio.weight = 100; } memory { memory.soft_limit_in_bytes = 100M; memory.limit_in_bytes = 200M; memory.swappiness = 100; } } group bgjob/restricted { perm { task { uid = root; gid = peo3; } admin { uid = root; gid = peo3; } } cpu { } cpuacct { } freezer { } }
[bgjob]
とにかく割当て資源量を少なくしています。cpu.share=10(rootグループのプロセスの1/100), メモリは100MB, swappinessは100(最もスワップアウトされ易くなる), blkio.weight=100(最低値)に設定。
[bgjob/restricted]
カーネルコンパイル時にはプロセスを大量に生成します。そして個々のプロセスはbgjob内のプロセスと同等のCPU資源を消費するので、バックグラウンドとはいえあまり嬉しくなかったので作りました。CPU使用量のみ制限してます。
単にCPU使用量を減らすだけならnice値を上げれば良いのですが、コンパイルを中断したいときがあったので別グループにしてfreezerサブシステムを有効にしました。これにより、
cgset -r freezer.state=FROZEN bgjob/restricted
で中断
cgset -r freezer.state=THAWED bgjob/restricted
で再開できます。また
cgexec -g blkio:bgjob cgexec -g cpu,freezer:bgjob/restricted fakeroot make-kpkg --initrd --append-to-version=-blkio kernel-image kernel-headers
で当該グループ内でカーネルコンパイルを行なうことができます*1。
ちなみにユーザがtasksやfreezer.stateを書きかえられるようにpermで設定しています。
Mozc編
group noswap { memory { memory.swappiness = 0; } }
swappinessを0にしてスワップされにくいようにしてます。いちおう効果があるぽいです。
なんちゃってAutogroup編
group user { perm { task { uid = root; gid = peo3; } admin { uid = root; gid = peo3; } } cpu { # release_agent = /usr/local/sbin/cgroup_clean; } cpuacct { } blkio { blkio.weight = 500; } }
ユーザがディレクトリを作れるようにperm.admin.gidをユーザのGIDに設定しています。
libcgroupはrelease_agentの設定に対応していないみたいです。なので/etc/init.d/cgconfigで設定するようにしました。
cgrules.conf
cgconfig.confで生成したグループにどのプロセスを入れるかを記述する設定ファイルです。cgrulesengdというデーモンが指定プロセスを指定グループに入れます(詳しい動作は後述)。
peo3:firefox-4.0-bin cpu,memory browser peo3:google-chrome cpu,memory browser peo3:firefox-bin cpu,memory browser peo3:plugin-container cpu,memory browser/flash peo3:ibus-engine-mozc memory noswap peo3:update-manager cpu,blkio,memory bgjob peo3:software-center cpu,blkio,memory bgjob peo3:dropbox cpu,blkio,memory bgjob peo3:gwibber-service cpu,blkio,memory bgjob peo3:tracker-store cpu,blkio,memory bgjob peo3:tracker-extract cpu,blkio,memory bgjob peo3:tracker-miner-fs cpu,blkio,memory bgjob lastfm:lastfmsubmitd cpu,blkio,memory bgjob #root:cron cpu,blkio,memory bgjob root:anacron cpu,blkio,memory bgjob root:aptd cpu,blkio,memory bgjob
cronの項目をコメントアウトしていますが、それはcgrulesengdがcronだけグループに入れてくれないからです。原因はまだわかってないです。
ただノートPCは常時起動させていないので、ほとんどの場合、定期実行タスクはanacron経由で実行されるため困ってはいないですが。
Tipsと注意
libcgroupのdefaultグループ
何はともあれ/etc/default/cgconfigを編集してCREATE_DEFAULT=noにしましょう。
cgconfigはCREATE_DEFAULT=yesのときにdefaultグループにすべてのプロセスを入れます。ですが、入れてはまずいカーネルスレッドも入れちゃうため、動作がおかしくなるときがあります*2。私の環境ではレジュームできなくなりました。
安全のためdefaultグループは使わないようにするのが良いと思います。(実際、なくても困らないですし。)
service cgconfig restart
修正した設定を反映させようとcgconfig restart/reloadすると、一度全部グループを消しちゃうので、割当てられたプロセスが全部rootグループに戻ってしまいます。
なので設定を反映させたかたらcgset等で自力でやるか、いっそのことrebootさせるのが良いかも。
service cgred restart
前述の通りシェルプロセスはuser/????/グループに入っているため、シェルでservice restartするとcgrulesengdデーモンがそのグループに入ってしまいます。
代わりにrootグループに入れるためには
sudo cgexec -g cpu,memory,blkio:/ service cgred restart
ってやると良いです。
cgconfig.confのコメントアウト
どうも行頭に'#'を書かないとだめみたいです。
デバッグ
See /var/log/messages
もっとkwsk知りたい方
cgrulesengdの動作
[どのタイミングでプロセスをグループに入れている?]
cgrulesengdはデーモンで、起動しているときにだけ指定プロセスを指定グループにいれてくれます。
最初は定期的にプロセスリストをチェックしてるのかと思ったのですが、そうではないようです。
netlinkのNETLINK_CONNECTORファミリでカーネルのprocess events connectorという機能を使うことで、プロセスの起動などのイベント発生時にcallbackしてもらってるようです。fork/exec/exit/setuid/setgidなどのイベントを契機にプロセスをグループに入れたりしているようです。
[cgrules.confに指定するプロセス名前]
どういうプロセス名前が指定可能なのか調べてみると、わりとまじめに名前抽出をしてることがわかりました。
cgrulesengdは/proc/
そうすることで/usr/bin/python /usr/lib/system-service/system-service-dのようなプロセスから、system-service-dという名前を抽出できるようになり、cgrules.confでsystem-service-dと指定可能になるわけです。
memory.limit_in_bytesとmemory.soft_limit_in_bytes
※正確なところはカーネル付属文書のSoft limitsの項を読むかソースコードを参照してください。
ざっくり言うとlimit_in_bytes(以下hard)の方が強い制限でsoft_limit_in_bytes(以下soft)の方が弱い制限ということになります。hardで設定したメモリ量を必ず越えないようにカーネルが制限をかける一方で*3、softで設定したメモリ量は、空きメモリに余裕がある間は超えても良いが、メモリが足りない状況では回収(もしくはスワップアウト?)されるかもしれないですすよ、ということらしいです。
softの細かい動作はこの辺りを読めば解るかも? kswapd => balance_pgdat => mem_cgroup_soft_limit_reclaim
もしsoft関連のreclaimがここにしかないならば、グループ内のプロセスがsoft limitを超えるメモリを使おうとしても、そのタイミングでは制限をかけない、ってことになるんじゃないかと思います。
group_isolationは設定すべき?
Linux付属ドキュメントにgroup_isolationに関する項目というものがあります。どうも、isolationを厳しくするか否かの設定らしいです。
私の環境では/sys/block/sda/queue/iosched/group_isolationです。
ドキュメントを読むと、0の場合(デフォルト)はsequential workloadの場合しか公平性を保証しない。1の場合は加えてrandom I/Oの公平性も保証するが、スループットが落ちるらしいです。
その下の段落の説明はよくわからなかったです。sync-noidleとかcollective idlingってなんだろ?
あと、最後の節「What works」に
Currently only sync IO queues are support. All the buffered writes are still system wide and not per group.
とありますね。。。うーん実際のところ普段のworkloadのうちどのぐらいがこのsync I/Oに該当するんだろ?もしかして大して効果ないのかな?
この辺りはまた暇があったら調べてみます。
おわりに
といった感じで様々な設定を行なうことで、前述の目標
- ブラウザが使う資源を制限したい
- バックグラウンドプロセスが使う資源を制限したい
- Mozcの反応を良くしたい
- Autogroupの真似事もしてみたい
は達成できた気がします。
個々の設定を決めるために、top, iotop, free, ps, vmstat, dstat, nice, ioniceそしてcgtreeといった資源使用量表示ツールを使って思ったとおり動いているか、自分で体感して不満はないかなどかなり試行錯誤をしています。快適な環境は一日にしてはならずですね。まぁメモリ増設すれ「それ以上いけない」
なにはともあれ、みんなもcgroupsを日常的に使ってみよう!
参考文献
*1:カーネル2.6.35はblkioの階層機能がなく、blkioとcpu,freezerでグループ階層が違うため、個別にグループ割当てを行なってます。2.6.38からはこの制限がなくなります。
*2:関係ありそうなバグレポート https://lkml.org/lkml/2011/1/4/313
*3:やむを得ず超えることもあるかも?この辺はソース読まないとなんとも言えないです。