LXCで学ぶコンテナ入門 -軽量仮想化環境を実現する技術

第46回 Linuxカーネルのコンテナ機能 ― cgroupの改良版cgroup v2[7]

この記事を読むのに必要な時間:およそ 6 分

プロセスが存在するcgroupへスレッドコントローラを登録

スレッド化サブツリーを作る方法はもう1つあります。

この方法では次の2つの手順が必要です。いずれも同じcgroupに対して操作を行います。

  • "domain"タイプであるcgroupでスレッドコントローラ有効にし,その子cgroupでもスレッドコントローラを有効にする(該当cgroupとその親cgroup両方のcgroup.subtree_controllにスレッドコントローラが登録された状態)
  • プロセスをcgroupに登録する

この手順の順番はどちらが先でも問題ありません。この結果は次のようになります。

  • 該当のcgroupは"domain threaded"なcgroupとなる
  • 上記の"domain threaded"なcgroup配下の"domain" cgroupは"domain invalid"となる

cgroup v2の当初の仕様では,すでにプロセスが登録されているcgroupのサブツリーでコントローラを有効にできませんでした。しかし,スレッド化サブツリーでは自身と子孫のcgroupでコントローラを有効にでき,スレッド化サブツリーを作成できます。

試してみましょう。

まずは先ほどの例と同様のツリーを作成します。そしてtdの親cgroup(ここの例ではroot cgroup)内のcgroup.subtree_controlファイルにcpuコントローラを登録します。cgroupでコントローラを有効化するあたりのお話は連載の第38回をご覧ください。

# mkdir -p td/th{01,02}
# tree -d .
.
└── td
    ├── th01
    └── th02

3 directories
# echo "+cpu" > cgroup.subtree_control 
# cat cgroup.subtree_control 
cpu
# cat td/cgroup.controllers 
cpu (tdでcpuコントローラが有効になった)

そしてcgroup tdにプロセスを登録します。

# echo $$ > td/cgroup.procs 
# cat td/cgroup.procs 
912
938
# cat td/cgroup.type
domain

この時点ではまだcgroup tdのタイプは"domain"です図4⁠。

図4 スレッドコントローラを登録して末端ではないcgroupにプロセスを登録

図4 スレッドコントローラを登録して末端ではないcgroupにプロセスを登録

ここでtdcgroup.subtree_controlファイルにCPUコントローラを登録し,tdの子孫cgroupでCPUコントローラが使えるようにしてみます。

# echo "+cpu" > td/cgroup.subtree_control 
# cat td/cgroup.subtree_control 
cpu
# cat td/th{01,02}/cgroup.controllers
cpu
cpu

図5 cgroup.subtree_controlファイルにスレッドコントローラを登録

図5 cgroup.subtree_controlファイルにスレッドコントローラを登録

tdcgroup.subtree_controlファイルにCPUコントローラが登録され,その子cgroupであるth01th02cgroup.controllersファイルにはcpuが登録されており,CPUコントローラが使える状態になっています。

ここでおもむろにtdcgroup.typeファイルを確認して,tdのcgroupタイプを確認してみましょう。

# cat td/cgroup.type 
domain threaded

プロセスが所属し,なおかつCPUコントローラが使える状態となったtdはdomain threadedになっています。その子cgroupであるth01th02のタイプを確認してみましょう。

# cat td/th{01,02}/cgroup.type
domain invalid
domain invalid

親となるtdが"domain threaded"となったので,元々domainであった子cgroup th01th02はいずれも"domain invalid"となっています。スレッド化サブツリーを完成させるには,この2つのcgroupをthreadedに変えましょう。

図6 tdがdomain threadedに変化

図6 tdがdomain threadedに変化

# echo "threaded" > td/th01/cgroup.type
# echo "threaded" > td/th02/cgroup.type
# cat td/th{01,02}/cgroup.type
threaded
threaded

th01th02がthreadedに設定されました。これでスレッド化サブツリーの完成です。

図7 スレッド化サプツリーの完成

図7 スレッド化サプツリーの完成

cgroup.typeファイルへの書き込みでスレッド化サブツリーを作成した際の例と同様にスレッドを登録してみましょう。

# pstree -p 928
threadtest(928)─┬─{threadtest}(929)
                └─{threadtest}(930)
# echo 928 > /sys/fs/cgroup/td/cgroup.procs 
# echo 929 > /sys/fs/cgroup/td/th01/cgroup.threads 
# echo 930 > /sys/fs/cgroup/td/th02/cgroup.threads 
(プロセスとスレッドを同一スレッド化サブツリーのそれぞれ別々のcgroupに登録)
# cat /sys/fs/cgroup/td/cgroup.procs 
893
928 (←登録できている)
939
# cat /sys/fs/cgroup/td/th01/cgroup.threads 
929 (←登録できている)
# cat /sys/fs/cgroup/td/th02/cgroup.threads 
930 (←登録できている)

