cgroup v2 では、自分や子孫の cgroup に変化があった場合に cgroup.events ファイルに、メモリーに関係するイベント(OOM Killer発動など)が発生したときは memory.events と memory.events.local ファイルに、そのイベントの回数が記録されます。
cgroup.events ファイルについては、私の連載第39回に説明がありますので、そちらを参照してください。
memory.events、memory.events.local ファイルについては第56回で簡単に説明しています(詳細な説明は、連載では、このブログ記事を書いてる時点ではありません)。
cgroup.events ファイルに関する連載記事で書いたように、このファイルの中身が変わったとき(=イベント発生時)に inotify(dnotify も?) や poll で通知が受け取れます。カーネル付属ドキュメントには記載はないですが、memory.events{,.local} でも同様に通知が受け取れます。
inotify については、連載記事をご覧いただくとして、poll についてはどうなんだろう? と思って調べてみました。
まずは、忘れがちな man の確認です。cgroup 関連は意外に cgroups(7) が役に立ちます。man 7 cgroups で調べましょう。
"Cgroups v2 cgroup.events file" というセクションに説明があります。
The cgroup.events file can be monitored, in order to receive notification when the value of one of its keys changes. Such monitoring can be done using inotify(7), which notifies changes as IN_MODIFY events, or poll(2), which notifies changes by returning the POLLPRI and POLLERR bits in the revents field.
poll() で監視している場合、revents フィールドが POLLPRI と POLLERR ビットを返すことで監視できるとあります。
poll(2) の方には、POLLPRI の部分に次のような説明があります。
POLLPRI
There is some exceptional condition on the file descriptor. Possibilities include:
• There is out-of-band data on a TCP socket (see tcp(7)).
• A pseudoterminal master in packet mode has seen a state change on the slave (see ioctl_tty(2)).
• A cgroup.events file has been modified (see cgroups(7)).
man の説明はいずれも cgroup.events ファイルについてですが、同様に memory.events{,.local} ファイルでも利用できるはずです。
試してみました。poll() なんて使うプログラミングをしたことはない素人なので、ここは AI に書いてもらいました。
#!/usr/bin/env python3 import select import sys def monitor_cgroup_event(filepath): """poll()を使ってcgroupイベントファイルを監視""" with open(filepath, 'r') as f: # 初期値を読み取って表示 initial = f.read() print(f"Initial value:\n{initial}") f.seek(0) # poll()オブジェクトを作成 poller = select.poll() # POLLPRIイベントを監視(cgroup v2の変更通知) poller.register(f.fileno(), select.POLLPRI) print(f"Monitoring {filepath} with poll()...") print("Waiting for events (Ctrl+C to exit)...\n") while True: # タイムアウトなしで待機(イベントが来るまでブロック) events = poller.poll() for fd, event in events: if event & select.POLLPRI: # ファイルを最初から読み直す f.seek(0) content = f.read() print(f"Event detected at {filepath}:") print(content) print("-" * 40) if __name__ == "__main__": if len(sys.argv) != 2: print(f"Usage: {sys.argv[0]} <cgroup-event-file>") print(f"Example: {sys.argv[0]} /sys/fs/cgroup/memory.events") sys.exit(1) monitor_cgroup_event(sys.argv[1])
こんな感じで memory.events ファイルを監視します。
$ sudo python3 monitor_cgroup.py /sys/fs/cgroup/test01/test011/memory.events
これで、test011 cgroup で memory.max を設定し、それを超えるメモリ消費をさせると、OOM Killer が発動し、この監視プログラムが memory.events ファイルからイベントを受け取ります。
$ ./polltest.py /sys/fs/cgroup/test01/test011/memory.events Initial value: low 0 high 0 max 0 oom 0 oom_kill 0 oom_group_kill 0 Monitoring /sys/fs/cgroup/test01/test011/memory.events with poll()... Waiting for events (Ctrl+C to exit)... Event detected at /sys/fs/cgroup/test01/test011/memory.events: low 0 high 0 max 1 oom 0 oom_kill 0 oom_group_kill 0 :(略) ---------------------------------------- Event detected at /sys/fs/cgroup/test01/test011/memory.events: low 0 high 0 max 286 oom 1 oom_kill 1 oom_group_kill 0 :(略)
監視できました。