cgroup を作成すると、そのディレクトリ配下にコア・インタフェースファイル(cgroup.procs など)が自動的に生成されます。コア・インタフェースファイルについては、以下の記事を参照してください。
一方、cpu.* や pids.* などのコントローラ・インタフェースファイルは、無条件に生成されるわけではなく、親 cgroup の cgroup.subtree_control でそのコントローラが有効化されている場合にのみ生成されます。ルート(/sys/fs/cgroup)の cgroup.subtree_control を確認すると、pids コントローラがあらかじめ有効になっていることが分かります。
今回作成した test cgroup はこのルートの直下に作成しているため、特別な操作をしなくても pids コントローラが有効な状態で引き継がれ、pids.* のコントローラ・インタフェースファイルが自動的に生成されます。
この章では、pids.current、pids.max、pids.peak、pids.events の4つのファイルを実際に操作しながら、それぞれの挙動を確認していきます。
3.1 実験
cgroup 内のプロセス数が上限(pids.max)に達すると、新しいプロセスを一切起動できなくなります。これは、状態確認用の cat や ls といった基本コマンドすら実行できなくなることを意味します。そのため、今回は制限の影響を受けない「確認用の別ターミナル」を同時に開いて実験を進めます。本記事では、root のターミナルと区別しやすいように確認用のターミナルは一般ユーザ(user1)でログインして使用します。以降は、操作用として root でログインしたターミナルと、状態確認用として一般ユーザ(user1)でログインしたターミナルの 2 つを使用します。
[user1@server ~]$
確認用ターミナルから、test cgroup に所属するプロセス数を確認します。まだ test cgroup にプロセスを移動していないため、test cgroup に所属するプロセス数(pids.current)の値は 0 です。
[user1@server ~]$ cat /sys/fs/cgroup/test/pids.current
0
test cgroup に移動する bash プロセスの PID を確認します。ここでは、bash プロセスの PID が 2563 であることが分かります。
[root@server ~]# echo $$
2563
実験の最後に、bash プロセスを元の cgroup に戻すため、現在 bash が所属している cgroup を確認します。ここでは、user.slice/user-0.slice/session-1.scope に所属していることが分かります。
[root@server ~]# cat /proc/$$/cgroup
0::/user.slice/user-0.slice/session-1.scope
bash プロセスを test cgroup に移動します。$$ は現在実行中の bash の PID を表しており、cgroup.procs に書き込むことで、bash プロセスを test cgroup に移動させることができます。
[root@server ~]# echo $$ > /sys/fs/cgroup/test/cgroup.procs
bashプロセスが所属するcgroupを確認すると、test cgroupに移動したことがわかります。
[root@server ~]# cat /proc/$$/cgroup
0::/test
bash プロセスがtest cgroup に移動したため、pids.current の値が 0 から 1 に変化したことが分かります。
[user1@server ~]$ cat /sys/fs/cgroup/test/pids.current
1
ここで、test cgroup に所属できる最大プロセス数を 3 に設定してみます。
[root@server ~]# echo 3 > /sys/fs/cgroup/test/pids.max
test cgroup に所属する bash プロセスから sleep プロセスを起動します。親プロセスである bash が test cgroup に所属しているため、子プロセスである sleep も自動的に test cgroup に所属します。
[root@server ~]# sleep 600 &
[1] 2606
プロセス数(pids.current)を確認すると、bash プロセスと sleep プロセスの合計 2 個になっていることが分かります。
[user1@server ~]$ cat /sys/fs/cgroup/test/pids.current
2
pkill コマンドを実行して sleep プロセスを終了します。
[root@server ~]# pkill sleep
[1]+ Terminated sleep 600
sleep プロセスを終了したため、test cgroup に所属するプロセスが 2 個から 1 個に減少したことが確認できます。
[user1@server ~]$ cat /sys/fs/cgroup/test/pids.current
1
pids.peak は、その cgroup に所属していたプロセス数が過去に到達した最大値を表します。この実験では、bash、sleep、および sleep を終了するために起動した pkill プロセスが同時に存在したため、最大値は 3 となっています。
[user1@server ~]$ cat /sys/fs/cgroup/test/pids.peak
3
再度、test cgroup に所属する bash プロセスからsleepプロセスを2つ起動します。
[root@server ~]# sleep 600&
[1] 2616
[root@server ~]# sleep 800&
[2] 2617
プロセスの親子関係を確認します。別のターミナルで確認することで、test cgroup に新たなプロセスを作成せずに状態を確認できます。2 つの sleep プロセスは、いずれも test cgroup に所属する bash プロセス(PID: 2563)から起動していることが確認できます。
[user1@server ~]$ ps -C sleep -o comm,pid,ppid
COMMAND PID PPID
sleep 2616 2563
sleep 2617 2563
ps コマンドの詳しい使い方は、以下のページをご覧ください。
hana-shin.hatenablog.com
この時点でのプロセス数(pids.current)は、上限の 3 になります。
[user1@server ~]$ cat /sys/fs/cgroup/test/pids.current
3
test cgroupに所属する最大のプロセス数は 3 のままであることがわかります。
[user1@server ~]$ cat /sys/fs/cgroup/test/pids.peak
3
新たに sleep を1つ起動します。しかし、test cgroup に移動した bash プロセスと、bash から起動した2個の sleep プロセスの合計3個のプロセスが、すでに test cgroup に存在しています。そのため、新たに sleep を起動することができず、以下のエラーメッセージが出力されています。
[root@server ~]# sleep 900&
-bash: fork: retry: リソースが一時的に利用できません
-bash: fork: retry: リソースが一時的に利用できません
-bash: fork: retry: リソースが一時的に利用できません
^C-bash: fork: システムコール割り込み
pids.events を確認します。max 7 は、pids.max に設定した上限に達したため、新しいプロセスの作成が拒否された回数を表しています。今回の環境では、sleep 900 & を起動しようとした際に、bash が内部的に複数回 fork() を再試行したため、1 回のコマンド実行であっても拒否回数が 7 回として記録されています。
[user1@server ~]$ cat /sys/fs/cgroup/test/pids.events
max 7
次に、test cgroup に所属する bash プロセスから ls コマンドを実行してみます。この時点で test cgroup には、bash プロセスと 2 個の sleep プロセスの合計 3 個のプロセスがすでに存在しています。pids.max に設定した上限は 3 のため、新たに ls プロセスを生成しようとすると上限を超えてしまいます。そのため、bash は fork() に失敗し、以下のエラーメッセージが出力されます。
[root@server ~]# ls
-bash: fork: retry: リソースが一時的に利用できません
-bash: fork: retry: リソースが一時的に利用できません
-bash: fork: retry: リソースが一時的に利用できません
^C-bash: fork: システムコール割り込み
pids.events は、pids.max に設定した上限に達したことにより、新しいプロセスの作成が拒否された回数を記録するファイルです。本実験では、sleep や ls の起動に失敗した回数が max に累積されていることが確認できます。
[user1@server ~]$ cat /sys/fs/cgroup/test/pids.events
max 10
3.2 後始末
root で実行していたターミナルは、test cgroup のプロセス数が上限に達しているため、新たなコマンドを実行できません。そのため、まずは確認用のターミナル(user1)から sudo pkill sleep コマンドを実行して sleep プロセスを終了させ、test cgroup にプロセス数の空きを作ります。
[user1@server ~]$ sudo pkill sleep
sleep プロセスが終了したため、test cgroup に所属しているプロセスは bash プロセス(PID: 2563)のみ(計1個)となり、root のターミナルでも再びコマンドが実行できるようになります。
[user1@server ~]$ cat /sys/fs/cgroup/test/cgroup.procs
2563
実験前に確認しておいた元の cgroup に bash プロセスを戻します。これにより、bash プロセスは test cgroup から抜け、もとのセッション cgroup に再所属します。
[root@server ~]# echo $$ > /sys/fs/cgroup/user.slice/user-0.slice/session-1.scope/cgroup.procs
bash プロセスが所属する cgroup を確認すると、元の user.slice/user-0.slice/session-1.scope に戻っていることが確認できます。
[root@server ~]# cat /proc/$$/cgroup
0::/user.slice/user-0.slice/session-1.scope