Linux 4.3 の Process Number Controller (1)
連載書いたり、勉強会で発表したりしているとなかなかブログが更新できませんね。久々の更新です。
これを書いている時点では Linux カーネルの 4.3-rc1 がリリースされていますが、久々に cgroup に新しいコントローラが追加されそうですね。
説明が不要なほどシンプルで分かりやすいです。このブログエントリ読まなくても使えるはず。:-)
軽く使ってみました。大体は上記ドキュメントに沿って試してるだけです。
カーネル
カーネルの config では Cgroup 関連の設定以下に "PIDs cgroup subsystem" という項目が新設されているので、ここを有効にしてカーネルを作成するだけです。
General setup ---> Control Group support ---> [*] PIDs cgroup subsystem
起動すると /proc/cgroups に pids が現れます。
# cat /proc/cgroups #subsys_name hierarchy num_cgroups enabled cpuset 1 1 1 cpu 2 1 1 cpuacct 3 1 1 blkio 4 1 1 memory 5 1 1 devices 6 1 1 freezer 7 1 1 net_cls 8 1 1 perf_event 9 1 1 net_prio 10 1 1 hugetlb 11 1 1 pids 12 2 1 debug 13 1 1
マウント
まずはマウントしてみましょう。単一階層構造でなく、現在の仕様の cgroup でも使えるようです。
マウントは他のコントローラと同じです。例えば
# mkdir -p /sys/fs/cgroup/pids # mount -t cgroup -o pids none /sys/fs/cgroup/pids
なかにはどんなファイルがあるかな? ルートは以下のような感じでした。
# ls /sys/fs/cgroup/pids/ cgroup.clone_children cgroup.sane_behavior pids.current tasks cgroup.procs notify_on_release release_agent
"pids.current" が PIDs サブシステム独自のファイルのようですね。
cgroup の作成
グループを作成してみます。ディレクトリを作ることに変わりはありません。
# mkdir /sys/fs/cgroup/pids/test01 # ls /sys/fs/cgroup/pids/test01 cgroup.clone_children notify_on_release pids.max cgroup.procs pids.current tasks
"pids.current" と "pids.max" という 2 つのファイルがこのコントローラの独自のファイルのようですね。
制限の設定
ファイル名を見ただけで大体わかると思いますが、それぞれのファイルの役割は以下です。
- pids.current
- 現在のプロセス(タスク)数
- pids.max
- 許可するプロセス(タスク)数の最大値
先に作った "test01" の作成直後、つまり初期値は以下のようになっています。
# cat /sys/fs/cgroup/pids/test01/pids.current 0 # cat /sys/fs/cgroup/pids/test01/pids.max max
"tasks" ファイルに何も追加していないので、当然現在値は 0 です。制限値なしにするには "pids.max" に "max" と書くようで、これが初期値です。
制限値を 2 にしてみましょう。
# echo 2 > /sys/fs/cgroup/pids/test01/pids.max # cat /sys/fs/cgroup/pids/test01/pids.max 2
きちんと設定されました。
現在のシェルをグループに追加します。
# echo $$ | tee /sys/fs/cgroup/pids/test01/tasks 5600 # cat /sys/fs/cgroup/pids/test01/pids.current 2
作成後に現在値を見ると、追加したシェルと実行した cat コマンドで 2 となっていますね。では、ここで 2 つ同時にコマンドを実行してみましょう。
# ( /bin/echo "Here's some processes for you." | cat ) bash: fork: retry: No child processes bash: fork: retry: No child processes bash: fork: retry: No child processes bash: fork: retry: No child processes bash: fork: Resource temporarily unavailable Terminated
シェルで 1 消費している状態で 2 つ実行しようとしたので、プロセスを生成できませんでした。
当たり前ですが、ちゃんと制限が効いていますね。
階層構造
上記の "test01" の子グループとして "test02" を作り、"test01" には制限をかけ、"test02" には制限をかけない状態にしてみます。
# mkdir /sys/fs/cgroup/pids/test01/test02 # echo 2 > /sys/fs/cgroup/pids/test01/pids.max # cat /sys/fs/cgroup/pids/test01/pids.max (test01の制限値は2) 2 # cat /sys/fs/cgroup/pids/test01/test02/pids.max (test02は無制限) max
以下のような設定です。
/sys/fs/cgroup/pids/ └── test01 --> (pids.max = 2) └── test02 --> (pids.max = max)
この状態で "test02" で "test01" の制限を超えるプロセスを起動してみます。
# echo $$ > /sys/fs/cgroup/pids/test01/test02/tasks (1 つ追加) # ( /bin/echo "Here's some processes for you." | cat ) bash: fork: retry: No child processes bash: fork: retry: No child processes bash: fork: retry: No child processes bash: fork: retry: No child processes bash: fork: Resource temporarily unavailable Terminated
起動しませんね。つまり複数の階層がある場合は、上位の階層の制限にひとつでも引っかかればダメということです。
制限を設定した状態でグループのタスクを増やす
"pids.max" を設定した状態で、グループの "tasks" にたくさんプロセスを追加していったらどうなるでしょう?
# echo 2 > /sys/fs/cgroup/pids/test01/pids.max # cat /sys/fs/cgroup/pids/test01/pids.max 2
"test01" に 2 の制限を設定しました。別途起動してあるプロセスを追加していってみましょう。
# echo 5954 > /sys/fs/cgroup/pids/test01/tasks # echo 5955 > /sys/fs/cgroup/pids/test01/tasks # echo 5956 > /sys/fs/cgroup/pids/test01/tasks #
おや、特にエラーにならずに、追加できてしまっています。
# cat /sys/fs/cgroup/pids/test01/tasks (追加したタスクは全部存在する) 5954 5955 5956 # cat /sys/fs/cgroup/pids/test01/pids.max (制限値は2に設定されている) 2 # cat /sys/fs/cgroup/pids/test01/pids.current (現在値は3で、制限値より多い) 3
つまり fork() や clone() で新しいプロセスを起動しようとする時にだけ制限が効くということですね。
存在するプロセス数以下の制限値を設定する
では、プロセスが既にグループに存在する状態で、その数以下に "pids.max" を設定してみます。
# echo 2 > /sys/fs/cgroup/pids/test01/pids.max (制限値は2) # cat /sys/fs/cgroup/pids/test01/tasks 5968 5970 # cat /sys/fs/cgroup/pids/test01/pids.current (現在2つのタスクが存在) 2
制限値以下であるふたつのタスクが存在していますが、ここで制限値を 1 に減らしてみましょう。
# echo 1 > /sys/fs/cgroup/pids/test01/pids.max (制限値を1に下げる) # cat /sys/fs/cgroup/pids/test01/tasks 5968 5970 # cat /sys/fs/cgroup/pids/test01/pids.current (タスクは2つのまま) 2
タスクは 2 つのままですね。
まとめ
Linux 4.3-rc1 環境で PIDs コントローラを試してみました。
- 制限値を設定しておくと、グループ内で fork() や clone() で新たに起動するプロセスを制限できる
- グループの "tasks" にプロセスを追加したり、制限値を変更したりしても、制限にひっかからない。つまり "pids.max" < "pids.current" となることもある
- グループに属することのできるタスク数は、そのグループの上位(祖先)のグループの制限を受ける
つづくかも...