前回は、
今回の実行例は、cgroup_
と指定して、
そして、root
ユーザで実行しています。
それでは早速、
スレッド化サブツリーを作るには2つの方法があります。
cgroup.typeファイルへの書き込みによるスレッド化サブツリーの作成
まずは1つめの方法です。
シンプルにcgroup.
ファイルにthreaded
という文字列を書き込むだけです。これまで説明してきたcgroupの操作方法をご存知であれば想像がつく方法かと思います。
この結果次のようになります。
threaded
という文字列を書き込んだcgroupのタイプは"threaded"になるthreaded
という文字列を書き込んだcgroupの親cgroupは"domain threaded"になるthreaded
という文字列を書き込んだcgroupの親cgroup配下の"threaded"以外のcgroupは"domain invalid"になる
つまり、threaded
と書き込んだcgroupの親cgroup配下が、
できあがったスレッド化サブツリー内の"domain invalid"な状態のcgroupすべてにthreaded
という文字列を書き込んでいかないと、
この動きを確認してみましょう。
cgroup v2のroot配下にtd
というディレクトリを、td
配下にth01
、th02
という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.
ファイルも確認しておきましょう。
# find /sys/fs/cgroup -name "cgroup.type" -exec cat {} \;
domain
domain
domain
(作成したいずれのcgroupのタイプもdomain)
いずれのcgroupも初期状態ですのですべて"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"に、th02
は"domain invalid"となりました。
つまり"threaded cgroup"の親は"domain threaded"に、

このままではth02
は使えない状態のままです。th02
を機能させるためには"threaded"にする必要があります。
# echo "threaded" > /sys/fs/cgroup/td/th02/cgroup.type # cat /sys/fs/cgroup/td/th02/cgroup.type threaded
これでth02
がthreadedになり、

スレッド化サブツリー内でのタスク操作
2つめの方法を見る前に、
マルチスレッドプロセスについて
プロセスやスレッドの追加について見る前に、マルチスレッドに関わるIDの用語がややこしいので、説明で使う用語の整理をしておきます。
まずはマルチスレッドプロセスを起動してみます。次のように2つのスレッドを持つプロセスですpstree
コマンドではスレッドは中かっこ{}
で囲われます)。
# pstree -p 907 threadtest(907)─┬─{threadtest}(908) └─{threadtest}(909)
この後は、上の例を使って説明を進めます。
- スレッドグループ:マルチスレッドプロセス内のプロセス、スレッドの集合
(ここではID 907
、908
、909
のプロセスからなるグループ) - スレッドグループリーダ:スレッドグループ内でスレッドを生成する元となるプロセス
(ここではID 907
のプロセス) - TGID:スレッドグループのID。スレッドグループリーダのPIDに等しい
(ここでは 907
) - PID:各スレッドのID
(ここではそれぞれ 907
、908
、909
)
プロセスやスレッドはそれぞれ、IDとしてカーネル内ではpid
というデータを持ちます。ここでは各スレッドのIDである907
、908
、909
です。スレッドIDとしてTIDと説明されることもありますが、カーネル内ではpid
という名前の変数ですので、ここでは各スレッドのIDとしてPIDという用語を使います。gettid(2)
というシステムコールを使うとこのpid
が返ります。
そして、907
、908
、909
からなるスレッドグループのID907
です。getpid(2)
というシステムコールでマルチスレッドプロセスのIDを取得すると、このTGIDが返ります。
PID、TID、TGIDとややこしいのですが、ここでは上記のようにカーネル内の名称をベースに説明を進めます。
スレッド化サブツリー内へのタスクの追加
用語の説明が済んだところで、スレッド化サブツリー内へのプロセスやスレッドの追加を見ておきましょう。
スレッド化サブツリーにマルチスレッドプロセスを登録させるには、cgroup.
に登録する必要があります。"threaded"や"domain threaded"のcgroupであっても、cgroup.
にプロセスやスレッドのIDは登録できません。
このスレッドグループリーダのPID907
を図3で作ったcgroup td
に登録してみます。
# echo 907 > /sys/fs/cgroup/td/cgroup.procs (スレッドグループリーダのPIDをdomain threadedのcgroup.procsに登録) # cat /sys/fs/cgroup/td/cgroup.procs 907 (cgroup.procsにはスレッドグループリーダのPID(TGID)のみ登録されている) # cat /sys/fs/cgroup/td/cgroup.threads 907 908 909 (cgroup.threadsには同じプロセスに属するスレッドも含めて登録されている)
スレッドグループリーダの907
をcgroup.
に登録すると、cgroup.
にはそのPIDが登録され、cgroup.
にはその子となるスレッドのID908
、909
が登録されています。
cgroup.
への登録は"domain threaded'であるcgroupへの登録である必要はなく、th01
やth02
への登録でも構いません。次の操作は"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.
の内容を見ています。これは"threaded"なcgroupのcgroup.
ファイルは読み取りできないからです。cgroup.
を読もうとすると次のようにエラーになります。
"domain threaded"であるcgroupのcgroup.
は先の例で実行したように読み取れます。
# cat /sys/fs/cgroup/td/th01/cgroup.procs cat: /sys/fs/cgroup/td/th01/cgroup.procs: Operation not supported
この例ではスレッドグループリーダのPIDを登録しました。代わりにスレッドのIDをcgroup.
に登録しても同じ結果となります。上の例のマルチスレッドプロセス内のスレッドのPIDである909
をcgroup.
に登録してみましょう。
# echo 909 > /sys/fs/cgroup/td/cgroup.procs (マルチスレッド内のスレッドのPIDをcgroup.procsに登録) # cat /sys/fs/cgroup/td/cgroup.procs 907 (cgroup.procsにはスレッドグループリーダのPIDが登録されている) # cat /sys/fs/cgroup/td/cgroup.threads 907 908 909 (cgroup.threadsにはスレッドグループリーダのPIDと各スレッドのPIDが登録されている)
このように、cgroup.
ではTGIDが表示され、cgroup.
にはスレッドグループ内のスレッドすべてが表示されていることがわかります。
先にcgroup.
に登録する必要があります」
プロセスをcgroup.
ではなく、cgroup.
に登録を試みます。先の例に続けて実行する場合は、
# 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.
に登録しようとするとエラーとなります。
一度TGIDをスレッド化サブツリーに登録したあとは、
# echo 907 > /sys/fs/cgroup/td/cgroup.procs (マルチスレッドプロセスをdomain threadedに登録) # cat /sys/fs/cgroup/td/cgroup.procs 907 (スレッドグループリーダのPIDが登録された) # cat /sys/fs/cgroup/td/cgroup.threads 907 908 909 (スレッドグループリーダのPIDとスレッドのPIDが登録されている) # 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 (移動された)
ここでスレッドのPIDをcgroup.
でなくcgroup.
に登録すると、
# echo 909 > /sys/fs/cgroup/td/th01/cgroup.procs (スレッドグループ内のスレッドのPIDをth01のcgroup.procsに登録) # cat /sys/fs/cgroup/td/th01/cgroup.threads 908 907 909 (登録したスレッドだけでなく属するプロセス全体が移動した)
色々試して頭が混乱したかもしれませんね。ここまでに試したことをまとめておきましょう。
- マルチスレッドプロセスをスレッド化サブツリーに移動する場合は、
まずはスレッド化サブツリー内のcgroupの cgroup.
に登録する必要があるprocs - 登録するIDはスレッドグループ内のスレッドのPIDでも良い
- "threaded"なcgroupの
cgroup.
は読み取れない。登録されているIDを確認するにはprocs cgroup.
を読む必要があるthreads domain threaded
なcgroupのcgroup.
は読み取れるprocs
- スレッドグループ内のスレッドグループリーダ以外のスレッドのPIDであっても
cgroup.
への書き込みではマルチスレッドプロセス全体が移動するprocs
プロセスが存在する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の当初の仕様では、
試してみましょう。
まずは先ほどの例と同様のツリーを作成します。そしてtd
の親cgroupcgroup.
ファイルに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 スレッドコントローラを登録して末端ではないcgroupにプロセスを登録

