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

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

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

前回は,4.14カーネルでcgroup v2に導入されたスレッドモードの概要を説明しました。今回は,実際にスレッド化サブツリーを作成して操作を行ってみましょう。

今回の実行例は,デフォルトではcgroupをマウントしないPlamo Linux 7.2環境で試しています。systemdを採用したディストリビューションの場合,カーネルの起動パラメータにcgroup_no_v1=allと指定して,すべてのコントローラがcgroup v2から使えるようにするとcgroup v2の機能が試しやすいでしょう。

そして,今回の実行例はいずれもrootユーザで実行しています。

スレッド化サブツリーの操作

それでは早速,cgroup v2のツリー中でどのようにスレッド化サブツリーを作り,操作していくのかを見ていきましょう。

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

cgroup.typeファイルへの書き込み

まずは1つめの方法です。

シンプルにcgroup.typeファイルにthreadedという文字列を書き込むだけです。これまで説明してきたcgroupの操作方法をご存知であれば想像がつく方法かと思います。

この結果次のようになります。

  • threadedという文字列を書き込んだcgroupのタイプは"threaded"になる
  • threadedという文字列を書き込んだcgroupの親cgroup"domain threaded"になる
  • threadedという文字列を書き込んだcgroupの親cgroup配下の"threaded"以外のcgroupは"domain invalid"になる

つまり,threadedと書き込んだcgroupの親cgroup配下が,前回説明したスレッド化サブツリーとなるわけです。前回説明したように,スレッド化サブツリー内はすべて"threaded"なcgroupでなければ使えませんので,"domain"だったcgroupは"domain invalid"に変化するという動きになっています。

できあがったスレッド化サブツリー内の"domain invalid"な状態のcgroupすべてにthreadedという文字列を書き込んでいかないと,スレッド化サブツリーが完成しないのは面倒ですね。これは将来的にスレッドモードの機能を拡張して,動作を変える必要が出てきた場合に備えてこのようになっているようです。

この動きを確認してみましょう。

cgroup v2のroot配下にtdというディレクトリを,td配下にth01th02というcgroupを2つ作成します。

# mount -t cgroup2 cgroup2 /sys/fs/cgroup/
(cgroup v2のマウント)
# mkdir -p td/th{01,02}
(tdとその配下にth01,th02というcgroupを作成)
# tree -d /sys/fs/cgroup/
/sys/fs/cgroup/
└── td
    ├── th01
    └── th02

3 directories

作成した3つのcgroup内のプロセスとコントローラファイルの中身を確認します。

# cat td/cgroup.procs 
# cat td/cgroup.controllers 
# cat td/th0{1,2}/cgroup.procs
# cat td/th0{1,2}/cgroup.controllers
(作成直後なのでプロセスもコントローラも表示されない)

まだ作っただけでプロセスの登録もコントローラの登録も行っていませんので,すべて空の状態です。

それぞれのcgroup.typeファイルも確認しておきましょう。

# find /sys/fs/cgroup -name "cgroup.type" -exec cat {} \;
domain
domain
domain
(作成したいずれのcgroupのタイプもdomain)

いずれのcgroupも初期状態ですのですべて"domain"となっています図1⁠。

図1 作成直後ですべてdomainの状態

図1 作成直後ですべてdomainの状態

この状態でth01を"threaded"としてみましょう。

# echo "threaded" > /sys/fs/cgroup/td/th01/cgroup.type 
(th01をthreadedに変更)
# cat /sys/fs/cgroup/td/th01/cgroup.type 
threaded
(threadedに変更されている)

threadedになりましたね。他のcgroupがどうなっているのかも見てみましょう。

# cat /sys/fs/cgroup/td/cgroup.type 
domain threaded
(th01の親cgroupはdomain threadedに変化している)
# cat /sys/fs/cgroup/td/th02/cgroup.type 
domain invalid
(td配下のcgroupはdomain invalidに変化している)

"threaded"であるth01の親cgroupであるtdはスレッド化サブツリーのrootである"domain threaded"に,兄弟cgroupであるth02は"domain invalid"となりました。

つまり"threaded cgroup"の親は"domain threaded"に,スレッド化サブツリー内のdomain cgroupは"invalid"になります図2⁠。

図2 スレッド化サブツリーができあがった直後

図2 スレッド化サブツリーができあがった直後

このままではth02は使えない状態のままです。th02を機能させるためには"threaded"にする必要があります。

# echo "threaded" > /sys/fs/cgroup/td/th02/cgroup.type 
# cat /sys/fs/cgroup/td/th02/cgroup.type 
threaded

これでth02がthreadedになり,使える状態になりました。プロセスやスレッドが追加できます図3⁠。

図3 domain invalidのcgroupをthreadedに変更(スレッド化サブツリーの完成)

図3 domain invalidのcgroupをthreadedに変更(スレッド化サブツリーの完成)

スレッド化サブツリー内へのタスクの追加

2つめの方法を見る前に,スレッド化サブツリー内へのプロセスやスレッドの追加を見ておきましょう。

スレッド化サブツリーにマルチスレッドプロセスを登録させるには,まずはプロセスをcgroup.procsに登録する必要があります。"threaded"や"domain threaded"のcgroupであっても,いきなりcgroup.threadsにプロセスやスレッドのIDは登録できません。

まずはマルチスレッドプロセスを起動してみます。次のように2つのスレッドを持つプロセスですpstreeコマンドではスレッドは中かっこ{}で囲われます⁠⁠。

# pstree -p 907
threadtest(907)─┬─{threadtest}(908)
                └─{threadtest}(909)

このマルチスレッドプロセスの親となるプロセスのPID(PPID)である907図3で作ったcgroup tdに登録してみます。

