リアルタイムLinux(PREEMPT_RT)の概要
通常のLinuxは「平均的に速い」ことを目指す汎用OSで、ある処理が必ず期限内に終わる保証(リアルタイム性)はありません。PREEMPT_RT パッチを当てたカーネルは、カーネル内の処理の多くを横取り(プリエンプト)可能にし、割り込み応答のレイテンシを抑えて最悪実行時間を予測しやすくします。モータ制御や計測のように「決められた周期を外すと困る」用途で効きます。uname -a でカーネル文字列に PREEMPT RT が含まれるかを確認でき、cyclictest でレイテンシを実測して評価します。万能ではなく、スループットとのトレードオフがある点に注意します。
普段使っているLinuxは、たくさんの処理をうまくさばいて「全体として高い性能」を出すことを目指した汎用OSです。多数のプロセスやWebサーバをCPUに効率よく割り当て、平均的なスループット(単位時間あたりにこなせる処理量)を高めるのが得意です。その代わり、「ある特定の処理を、必ず決められた時間内に終わらせる」という保証はありません。普段は一瞬で終わる処理でも、ちょうど別の重い処理やカーネル内部の作業、割り込みの処理と重なると、応答が数ミリ秒〜時に数十ミリ秒も遅れることがあります。しかもその遅れは毎回同じではなく、いつ大きく遅れるかを事前に読みにくいのが汎用OSの性質です。動画再生やファイル転送なら多少の遅れは飲み込まれて問題になりませんが、機械を相手にする組込みの世界では、この「たまに、予測できないタイミングで遅れる」ことが致命傷になる場面があります。
リアルタイム性とは何か
ここで言うリアルタイム性とは「速いこと」ではなく「締め切りを必ず守れること(時間的な予測可能性)」を指します。重要なのは平均の速さではなく、最も遅いとき=最悪値がどれだけに収まるか、です。平均1ミリ秒でも、ごくまれに50ミリ秒かかることがあるなら、締め切りが10ミリ秒の制御には使えません。逆に毎回安定して5ミリ秒で終わるなら、平均が遅くても締め切り10ミリ秒は守れます。たとえばモータを一定周期できっちり制御したい、センサを正確な間隔でサンプリングしたい、という用途では、1回でも周期を外すと制御が乱れたりデータが歪んだりします。こうした「決められた周期や期限を外すと困る」要求に応えるのが、リアルタイム性の考え方です。組込みでモータ制御・計測・通信のタイミング生成などを担う場合、ここが効いてきます。なお、絶対に1回も期限を外してはならない「ハードリアルタイム」と、ときどきなら許される「ソフトリアルタイム」という区別もあり、要求の厳しさに応じて必要な作り込みの度合いが変わります。
PREEMPT_RT パッチ
通常のLinuxカーネルにこのリアルタイム性を与えるのが PREEMPT_RT というパッチ(およびそれを取り込んだカーネル構成)です。鍵となるのは「プリエンプト(横取り)」の範囲を広げることです。プリエンプトとは、いま動いている処理を中断して、より優先度の高い処理に即座にCPUを譲ることを指します。通常のカーネルでは、いったんカーネル内部の処理に入ると、優先度の高い処理が来ても割り込めない区間(プリエンプト不可の区間)が多く残っています。たとえばカーネルがあるデータを保護するためにロックを握っている間や、割り込み処理の最中などです。この「横取りできない区間」こそが、リアルタイム処理の応答が読めずに遅れる主な原因になります。
PREEMPT_RT は、こうしたカーネル内のロックや割り込み処理の多くを、後から横取り可能な形に作り変えます。割り込みハンドラを優先度を持つカーネルスレッドとして動かせるようにするなどの工夫により、優先度の高いリアルタイム処理が「待たされる時間」を大幅に短くします。結果として、割り込みやイベントが起きてから処理が始まるまでの遅れ=レイテンシの最悪値が抑えられ、締め切りを予測しやすくなります。重要なのは、これが「平均を速くする」のではなく「最悪のばらつきを小さく抑える」方向の改善だという点です。なお PREEMPT_RT は長年メインラインのLinuxカーネルへのRT機能統合が進められてきた経緯があり、近年その作業が大きく前進しています。Raspberry Piでも、RT構成のカーネルを用意して動かすことができます。
確認と計測
自分のカーネルがリアルタイム構成かどうかは、まず uname で見当をつけます。`uname -a` を実行し、出力されるカーネル文字列の中に PREEMPT RT(あるいは PREEMPT_RT)という表記が含まれていれば、RT構成のカーネルである手がかりになります。ただし表記があるだけでは実力は分かりません。実際にどれだけレイテンシが抑えられているかは、rt-tests パッケージに含まれる cyclictest で実測します。cyclictest は一定周期で起床するタスクを動かし、「起きるべき時刻」と「実際に起きた時刻」のズレ(=そのままレイテンシ)を測って、最小・平均・最大(最悪値)を報告します。何もしていないときは通常カーネルでも良い値が出るため、評価では別途CPUやI/Oに負荷をかけた状態で長時間回し、そのときの最大レイテンシを見ます。この最大値が小さく、しかも時間が経っても跳ね上がらずに安定しているほど、リアルタイム性が高いと評価できます。逆に普段は小さくてもときどき大きなスパイクが出るなら、その用途には使えません。レイテンシは平均より最悪値が肝心、という原則を、この計測を通して具体的な数字で体感できます。
トレードオフと実務の勘どころ
PREEMPT_RT は万能薬ではありません。カーネルを横取り可能にするための追加処理が増えるぶん、全体のスループットはわずかに犠牲になることがあります。つまり「最悪値を抑える代わりに、平均的な処理量は少し下がる」というトレードオフが存在します。動画のエンコードや大量のファイル処理のように、とにかく総処理量を稼ぎたい用途では、RT化はむしろ不利に働くこともあります。だからこそ、何でもRT化すればよいのではなく、本当に時間保証が要る用途かを見極めて採否を決めます。実務では、まず通常カーネルで要求周期を満たせるか試し、レイテンシが問題になる場合に PREEMPT_RT を導入し、cyclictest で最悪値を確認したうえで、制御ループの優先度設定(リアルタイム優先度の付与)やCPUの割り当て(特定コアを制御専用にする)まで含めてチューニングします。「平均ではなく最悪を見る」「保証が要る所だけRT化する」——この2点が、リアルタイムLinuxを扱ううえでの勘どころです。