cgroup v2がstableとされた4.
4.
今回は、この連載の各回を書いた時点では存在していなかったため紹介しておらず、その後追加された細かな機能をいくつか紹介します。
favordynmodsオプション
まずは、cgroup v2を使う場合に指定できるマウントオプションfavordynmodsです。
favordynmodsオプションを指定すると、タスクのcgroup間の移動やコントローラのオン・
Androidでは、タスクがcgroup間を移動する処理が一般的な操作であるために、その際のコストを削減するために追加されたようです。
一般的に、起動したコンテナやプロセスは、デフォルトでは親プロセスと同じcgroupに配置されますので、親プロセスと異なるcgroupに所属させたい場合は移動させなければなりません。このオプションを指定すると、このときのレイテンシを下げられるでしょう。
ただし、5.clone3システムコールを使うと任意のcgroupに所属させた状態でプロセスを生成できます。このため、clone3システムコールを使って、直接目的のcgroupにプロセスを配置する場合はレイテンシ削減の効果はありません。
4.
カーネルビルド時のconfigでCONFIG_を有効y)n)
6.cgroup_をtrueに設定すると、この機能を有効にできます。前述のように、カーネルビルド時に有効化されている場合は、このパラメータをfalseに設定すれば無効化できます。
cgroup.events
ここからは、cgroup内に存在するファイルについてのお話です。
第39回で、cgroupの状態を通知する機能を説明する際に、cgroup.ファイルについて説明しました。このときは4.cgroup.ファイルに含まれる項目はpopulated1つだけでした。
その後、5.frozenという項目が追加されました。このあとの説明で使いますので、ここであらためてcgroup.ファイルについて紹介します。
cgroup.ファイルには、表1のとおり2つの項目があります。
| 項目名 | 説明 | 実装されたバージョン |
|---|---|---|
populated |
自身のcgroupとその子孫のcgroup内にプロセスが存在するかどうか。cgroupにプロセスが存在するときは1、プロセスが存在しないときは0 | 4. |
frozen |
自身のcgroupがフリーズ |
5. |
このファイルには2つの機能があります。
まず、ファイルの内容を読み取ることで、自身のcgroupおよびその子孫cgroup内のタスクの存在状況や、自身のcgroupのfreezerコントローラの状態を確認できます。
次に、このファイルを監視することで、自身と子孫cgroupの状態が変化した通知を受け取れます。
cgroup.ファイルはroot cgroup以外に存在します。root cgroupには常にタスクが存在し、またroot cgroupではfreezerコントローラを使えないため、root cgroupに存在する意味がないからです。
第39回では、cgroup.ファイルのうち、populatedについて値の変化や値が変化したときの通知について説明していますので、populatedについてはそちらをご覧ください。
frozenについては、このあとfreezerコントローラの説明で実行例を紹介します。
freezerコントローラとcgroup.freeze
cgroup v2では、cgroupと子孫のcgroupに所属するタスクをすべてフリーズ
また、先に紹介したcgroup.ファイルを用いてフリーズ状態を監視できます。
cgroup v1にもfreezerというコントローラが存在しました。cgroup v2リリース時点ではfreezerコントローラは使えず、使えるようになったのは5.cgroup.ファイルからfreezerコントローラの機能を利用します。
cgroup v2では、cgroupのコア機能に関係するファイルの名前はcgroup.で始まります。ファイル名からわかるように、この機能はコントローラではなくcgroupコアの機能として位置づけられるようになりました。
| ファイル名 | 機能 | 可能な操作 | 実装されたバージョン |
|---|---|---|---|
cgroup. |
cgroup 内と子孫 cgroup 内のすべてのタスクを一時停止・ |
読み書き | 5. |
ここからはcgroup.ファイルの動きを見てみましょう。
シェルを起動してcgroupを作成し、そのシェルのプロセスをcgroupに登録します。その後、cgroup.ファイルに1を書き込みます。
$ sudo mkdir /sys/fs/cgroup/test01 (test01 cgroupの作成) $ echo $$ | sudo tee /sys/fs/cgroup/test01/cgroup.procs (シェルをcgroupに登録) 1060 $ echo 1 | sudo tee /sys/fs/cgroup/test01/cgroup.freeze (cgroup.freezeに1を書き込みフリーズ状態にした) (ここで改行を入力しようとしているがシェルの反応がなくなる)
cgroup.ファイルに1を書き込むとシェルの応答がなくなります。たとえば、キーボードから文字を入力しようとしても応答がありません。
このとき、別のシェルからtest01 cgroupのcgroup.ファイルを確認してみます。
$ cat /sys/fs/cgroup/test01/cgroup.events populated 1 frozen 1
cgroup.ファイルのfrozenの項目が1になっているはずです。
この状態で、別のシェルからtest01 cgroupのcgroup.ファイルに0を書き込みます。
$ echo 0 | sudo tee /sys/fs/cgroup/test01/cgroup.freeze 0
すると、さきほど応答のなかったシェルがふたたび応答するようになります。
$ echo 1 | sudo tee /sys/fs/cgroup/test01/cgroup.freeze(freeze前に実行したコマンド) (さきほどcgroup.freezeに1を書き込んだあと改行を入力したが、応答がなくなっていた) 1 (シェルの実行が再開され、teeコマンドの出力が表示される) $ (freezeのあとに入力した改行に対する応答がある) $
親子関係にあるcgroupに所属する複数のタスクが同時にフリーズ状態になることも確認しておきましょう。
$ openssl speed -multi $(grep processor /proc/cpuinfo|wc -l)
このようにopensslコマンドでCPUを激しく使うようなタスクを複数起動します。ここで検証に使っている環境はCPUが2つのホストです。別シェルでpsコマンドで確認すると、次のようにopensslのタスクが2つ、CPUを消費していることがわかります
$ ps auxf | grep openssl
karma 1187 0.0 0.3 9660 6144 pts/1 S+ 14:51 0:00 | \_ openssl speed -multi 2
karma 1188 100 0.1 9788 3460 pts/1 R+ 14:51 0:09 | \_ openssl speed -multi 2
karma 1189 99.4 0.1 9788 3716 pts/1 R+ 14:51 0:09 | \_ openssl speed -multi 2
(PID 1188と1189が実行中でCPUを消費していることがわかる)
$ grep State /proc/{1188,1189}/status
/proc/1188/status:State: R (running)
/proc/1189/status:State: R (running)
プロセスの状態の列を見るとR+となっており、プロセスは実行中であることが確認できます。/proc/[PID]/statusファイルからも実行中であることがわかります[3]。
それでは、この2つのプロセスの一方を親であるtest01 cgroupに、もう一方を子であるtest02 cgroupに所属させます。
$ echo 1188 | sudo tee /sys/fs/cgroup/test01/cgroup.procs (PID:1188は親cgroupであるtest01に所属させる) 1188 $ echo 1189 | sudo tee /sys/fs/cgroup/test01/test02/cgroup.procs (PID:1189は子cgroupであるtest02に所属させる) 1189
ここで、test01 cgroupのcgroup.に1を書き込みます。
$ echo 1 | sudo tee /sys/fs/cgroup/test01/cgroup.freeze
1
$ cat /sys/fs/cgroup/test01/cgroup.events
populated 1
frozen 1 (frozenが1に変化した)
$ grep State /proc/{1188,1189}/status
/proc/1188/status:State: S (sleeping)
/proc/1189/status:State: S (sleeping)
(runningだったタスクがsleepingに変化している)
cgroup.ファイルに1を書き込むと、test01 cgroupおよびその子であるtest02 cgroupがフリーズ状態になり、先ほどまで実行中だったプロセスの状態が両方ともsleepingに変化したことが確認できます。
このとき、/proc/[PID]/schedファイル内のタスクが実際に実行された時間を表示する項目se.)
$ while :; do sleep 1; grep sum_exec_runtime /proc/1188/sched; done :(略) se.sum_exec_runtime : 478584.436171 se.sum_exec_runtime : 479586.493546 se.sum_exec_runtime : 480585.942886 (ここまでは値が増加している) se.sum_exec_runtime : 481361.444251 (このあたりでcgroup.freezeに1を書き込んだ) se.sum_exec_runtime : 481361.444251 (このあと値が増加しない) se.sum_exec_runtime : 481361.444251 :(略)
cgroup.ファイルに0を書き込むことで、タスクの実行が再開されます。
$ echo 0 | sudo tee /sys/fs/cgroup/test01/cgroup.freeze
$ cat /sys/fs/cgroup/test01/cgroup.events
populated 1
frozen 0 (frozenが0になる)
$ grep State /proc/{1188,1189}/status
/proc/1188/status:State: R (running)
/proc/1189/status:State: R (running)
(タスクの実行が再開されている)
このとき、inotifywaitコマンドでcgroup.ファイルを監視すると、frozenが1になった時点で通知が受信できます。
$ inotifywait -m /sys/fs/cgroup/test01/cgroup.events Setting up watches. Watches established. /sys/fs/cgroup/test01/cgroup.events MODIFY (cgroup.freezeに1を書き込んだ) /sys/fs/cgroup/test01/cgroup.events MODIFY (cgroup.freezeに0を書き込んだ)
ここまで見たように、cgroupをフリーズ状態にすると、その子孫のcgroupもフリーズ状態になります。しかし、子孫のcgroupを直接フリーズ状態にしても、自身のcgroupがフリーズ状態ではない場合は、cgroup.ファイルのfrozenは0のままです。
$ cat /sys/fs/cgroup/test01/cgroup.events | grep frozen frozen 0 $ cat /sys/fs/cgroup/test01/test02/cgroup.events | grep frozen frozen 0 (test01, test02 cgroupともにフリーズ状態ではない) $ echo 1 | sudo tee /sys/fs/cgroup/test01/test02/cgroup.freeze (test02 cgroupをフリーズ状態にする) 1 $ cat /sys/fs/cgroup/test01/cgroup.events | grep frozen frozen 0 (test01 cgroupはフリーズ状態ではない) $ cat /sys/fs/cgroup/test01/test02/cgroup.events | grep frozen frozen 1
cgroup.ファイルのpopulatedが子孫を考慮した値になっているのに対し、同じファイル内のfrozenは自身の状態のみを表示していることに注意してください。
フリーズ状態になっているcgroupには、タスクを追加することができます。
$ echo 1 | sudo tee /sys/fs/cgroup/test01/cgroup.freeze 1 $ echo $$ | sudo tee /sys/fs/cgroup/test01/cgroup.procs 1172 (シェルの反応がなくなった)
上の実行例は、まずtest01 cgroupをフリーズ状態にしたあとに、そのフリーズ状態にあるtest01 cgroupにシェルのプロセスを登録しました。すると、登録した直後にシェルからの応答がなくなりました。
ここまで、cgroup.ファイルとcgroup.ファイルを使ったfreezerコントローラ機能の紹介でした。
cgroup.kill
cgroup.ファイルを使うと、cgroup内とその子孫cgroup内のプロセスすべてにSIGKILLシグナルを送って、プロセスをkillできます。
| ファイル名 | 機能 | 可能な操作 | 実装されたバージョン |
|---|---|---|---|
cgroup. |
cgroup内と子孫cgroup内のすべてのタスクを強制終了させる | 書き込み専用 | 5. |
たとえば、複数のプロセスが動いているコンテナが不安定になったために、コンテナ内のプロセスをすべて同時に停止させるような場合に使えます。
それでは、cgroup.ファイルの動きを見てみましょう。
cgroup.ファイルには1以外の数値は書き込めません。また、書き込み以外の処理はできません。1以外の値を書き込もうとしたり、ファイルを読もうとしたりするとエラーが発生します。
$ echo 5 | sudo tee /sys/fs/cgroup/test01/cgroup.kill 5 tee: /sys/fs/cgroup/test01/cgroup.kill: Numerical result out of range (cgroup.killに5を書き込もうとしたらエラーになった) $ cat /sys/fs/cgroup/test01/cgroup.kill cat: /sys/fs/cgroup/test01/cgroup.kill: Permission denied (cgroup.killファイルを読もうとしたらエラーになった)
test01 cgroupとその子cgroupとしてtest02を作成します。そしてプロセスを複数起動させ、test01とtest02 cgroupに所属させます。
$ sudo mkdir -p /sys/fs/cgroup/test01/test02 $ sleep 1800 & [1] 1114 $ sleep 1800 & [2] 1115 $ sleep 1800 & [3] 1116 $ echo 1114 | sudo tee /sys/fs/cgroup/test01/cgroup.procs 1114 $ echo 1115 | sudo tee /sys/fs/cgroup/test01/cgroup.procs 1115 $ echo 1116 | sudo tee /sys/fs/cgroup/test01/test02/cgroup.procs 1116
上の実行例では、sleepコマンドを3つ実行し、そのうちの2つを親cgroupであるtest01 cgroupに、1つを子cgroupであるtest02 cgroupに所属させました。
そして、親cgroupであるtest01 cgroup内のcgroup.ファイルに1を書き込みます。
$ echo 1 | sudo tee /sys/fs/cgroup/test01/cgroup.kill 1 [1] Killed sleep 1800 [2]- Killed sleep 1800 [3]+ Killed sleep 1800 $
するとtest01とtest02 cgroupにいるsleepプロセスがすべてkillされました。
cgroup.ファイルはプロセス単位での操作にのみ対応しており、第45回、第46回で説明した、スレッドモードのスレッド化サブツリー内ではEOPNOTSUPPでエラーになります。
まとめ
今回は、これまで連載では紹介していなかったcgroup v2の機能を紹介しました。
cgroup v2がstableになって以降も、さまざまな変更が加わっています。この連載で紹介したあとにも機能が追加されており、まだ紹介できていない機能があります。
今後も、時々はそのような機能を取り上げて紹介したいと思います。