# echo 907 > /sys/fs/cgroup/td/cgroup.procs
(PPIDをdomain threadedのcgroup.procsに登録)
# cat /sys/fs/cgroup/td/cgroup.procs 
907
(cgroup.procsにはプロセスのIDとなるPPIDのみ登録されている)
# cat /sys/fs/cgroup/td/cgroup.threads 
907
908
909
(cgroup.threadsには同じプロセスに属するスレッドも含めて登録されている)

PPIDの907cgroup.procsに登録すると,cgroup.procsにはそのPIDが登録され,cgroup.threadsにはその子となるスレッドのID(TID)である908909が登録されています。

cgroup.procsへの登録は"domain threaded'であるcgroupへの登録である必要はなく,いきなり"threaded"であるth01th02への登録でも構いません。次の操作は"threaded"であるth01へマルチスレッドプロセスを登録している例です。

# echo 907 > /sys/fs/cgroup/cgroup.procs
(前の例の後に実行するために一度プロセスをrootに戻す)
# echo 907 > /sys/fs/cgroup/td/th01/cgroup.procs 
(threadedであるth01へマルチスレッドプロセスを登録)
# cat /sys/fs/cgroup/td/th01/cgroup.threads 
907
908
909
(cgroup.threadsには同じプロセスに属するスレッドも含めて登録されている)

上の例で,cgroupに登録されたプロセスとスレッドのIDを確認するためにcgroup.threadsの内容を見ています。これは"threaded"なcgroupのcgroup.procsファイルは読み取りできないからです。cgroup.procsを読もうとすると次のようにエラーになります。

"domain threaded"であるcgroupのcgroup.procsは先の例で実行したように読み取れます。

# cat /sys/fs/cgroup/td/th01/cgroup.procs 
cat: /sys/fs/cgroup/td/th01/cgroup.procs: Operation not supported

この例ではマルチスレッドプロセスのPPIDを登録しました。代わりにスレッドのIDをcgroup.procsに登録しても同じ結果となります。上の例のマルチスレッドプロセス内のスレッドのTIDである909cgroup.procsに登録してみましょう。

# echo 909 > /sys/fs/cgroup/td/cgroup.procs 
(マルチスレッド内のスレッドのIDをcgroup.procsに登録)
# cat /sys/fs/cgroup/td/cgroup.procs 
907
(cgroup.procsにはPPIDが登録されている)
# cat /sys/fs/cgroup/td/cgroup.threads 
907
908
909
(cgroup.threadsにはマルチスレッドプロセスのPIDとTIDが登録されている)

このように,マルチスレッドプロセス内のスレッドのTIDを登録しても,cgroup.procsではPPIDが表示され,プロセス内のスレッドすべてがcgroup.threadsに表示されていることがわかります。

先に「まずはプロセスをcgroup.procsに登録する必要があります」と書きました。これを確認しておきましょう。

cgroup.procsではなく,いきなりcgroup.threadsに登録を試みます。先の例に続けて実行する場合は,先と同様に一度プロセスをrootに戻して試してください。

# echo 907 > /sys/fs/cgroup/cgroup.procs
(前の例の後に実行するために一度プロセスをrootに戻す)
# echo 907 > /sys/fs/cgroup/td/cgroup.threads 
bash: echo: write error: Operation not supported
# echo 907 > /sys/fs/cgroup/td/th01/cgroup.threads 
bash: echo: write error: Operation not supported
(エラーになる)

このようにいきなりcgroup.threadsに登録しようとするとエラーとなります。

一度マルチスレッドプロセスをスレッド化サブツリーに登録したあとは,スレッド化サブツリー内で自由に移動できることを確認してみましょう。

# echo 907 > /sys/fs/cgroup/td/cgroup.procs 
(マルチスレッドプロセスをdomain threadedに登録)
# cat /sys/fs/cgroup/td/cgroup.procs 
907
(PPIDが登録された)
# cat /sys/fs/cgroup/td/cgroup.threads 
907
908
909
(PPIDとTIDが登録されている)
# echo 908 > /sys/fs/cgroup/td/th01/cgroup.threads 
(スレッドをth01に移動)
# cat /sys/fs/cgroup/td/th01/cgroup.threads 
908 (移動された)
# echo 909 > /sys/fs/cgroup/td/th02/cgroup.threads 
(別のスレッドをth02に移動)
# cat /sys/fs/cgroup/td/th02/cgroup.threads 
909 (移動された)

ここでスレッド(TID)cgroup.threadsでなくcgroup.procsに登録すると,マルチスレッドプロセス全体が移動してしまうことに注意してください。

# echo 909 > /sys/fs/cgroup/td/th01/cgroup.procs   
(プロセス内のTIDをth01のcgroup.procsに登録)
# cat /sys/fs/cgroup/td/th01/cgroup.threads 
908
907
909
(登録したスレッドだけでなく属するプロセス全体が移動した)

色々試して頭が混乱したかもしれませんね。ここまでに試したことをまとめておきましょう。

  • マルチスレッドプロセスをスレッド化サブツリーに移動する場合は,まずはスレッド化サブツリー内のcgroupのcgroup.procsに登録する必要がある
    • 登録するIDはプロセス内のTIDでも良い
  • "threaded"なcgroupのcgroup.procsは読み取れない。登録されているIDを確認するにはcgroup.threadsを読む必要がある
    • domain threadedなcgroupのcgroup.procsは読み取れる
  • TIDであってもcgroup.procsへの書き込みではマルチスレッドプロセス全体が移動する

著者プロフィール

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

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

Plamo Linuxメンテナ

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