Linux Daily Topics

リアルタイムLinuxをあきらめない ―20年越しでメインラインに統合されたPREEMPT_RTの物語

2024年11月中旬のリリースに向けて開発中の次期Linuxカーネル「Linux 6.12」ではいくつかの大きなアップデートが予定されているが、その中でも最大の変更点とされているのが20年越しの実装となったリアルタイムLinuxのパッチセット「PREEMPT_RT」のメインライン統合だ。すでにCanonicalやSUSEなどは独自にPREEMPT_RTを統合したリアルタイムLinuxディストリビューションを提供しているが、メインラインカーネルのリアルタイム機能サポートにより、自動運転の制御システムや産業用ロボット、医療機器、金融取引といったミッションクリティカルなワークロードでのLinux採用がより拡がっていくことが期待される。

Linuxカーネルはもともと、リアルタイムOSの実現に欠かせない「決定論的な動作」を前提には書かれていない。リアルタイムOSではマイクロ秒単位の時間制約の中で、外部環境の変動に左右されることなく確実に重要なタスクを完了させる ‐ つまり決められた時間内に優先度の低いタスクを無視して最優先のタスクを実行しなければならないが、⁠通常の)Linuxではそれが保証されない。Linuxカーネルには長期間のプリエンプションや割り込みを禁止する領域があり、高優先度のタスクであってもレイテンシが発生する可能性が高くなる。

今回Linux 6.12にマージされたPREEMPT_RTは、それまでメインラインカーネルとは別で開発が行われていた複数のリアルタイム化プロジェクトを、2004年にカーネル開発者のIngo Mornarが統合/再構築したパッチセットがはじまりで、名前の通り、カーネル内のほぼすべての領域でプリエンプションを可能にすること目的としている。

しかし、もともとリアルタイムOSではないLinuxカーネルにリアルタイム機能を実装するには、当然ながらいくつもの技術的なハードルが立ちはだかり、最終的にメインラインにマージされるまでには20年という長い時間を要することになる。なかでも後述のカーネル空間へのログ出力を担うprintk()関数の書き換えはPREEMT_RT開発者たちを最後まで悩ませることになった。また、スポンサー企業のリアルタイムOSへの関心の薄さによる資金不足や、フルタイムで作業を行うリアルタイムLinux開発者の不足は深刻で、PREEMPT_RTの主要開発者である古参メンテナーのThomas Gleixnerは過去に何度か「⁠⁠リアルタイムLinuxの)コードを維持するための専任の開発チームがいない」ことを重大な懸念として訴えている。

こうしたストレスを抱えながらも、Glexinerをはじめ、Steven RostedtやPetr Mladek、John OgnessといったPREEMPT_RT開発者たちはリアルタイムLinuxの実装を続け、その甲斐あって彼らが開発した機能は徐々にメインラインカーネルに取り込まれていった。2021年10月リリースのLinux 5.15ではリアルタイム機能の大部分を占めるPREEMPT_RTロックコードがマージされるに至っている。

マージ直前で差し戻し

もっともPREEMPT_RTが完全にマージされるまでには、Linux 5.15のリリースからさらに3年の月日を要することになるのだが、その最後にして最大の難関となったのが「printk()の書き換え」だった。Linux誕生時から存在するカーネル空間のログ出力関数printk()はロック競合や割り込み無効化、コンソール出力の遅延など、リアルタイムLinuxを実現するうえでハードルとなる特徴を多く備えている。なかでもシリアルコンソール出力のレイテンシは、リアルタイムLinuxをエンタープライズで普及させるにあたって許容できるレベルではなかった。

PREEMPT_RT開発者たちは数年に渡ってprintk()の改修を重ねてきたが、彼らがたどり着いた最終的なアイデアは「登録されたコンソールごとにカーネルスレッド(kthread)を作成し、コンソールの出力をprintk()の呼び出し元から完全に切り離す→個々のkthreadにメッセージの出力をオフロードする」というものだった。コンソールごとにプリントスレッドを作成することで、各コンソールはフルスピードでメッセージを出力することが可能になり、コンソール間のロックの競合も発生しない。このプランはOgenssによって2019年に公開されたprintk()の改修案をベースにしている。

PREEMPT_RT開発者たちによるこの⁠コンソール出力のスレッド化⁠にフォーカスしたprintk()の書き換えは一進一退を繰り返しつつ、2022年8月にはLinux 6.0でマージ直前まで進展する。しかし当時のマージウィンドウに送られたプルリクエストでは、アトミックコンテキストの出力でスピンロックがスリープし、リアルタイム性が保証されなくなってしまうことから「CONFIG_RT(PREEMPT_RTパッチを有効化する設定)でカーネルのリアルタイム機能を有効化するとコンソール出力をスキップする(無効化する⁠⁠→ログバッファの取得はユーザ空間で行う」という修正が加えられたため、カーネルによるユーザランドへの影響を強く嫌うLinusは「この修正は到底受け入れられない」としてプルリクエストのマージを拒否、開発者たちは再度パッチの見直しを迫られることになる。

Linus Torvaldsも賞賛⁠“レガシーとのたたかい”

Linusからマージを拒否された1ヵ月後の2022年9月、⁠失敗を徹底的に分析した」というPREEMPT_RTチームを代表してGleixnerが29個からなる新しいprintk()パッチセットをRFCとして公開した。新しいパッチセットは、初期のLinuxカーネルに存在したロック機構であるBKL(Big Kernel Lock)を一掃する意味合いも込めて「NOBKL(No Big Kernel Lock⁠⁠」としてレビューが続けられたが、最終的には「NBCON(No Blocking CONsole:任意のコンソールが他のコンソールを制限しない⁠⁠」パッチセットと呼ばれている。

NBCONを実装したコンソールでは、コンテキストの優先度を通常/緊急/パニックの3つのレベルに分け、優先度の高いメッセージをもつCPUがプリントスレッドにアクセスできるように設計されている。たとえば「CPU 0」が通常の優先度でメッセージをコンソールに出力中に、⁠CPU 1」が緊急の優先度で警告を出力しようとする場合、CPU 0はプリエンプトされ友好的にコンソールを手放す、あるいはCPU 1による敵対的なテイクオーバーが行われる(コンソールロック⁠⁠。また、NBCONを実装したドライバを書くために必要なコールバックとして、スレッドコンテキストから呼び出される(スリープ可能な)write_thread()と、NMIを含むアトミックモードから呼び出される(ブロック不可な)write_atomic()が追加されている。なお現時点ではまだNBCONをサポートするコンソールは存在せず、今後、このパッチを適用するドライバを待つことになる。

9月16日にオーストリア・ウィーンで開催された「Open Source Summit Europe 2024」に出席したLinusは、20年に渡ってリアルタイムLinuxをあきらめることなくパッチの改修を続けたPREEMPT_RT開発者たちを賞賛し、Linux 6.12へのマージを明らかにした。以前、Gleixnerはイベントで「printk()はLinuxのレガシーがもたらす混乱の中で僕がクリーンアップする最後の存在だ。その後は若い人たちに、僕が20年前に作り込んでしまったかもしれないレガシーをクリーンアップする役目を引き継ぎたい」とコメントしている。新しい技術を実装しようとするとき、レガシーと化してしまった技術とどう向き合うのか、PREEMPT_RTプロジェクトの歩みはそのありかたのひとつを示しているのかもしれない。

おすすめ記事

記事・ニュース一覧