前回の記事は、私が所属する会社のAdvent Calendarの5日目の記事でした。これまでは、この連載記事でAdvent Calendarに参加するのは1回だけでした。
今年はcgroup v2の話題を書こうと決めたときに、内容から考えて1回では済まない量になるだろうと思いました。そこで、続けて2回でcgroup v2の紹介をして、2つのAdvent Calendarに参加しようと決めました。1回目はちょうど社内で募集が始まっていた会社のAdvent Calendar、2回目は例年どおりLinux Advent Calendarに参加することにしました。
そういうわけで、今回の記事は昨年まで何度か参加していたLinux Advent Calendar 2017の19日目の記事です。
今回は、実際にcgroup v2を操作しながら、前回紹介したcgroup v2の特徴をおさらいしましょう。
今回の実行例は、cgroup v1、v2のマウントを自由に行うために、systemdが動いていない環境であるPlamo Linux 6.2の4.11.12カーネル上で実行しています。
前回書いたとおり、執筆時点で最新の4.14カーネルでは新しい機能が追加されています。新しい機能については後の回で紹介しますので、今回は少し前のバージョンである4.11カーネルを使用しています。4.13以降の新しいカーネルでは、今回の実行例とは実行結果が異なることがありますのでご注意ください。
cgroup v2のマウント
cgroup v2は、ファイルシステムのタイプとしてcgroup2
を指定してマウントします。今回試している4.11カーネルの時点では、マウントオプションはありません。
cgroup v1では、サブシステム(コントローラ)名やその他いくつかオプションが存在しましたが、cgroup v2では上の実行例のように指定するオプションもなく、シンプルにファイルシステムをマウントするだけです。
上の実行例のようにマウントした際にcgroup rootとなる、/sys/fs/cgroup
を確認してみましょう。
cgroup v2でコントロールを行うためのファイルが3つ現れており、cgroup v2がマウントされていることがわかります。
なお、4.13カーネルでcgroup namespace関連のマウントオプションがひとつだけ追加されました。この機能については後の回で紹介する予定です。
cgroup v2コアで使用するファイル
cgroup v2の各cgroup内に現れるファイルのうち、サブシステムと関係なく出現するファイルを紹介しておきましょう。それぞれのファイルの詳細については後で説明します。
表1 cgroup v2コアで使用するファイル
ファイル名 |
説明 |
cgroup.controllers |
そのcgroupで使えるサブシステム |
cgroup.procs |
そのcgroupに属するプロセスの登録、確認 |
cgroup.subtree_control |
子cgroupで有効にするサブシステム |
cgroup.events |
そのcgroupと子孫のcgroupにプロセスが所属しているかを確認する。また、所属状況が変化した場合に通知を受け取れる。root以外に出現 |
cgroupの作成
cgroupを作成する方法は、前回説明した通りcgroup v1と同じです。
"test01"というcgroupを作ってみましょう。cgroup v1と同様にmkdirコマンドを使います。
作成したcgroupの中のファイルを一覧してみると、rootよりはファイルがひとつ増えていますね。cgroup.events
というファイルです。これは前回紹介したcgroup v2の特徴のうち、「cgroup状態通知」に関係するファイルです。このファイルについては次回説明します。
cgroupの削除
cgroupを作成する方法だけでなく、削除方法もcgroup v1と同様にrmdir
コマンドで行います。上で作成した"test01"を削除してみましょう。
cgroupにプロセスが所属しておらず、子cgroupを持っていないcgroupのみ削除できるのも、cgroup v1と同じです。
cgroupへのプロセスの追加、削除、確認
使用するファイル
cgroup v1でタスクをcgroupに所属させる場合は、スレッドIDやプロセスIDをファイルに書き込みました。また、cgroupに所属しているタスクは、ファイルの中身を見ることで確認できました。このとき使うファイルは2種類ありました。
表2 cgroupに所属するタスクに関連するファイル
ファイル名 |
表示されるID |
cgroupのバージョン |
tasks |
スレッドID |
v1のみ |
cgroup.procs (※) |
プロセスID |
v1, v2 |
プロセスの追加
cgroup v2ではプロセス単位で追加を行いますので、tasks
ファイルはありません。cgroup.procs
ファイルを使って登録や確認を行います。
プロセスを登録する操作はcgroup v1と同じです。プロセスをcgroupに登録してみましょう。さきほどの例で作成した"test01"グループに登録します。
cgroup.procs
にカレントシェルのPIDを書き込み、ファイルの中身を表示させると、登録したPIDが表示されました。
プロセスの削除
プロセスの削除は、PIDを別のcgroupに登録することで行います。これもcgroup v1と同じですね。
上の例のように、PIDが3028のプロセスをroot(または他のcgroup)に登録すると、"test01"からは削除されます。
プロセスが所属するcgroupの確認
プロセスが所属しているcgroupの情報は、/proc/[PID]/cgroup
にあります("[PID]"にはプロセスのPIDが入ります)。
cgroup v1では、このファイルの中身は次のようになっています。
階層ごとに1行エントリが作成されます。各行はコロン区切りで「(階層を作成した順):(サブシステム名):(所属するcgroupパス)
」となります。ひとつの階層に複数のサブシステムが属している場合は、「(サブシステム名)」の部分はカンマ区切りでサブシステムが並びます。
上の例だと、PID:12314のプロセスは、cpuサブシステム用のツリーのみ/test01
に属しており、その他のツリーではrootに属していることがわかります。
cgroup v2では、次のようになります。
階層はひとつしかありませんので1行で、v1でサブシステム名が入っていた部分は何も入っていません。v2では番号は常に"0"で、サブシステム名の部分には何も入らず「0::(所属するcgroupパス)
」となります。
ちなみにv1とv2を共存させた場合、cgroup
ファイルの中身は次のようになります。
cgroup v2は必ず"0"でエントリが始まり、サブシステム名が空ですので、v1のエントリと区別がつきます。v1のエントリは"1"から始まりますのでバッティングすることもありません。
サブシステム(コントローラ)の制御
cgroup.controllersファイル
各cgroupにはcgroup.controllers
というファイルがあり、そのcgroupで使えるサブシステムがリストされます。
上の実行例ではroot cgroupのcgroup.controllers
ファイルを見ています。つまり、このシステム上のcgroup v2で使えるサブシステムが、io、memory、pids、rdmaの4つだということを表しています。
前回説明したように、cgroup v1で使われていないサブシステムのみがv2で使えますので、同じカーネルを使っていてもcgroup v1のマウント状況次第で、このファイルに現れるサブシステムは異なります。
図1は前回のサブシステム制御の解説部分で図2として載せた図を、今回の実行例に合わせて変えたものです。cgroup.controllers
ファイルは、この図で「有効なサブシステム」と書かれている部分に当たります。
cgroup.subtree_controlファイル
それでは、親cgroupから子cgroupのサブシステムを制御する方法を見ていきましょう。図1で「子で有効にするサブシステム」と表した部分に当たるファイルは、cgroup.subtree_control
というファイルです。
このファイルは、cgroup v2をマウントした直後や、cgroupを作成した直後は空です。
このファイルに、cgroup.controllers
ファイルに一覧されているサブシステムのうち、子cgroupで有効にしたいcgroupを登録します。また、子で有効にしているサブシステムの無効化もこのファイルで行います。
この有効にしたり、無効にしたりといった処理は、このファイルに書き込む事で行います。そして、このファイルの中身を見ることで、子cgroupで有効にしているサブシステムの確認を行えます。
子cgroupで使えるサブシステムの登録と削除は、次のような文字列をcgroup.subtree_control
ファイルに書き込むことで行います。
- サブシステムを子cgroupで使えるように有効化するには、サブシステム名の前に"+"をつける
- 子cgroupで有効化されているサブシステムを削除するには、サブシステム名の前に"-"(マイナス)をつける
また、次のように複数のサブシステムを登録、削除できます。
- 複数のサブシステムをスペース区切りで書けば、一度で有効化、無効化できる
- 複数回、サブシステムを登録、削除する文字列を書き込めば、複数のサブシステムを有効化、無効化できる
それでは実際にサブシステムが子cgroupで使えるように、登録と削除を行ってみましょう。
サブシステムの登録
まずはrootで使えるサブシステムのすべてを子cgroupで使えるように登録してみます。
上の例では、まずio, memory, pidsサブシステムを有効にした後、rdmaサブシステムを有効にしています。同時に指定して登録したサブシステムも、その後に別に登録したサブシステムも有効になっていることがわかります。
サブシステムの削除
この後、登録したサブシステムを削除するには、サブシステム名の前に "-"(マイナス)をつけてcgroup.subtree_control
ファイルに書き込みます。
子cgroupでのサブシステムの確認
では、ここで子cgroupを作成してみましょう。親cgroupのcgroup.subtree_control
ファイルで、子cgroupで使えるサブシステムを登録したわけですから、子cgroupのcgroup.controllers
ファイルには登録したサブシステムが現れているはずです。
上の例では、
- 親cgroupの
cgroup.subtree_control
で指定したサブシステムが、子cgroup("test01")内のcgroup.controllers
ファイルに出現している
- 親cgroupの
cgroup.subtree_control
で追加、削除したサブシステムが、子cgroup内cgroup.controllers
に現れたり消えたりしている
ことが確認できます。
上の例では"test01" cgroupのcgroup.controllers
ファイルで有効になっているサブシステムを確認しました。もちろん、"test01"ディレクトリには各サブシステム用のファイルが出現しています。
親cgroupでサブシステムを有効すると同時に、ディレクトリには有効化したサブシステムのファイルが出現し、逆に有効になっているサブシステムを親cgroupで無効化した場合は、サブシステム用のファイルもcgroupから消えます。
このように、あるcgroupのcgroup.controllers
ファイルに存在するサブシステムをcgroup.subtree_control
に登録して、子cgroupに分配していきます。
トップダウン制約
次に、上位のcgroupから許可されたサブシステム以外は、子孫のcgroupでは使えないことを確認してみましょう。
まずは、先に作成した"test01" cgroupに子cgroupを作成します。
上のように"test01" cgroupの子cgroupとして"test02"、"test03"を作成しました。そして、"test01"のcgroup.subtree_control
ファイルに+memory
と書き込み、子cgroupではmemoryサブシステムのみ使用できるように設定しました。"test02"、"test03"では確かにmemoryサブシステムのみ使えるようになっています。
ここでroot cgroupで、子cgroupでは使えるように設定されていない"rdma"を"test01"と"test02"に登録してみましょう。
このように、root cgroupでは配下のcgroupでrdmaサブシステムが使えるようには設定されていないので、root cgroup以外のcgroupでは、rdmaサブシステムをcgroup.subtree_control
ファイルに登録しようとするとエラーになります。
上位のcgroupからサブシステムが使えるように設定されていない限りは、子孫のcgroupでサブシステムを使ったリソースの分配はできないことが確認できました。
まとめ
冒頭で書いたように、cgroup v2を2回で紹介するつもりで今回の記事を書きはじめました。しかし、いざ書いてみると意外に分量が多くなり、2回では収まらなくなりました (^_^;)。
次回以降で、今回紹介できなかったcgroup v2の操作や、サブシステムについて紹介していく予定です。