親プロセスのPIDとそのプロセスに所属するスレッドが,同一のスレッド化サブツリーの別々のcgroupに登録できていることがわかります。

cgroup.threadsファイルへのタスクの登録権限

第40回で,cgroup v2を操作する際の権限についての説明をしました。

今回の実行例では,いずれもroot権限で操作を行っていますので,特に気にする必要はありません。しかし,一般ユーザ権限でプロセスやスレッドの操作を行う場合は,第40回での説明と同様に必要な権限を持っているかどうかを考える必要があります。

つまり,cgroup.threadsファイルへの書き込みには第40回で説明した,cgroupへのプロセス登録の際と同様の権限が必要です。

  • 書き込む先のcgroup.threadsファイルへの書き込み権
  • 移動元と移動先のcgroupの共通の祖先にあるcgroup.procsファイルへの書き込み権

そして,前回説明したとおり,同じプロセス内のスレッドは同じスレッド化サブツリーに所属する必要がありますので,

  • 移動元と移動先のcgroupは同じスレッド化サブツリー内に所属している必要がある

という条件が加わります。

root cgroupの扱い

これまでの例では,root cgroupの下に"domain threaded"となるcgroupを作成し,その配下に"threaded"なcgroupを作りました。

では,root直下に"threaded"なcgroupを作成できるのでしょうか? 試してみましょう。

root直下にth01というcgroupを作成し,"threaded"に変更します。

# mkdir /sys/fs/cgroup/th01
# cd /sys/fs/cgroup/
# echo threaded > th01/cgroup.type 
(root直下のth01をthreadedに変更する)
# cat th01/cgroup.type 
threaded
(threadedに変更された)

このth01に,これまでの実行例と同様のマルチスレッドプロセスを登録してみます。

# pstree -p 902
threadtest(902)─┬─{threadtest}(903)
                └─{threadtest}(904)
# echo 902 > th01/cgroup.procs (マルチスレッドプロセスをth01に登録)
# cat th01/cgroup.threads 
902
903
904
(プロセス内のタスクがすべて登録された)

このように何の問題もなく登録できました。つまりroot直下に"threaded" cgroupを作成できるということです。

ここでスレッドを1つrootに移動してみましょう。

# echo 904 > /sys/fs/cgroup/cgroup.threads 
(スレッドの1つをrootに移動)
# grep 904 cgroup.threads 
904 (rootに移動した)
# cat th01/cgroup.threads 
902
903
(th01には残りのタスクが残っている)

移動したスレッドだけがrootに移動し,残りのタスクはth01 cgroupに残っています。

つまり,root直下に"threaded" cgroupを作ると,root cgroupが"domain threaded"と同じ働きをします。そして"domain threaded"なcgroupが存在する場合と同様に操作できます。

root cgroupは"domain"と"threaded"なcgroupの親になります。このようにroot cgroupを例外的に扱っているのは,"threaded"なcgroupをroot直下に作ることでなるべく階層を浅くして,cgroup階層を走査するコストを下げるためです。

図8 root直下のcgroupを"threaded"に変更する

図8 root直下のcgroupを

もし"threaded"に変化したroot直下のcgroupが子cgroupを持っていた場合は,子cgroupは"domain invalid"に変化します。その子cgroupを使用するためには,そのcgroupを"threaded"に変更する必要があります。

# mkdir -p th02/th03
(2階層のcgroupを作成する)
# echo threaded > th02/cgroup.type 
(親cgroupをthreadedに変更する)
# cat th02/cgroup.type 
threaded
# cat th02/th03/cgroup.type 
domain invalid
(子cgroupはdomain invalidに変化する)

まとめ

今回はスレッド化サブツリーの操作について説明をしました。

スレッド化サブツリーを作成するには2つの方法がありました。

  • cgroup.typeファイルにthreadedという文字列を書き込む
  • プロセスが存在するcgroupへスレッドコントローラーを登録する

そして,スレッド化サブツリー内のプロセス,スレッドの操作方法や権限,root cgroupの例外的な扱いについても説明しました。

今回でcgroup v2のスレッドモードの説明は終わりです。前回と今回の説明で,スレッドモードがcgroup v2の仕様とうまく共存できるように作られていることが理解できたのではないでしょうか。

著者プロフィール

加藤泰文(かとうやすふみ)

2009年頃にLinuxカーネルのcgroup機能に興味を持って以来,Linuxのコンテナ関連の最新情報を追っかけたり,コンテナの勉強会を開いたりして勉強しています。英語力のない自分用にLXCのmanページを日本語訳していたところ,あっさり本家にマージされてしまい,それ以来日本語訳のパッチを送り続けています。

Plamo Linuxメンテナ

Twitter:@ten_forward
技術系のブログ:http://tenforward.hatenablog.com/