前提
cgroup.event_control
の概要
特定のcgroupに何かが起こった時に、eventfdを経由してユーザランドに通知を送り込む仕組みがある。 cgroup.event_control
は # an interface for event_fd()
とされている。
cgroupを一つ作ると、原則そのファイルも新しく一つできる。あるcgroupに一対一で存在するという認識で良さそう。
root@ubuntu-bionic:~# mkdir /sys/fs/cgroup/memory/udzura-sample
root@ubuntu-bionic:~# ls -l /sys/fs/cgroup/memory/udzura-sample
total 0
-rw-r--r-- 1 root root 0 Jul 23 09:24 cgroup.clone_children
--w--w--w- 1 root root 0 Jul 23 09:24 cgroup.event_control
-rw-r--r-- 1 root root 0 Jul 23 09:24 cgroup.procs
-rw-r--r-- 1 root root 0 Jul 23 09:24 memory.failcnt
--w------- 1 root root 0 Jul 23 09:24 memory.force_empty
-rw-r--r-- 1 root root 0 Jul 23 09:24 memory.kmem.failcnt
-rw-r--r-- 1 root root 0 Jul 23 09:24 memory.kmem.limit_in_bytes
-rw-r--r-- 1 root root 0 Jul 23 09:24 memory.kmem.max_usage_in_bytes
-r--r--r-- 1 root root 0 Jul 23 09:24 memory.kmem.slabinfo
-rw-r--r-- 1 root root 0 Jul 23 09:24 memory.kmem.tcp.failcnt
-rw-r--r-- 1 root root 0 Jul 23 09:24 memory.kmem.tcp.limit_in_bytes
-rw-r--r-- 1 root root 0 Jul 23 09:24 memory.kmem.tcp.max_usage_in_bytes
-r--r--r-- 1 root root 0 Jul 23 09:24 memory.kmem.tcp.usage_in_bytes
-r--r--r-- 1 root root 0 Jul 23 09:24 memory.kmem.usage_in_bytes
-rw-r--r-- 1 root root 0 Jul 23 09:24 memory.limit_in_bytes
-rw-r--r-- 1 root root 0 Jul 23 09:24 memory.max_usage_in_bytes
-rw-r--r-- 1 root root 0 Jul 23 09:24 memory.move_charge_at_immigrate
-r--r--r-- 1 root root 0 Jul 23 09:24 memory.numa_stat
-rw-r--r-- 1 root root 0 Jul 23 09:24 memory.oom_control
---------- 1 root root 0 Jul 23 09:24 memory.pressure_level
-rw-r--r-- 1 root root 0 Jul 23 09:24 memory.soft_limit_in_bytes
-r--r--r-- 1 root root 0 Jul 23 09:24 memory.stat
-rw-r--r-- 1 root root 0 Jul 23 09:24 memory.swappiness
-r--r--r-- 1 root root 0 Jul 23 09:24 memory.usage_in_bytes
-rw-r--r-- 1 root root 0 Jul 23 09:24 memory.use_hierarchy
-rw-r--r-- 1 root root 0 Jul 23 09:24 notify_on_release
-rw-r--r-- 1 root root 0 Jul 23 09:24 tasks
で、今回はmemory controllerでの実装をまとめる(というか、他のcontrollerでの挙動を教えて欲しい...)。
eventfd
とは...
何かしらのイベント通知を待ち受けるためのファイル記述子(fd)。普通のfdなので、read(2)などで読み取り可能になるのを待つことができるなど、一般のファイル経由で作ったfdと同様にプログラムの上で扱うことができる。
昔、パイプを使ってプロセス間での通知を試したことがあったが、eventfdの場合はカーネルからのイベントもユーザ空間で待ち受けることができる。
udzura.hatenablog.jp
基本的な使い方
- eventfdを作る ...(1)
- memoru cgroupのあるcgroupに紐づく(=そのcgroupのディレクトリ配下にある)、特定のファイルのfdを得る ...(2)。イベントごとに異なる
- (1)と(2)のfd、プラス場合によってその他のパラメータを同じcgroupに紐づく
cgroup.event_control
に書き込む
- 以上の手順により、(1)にイベントが登録される
- この手順を見て、「fdってプロセスごとにしか番号がユニークじゃないのにどうなってるんだろう?」と思っているんだけれど、カーネルレベルならプロセスIDとfdの番号だけでどういうファイルを掴んでいるかを特定できるという認識なので、頑張っているのだろう。ソース読まないといけない。
- ということでこの操作は同じプロセス内で行うのが基本っぽい
このように錬成されたeventfdをread()することで、イベントが発生したらデータを読み取ることができる。ブロッキングIOの場合は読み取り可能になるまで待つ。このようにeventfd自体は他のものと同様にファイル記述子なので、ノンブロッキングにしたり、poll(2)とかepoll(7)とかそういうやつで多重に待ち受けたり、色々と扱いやすいし、むやみに新しい概念を覚える必要なく使える。
どのようなイベントが存在するか?
カーネルのドキュメント によると現在の最新のカーネルでは3つ存在する模様。
- Memory thresholds より
"<event_fd> <fd of memory.usage_in_bytes> <threshold>"
というものを書き込む
- thresholdはバイト単位
- グループ内のメモリ利用量がそのスレッショルドに達したら通知される
- memswの方でも検知できる、同じようなインタフェース。
- OOM Control より
<event_fd> <fd of memory.oom_control>
を書き込む
- OOM がグループで怒ったら通知される
- root cgroup (
/
) では動かない
- memory.oom_control というファイルもある、ここに
1
を書き込むとOOMが無効になる
- 必要なメモリが不足すると、OOM-waitqueueにタスクが突っ込まれ、停止する
- 再び動かすには、Limitを大きくするか、メモリ利用状況を(タスクを殺すなどで)改善するしかない
- memory.oom_control を読み取ると、以下の二行の情報がわかる
oom_kill_disable
OOM が無効かどうか。0が有効、1が無効(二重否定感)
under_oom
1 であれば、OOMされるべきプロセスがあり、現在停止しているところ
- memory.oom_control oom_kill_disable = 1 にしてなおかつeventfdを登録しておけば、OOMでKillせずにcgroup設定値を自動で拡張するなどの対処が取れると思われるのだが、試した方が良さそう。 oom_control oom_kill_disable = 0 だとイベント登録しても殺されてしまう?
- Memory Pressure より
- "<event_fd> <level[,mode]>"` を書き込む
- level は三つある
"low"
: システムが新しいメモリ割り当てのためにReclaimを行った時
"medium"
: システムがスワップを作ったり、ページアウトを行ったりしている
"critical"
: システムがスラッシングしており、OOMが起こりそう、あるいはOOMのトリガーが走ろうとしている
- modeも三つある
"default"
: デフォルトの挙動。すなわちA->B->Cとcgroupがあった時、イベントがセットアップされた中で一番階層が低いcgroupにしか通知がされず、イベント登録がある階層までメモリプレッシャーの通知は伝播する
"hierarchy"
: A->B->Cとcgroupがあった時、Cでメモリプレッシャーがあったら必ずB→Aとイベント通知は伝播していく
"local"
: イベントの登録がそのものズバリのcgroupにない限り通知されない。例えば、A->B->Cとcgroupがあった時、CのメモリプレッシャーはCのイベントの有無に関係なくBより上には通知されない、デフォルトでは、Cにイベント登録がない場合、CのメモリプレッシャーはBに通知が行く
明日はこれらのイベントを実際にコードで取り扱ってみる。
参考
d.hatena.ne.jp
tenforward.hatenablog.com