ここでtd
のcgroup.
ファイルに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

td
のcgroup.
ファイルにCPUコントローラが登録され、th01
、th02
のcgroup.
ファイルにはcpu
が登録されており、
ここでおもむろにtd
のcgroup.
ファイルを確認して、td
のcgroupタイプを確認してみましょう。
# cat td/cgroup.type domain threaded
プロセスが所属し、td
はdomain threadedになっています。その子cgroupであるth01
、th02
のタイプを確認してみましょう。
# cat td/th{01,02}/cgroup.type domain invalid domain invalid
親となるtd
が"domain threaded"となったので、th01
、th02
はいずれも"domain invalid"となっています。スレッド化サブツリーを完成させるには、

# echo "threaded" > td/th01/cgroup.type # echo "threaded" > td/th02/cgroup.type # cat td/th{01,02}/cgroup.type threaded threaded
th01
、th02
がthreadedに設定されました。これでスレッド化サブツリーの完成です。

cgroup.
ファイルへの書き込みでスレッド化サブツリーを作成した際の例と同様にスレッドを登録してみましょう。
# 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.threads
ファイルへのタスクの登録権限
第40回で、
今回の実行例では、root
権限で操作を行っていますので、
つまり、cgroup.
ファイルへの書き込みには第40回で説明した、
- 書き込む先の
cgroup.
ファイルへの書き込み権threads - 移動元と移動先のcgroupの共通の祖先にある
cgroup.
ファイルへの書き込み権procs
そして、
- 移動元と移動先のcgroupは同じスレッド化サブツリー内に所属している必要がある
という条件が加わります。
root cgroupの扱い
これまでの例では、
では、
root直下にth01
というcgroupを作成し、
# 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 cgroupは"domain"と"threaded"なcgroupの親になります。このようにroot cgroupを例外的に扱っているのは、

もし"threaded"に変化したroot直下のcgroupが子cgroupを持っていた場合は、
# 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へスレッドコントローラーを登録する
そして、
今回でcgroup v2のスレッドモードの説明は終わりです。前回と今回の説明で、