プロセスの一覧には数個しか見えていないのに、新しいスレッドを作れなくなることがあります。Linuxのcgroup v2で使うpids.maxを読むときは、アプリケーションの個数だけでなく、その中で動くスレッドと、親グループに含まれる別の仕事まで見る必要があります。本稿ではLinux 6.8の文書と実装を対象に、何を数え、どこで追加が止まるのかを整理します。[1][2]
簡単にまとめると?
- pidsコントローラーが数える単位は、カーネルがTIDで識別するタスクです。一つのプロセスに複数のスレッドがあれば、その分も数えます。
- 子グループに余裕があっても、祖先グループの合計が上限に達していれば、新しいタスクの生成は拒否されます。
- 上限を現在数より低く設定しても、既存タスクをその数まで自動終了させる仕組みではありません。
- CPU時間、メモリー量、タスク数は別の制約です。プロセス一覧だけでなく、対象と祖先の現在数・上限を併せて確認します。
「プロセス数」という名前だけでは数え方が分からない
cgroupは、仕事を階層的なグループに分け、資源の利用を管理するLinuxの仕組みです。pidsコントローラーは、その中で新しく作られるタスクの数を制限します。メモリーにまだ余裕があっても、タスクを大量に増やせば別の制約へ先に到達し得るため、メモリー制御とは別に用意されています。[1、PID節]
通常の説明では、プロセスは実行中のプログラムの単位、スレッドはその中の実行の流れとして区別します。ところが、このコントローラーのPIDという語は、文書が明記するようにTID、つまり個々のタスクを識別する番号を指します。メインのスレッドも一つに数えます。「プロセスが二つだから使用数は二つ」とは限りません。[1]
たとえば、一つ目のプロセスがメインを含めて三スレッド、二つ目が一スレッドなら、合計は四タスクです。同じグループ内のタスクだけでなく、その配下のグループにあるタスクもpids.currentへ含まれます。どの階層の値を読んでいるのかを揃えないと、プロセス一覧との比較を誤ります。
子に四つ分の余裕があっても、あと一つで止まる例
独自の小さな計数モデルで考えます。親Pの上限を十タスクとし、その下に子Aと子Bを置きます。P自身にはタスクがなく、Aには前述の二プロセス・四タスク、Bには一プロセス・五タスクがあるとします。Aの上限は八、Bの上限も八です。親Pの現在数は、四足す五で九になります。
Aだけを見ると、八という上限まであと四つ増やせそうです。しかし、P全体ではあと一つしか空きがありません。Aで新しいスレッドを一つ作ると、Aは五、Pは十となり、このモデルでは許可されます。続けてもう一つ作ろうとすると、Aの六は上限八以下でも、Pの十一が上限十を超えるため拒否されます。拒否後の現在数はAが五、Pが十のままです。
Linux 6.8のpids_try_chargeは、対象グループから祖先へ順に数を加えて上限を確かめ、途中で超過したら加算を取り消して-EAGAINを返します。[2] この動作を、ここでは競合のない逐次の計数として再現しています。実際のカーネルで負荷をかけた実験ではなく、同時生成・終了や他の生成失敗要因も扱っていません。

Bのタスクが一つ解放され、カウントが四へ減れば、Pは九に戻ります。この後でAが一つ生成する例では、Aが六、Bが四、Pが十となって許可されます。同じAの要求でも、兄弟グループの使用状況が結果を変えることが分かります。Aの設定を八からさらに大きくしても、親Pの十という制約は取り除けません。
現在数が上限より大きくても、ただちに異常とは限らない
上限は、常に現在数をその値以下へ押し戻す命令ではありません。前の例のAが六タスクを持つ状態で、Aのpids.maxを四へ下げたとします。文書は、このような設定変更やタスクのグループ間移動によって、pids.current > pids.maxになる場合があると説明しています。[1]
この設定変更だけで、六つのうち二つが自動的に終了するわけではありません。Linux 6.8の上限書込み処理は上限値を更新し、新しいタスクの生成時には別途その値を検査します。[2、pids_max_write] したがって「六から四へ設定したのに、現在数が六のまま」という観測だけで、上限が無効だとは判断できません。
また、現在数が四まで下がっても、次の一つを加えると五になるため、上限四のままでは生成できません。他の制約がない単純なモデルなら、三以下になって初めて一つ分の余地が生まれます。上限と現在数が等しい状態を「まだ一つ追加できる」と読み違えないことが大切です。実機では解放がカウントへ反映された時点と、別の生成が進んだ時点にも注意が必要です。
調べる順番は、数える単位、所属、祖先の順
新規生成が失敗したときは、まず何が増えようとしていたのかを確認します。別プロセスの起動なのか、既存プロセスのスレッド追加なのかで、通常のプロセス一覧から見える変化が異なります。次に、失敗した処理が属するグループと、観測しているpids.currentの階層を合わせます。最後に、対象だけでなく祖先側の上限と合計数を辿ります。
pids.maxの値がmaxなら、そのグループ自身には数値上限を置いていないという意味です。祖先の上限までなくなるわけではありません。[2] 子ごとの設定を比較する際にも、「Aには八、Bにも八だから親全体で十六まで使える」と足し算せず、親の条件を別に確かめます。
ここで示したEAGAINは、pidsコントローラーが生成を拒否する経路の説明です。生成APIが返したエラーだけから、この上限が原因だと断定するものではありません。利用しているLinuxの版、コントローラーの有効化、管理ソフトが設定した所属と上限、同時に動く処理を照合する必要があります。CPU時間を配分する設定やメモリーの上限を増やしても、タスク数の制約を直接解消したことにはなりません。何が不足しているのかを単位ごとに分けると、変更すべき設定を絞り込めます。
