⏱️

RTOSの概要とリアルタイムを実現するための仕組み

2024/12/05に公開

これは SMat Advent Calendar 2024 の12/5分の記事です。

株式会社エスマット エンジニアの若林です。

弊社では「SmartMat Cloud」というIoT重量計 x SaaSでモノの流れを可視化するサービスを提供しております。弊社で働いているエンジニアはほとんどがWebアプリエンジニアで、組み込み系のソフトエンジニアは現在私しかおらず少し寂しさを感じています。

そこで組み込み系の技術についてWebアプリエンジニアにも触れてもらうために、今回はRTOSに関して記事を書いてみました。同じような会社は他にもあると思うのでIoTを取り扱っているエンジニアの方はぜひご覧ください。

RTOSとは

RTOS(Real-Time Operating System)とは、時間的な制約がある処理を実行するための機能や特性を備えたOSのことです。

家電やIoTデバイスなどの組み込み機器では、ボタンを押したときにすぐ音を鳴らすとか、異常を検知したときにすぐモーターを止めるなど、あるイベントが発生したらこの処理を何ミリ秒以内に完了させなければならいないといった時間的制約があります。そのような時間的制約を守るための機能をRTOSは備えています。

汎用OSとどう異なるのか

皆さんが使っている Windows, Mac, Linux などの汎用OSとの一番の違いはタスクのスケジューリング方法です。タスクやスケジューリングについては後で詳しく説明しますが、「同時並行で動かす必要のある様々な処理の中で、いまはどの処理を動かすか」を決める方法が異なると考えてください。

複数の処理を同時に動かす必要があっても、使えるCPUは1つです。(マルチコアとかマルチCPUとかもありますが、数が限られているという点は同じ)そのためOSはどの処理にCPUを割り当てるかをスケジューリングしなければなりません。

例えばPCでマウスをクリックしてもすぐに反応せず少し遅れてから反応することがありますが、それはスケジューリング方式の影響かもしれません。RTOSでは、マウスのクリックを受け付ける処理が最優先だからほかのタスクは後回しにするぞということが制御できるようになります。

また、RTOSでは組み込み向けに使われるため、組み込みデバイス向けの以下のような特徴を持っています。

  • プログラムサイズが小さい
  • RAMの使用は最小限
  • ハードウェアを直接制御する機能がある

例えば、WindowsやMacではグラフィック表示機能(GUI)や仮想メモリ機能、ファイルシステムなど様々な機能が標準装備されていますが、RTOSでは搭載されていなかったりオプションで取捨選択できるようになっています。

RTOSではないですが、組み込みLinuxなどの汎用OSを軽量化した組み込み向けOSというのもあります。ネットワーク機器ではよく使われているようです。

RTOSを使わない場合、組み込みソフトはどう作るのか

処理が複雑ではなかったり、組み込み向けのOSさえもいれられないくらいハードウェアリソースが非力な場合、RTOSを使わずに組み込みソフトを作ることも当然あります。
その場合はメインループと割り込み処理を組み合わせて組み込みソフトを作っていきます。

以下に、ボタンを押したらLEDが一定時間ごとにチカチカ点滅するプログラム(通称Lチカ)をRTOSを使わずに実装する際のイメージを載せます。

uint8_t is_pushed;
uint8_t led_status;
uint16_t timer_count;

void app_main(){ // メインループ(基本的にはこの関数が実行されている)
    initialize(); // 初期化(グローバル変数の初期化や、ボタンおよびLEDの設定など)

    while(1){
        if(is_pushed){
            if(timer_count > SWITCH_COUNT){ // 一定時間(例えば500ms)たったらLEDのON/OFFを切り替える
                led_status = ((led_status == LED_ON) ? LED_OFF : LED_ON); // LED が ONならOFFへ、OFFならONへ切り替える
                set_led(is_led_on);
            }
        }else{
            led_status = LED_OFF; // ボタンがOFFならLEDもOFFにする
            set_led(LED_OFF);
        }
        /* メインループはこれの繰り返し */
    }
}

void button_interrupt(){ // ボタンが押されたらこの関数が呼ばれる(ように設定する)
    is_pushed != is_pushed; // すでに押されてたらfalseに、押されていなかったらtrueに切り替える
    timer_count = 0;
}

void timer_interrupt(){ // 一定時間(例えば10msなどの細かい時間)ごとにこの関数が呼ばれる(ように設定する)
    timer_count++;
}

このように、ボタンの押下やタイマなどのイベントが発生したら割り込み処理でグローバル変数を書き換え、メインループはグローバル変数の状態を見て動作をするというような作りになります。

この例だと簡単でしたが、ループと割り込み処理の組み合わせでのつくりでは、処理が複雑になっていくにつれて破綻してきます。
例えばボタンの状態をサーバに送る機能を付けるとしましょう。もしプログラム中の /* メインループはこれの繰り返し */の位置にデータ送信処理関数の呼び出しを記載したとすると、次回の点滅時間までにデータを送り終わらないとLEDが正しい間隔で点滅しなくなってしまうでしょう。

正しい間隔で点滅させるようにするにはデータ送信処理関数をぶつ切りにして、TCPソケットが接続できたらいったん終わる、データを半分送ったらいったん終わる、残りのデータを送ったら終わるというように時間単位で処理を分割する必要があります。
そのように一連の処理を時間単位で分割してしまうと可読性や保守性が悪くなってしまいます。さらにその分割で確実にLEDの点滅間隔が正しくなるかを検討するのも難しいです。
RTOSを用いて機能単位で分割できていれば、他の組み込み機器のソフトにもその機能を移植するということもしやすくなるでしょう。

タスク

タスクとは

RTOSではある一連の処理の塊をタスクと呼ぶ単位に分けて管理します。WindowsやLinuxでいうところのスレッドやプロセスに相当します。
RTOSは今このタイミングではどのタスクを実行すべきかというのを、タスクの状態や優先度をもとに判断しスケジューリングを行います。

先ほどの例では、機能単位でLEDを点滅させるタスクとデータ送信をするタスクの2つにわけ、タスクの切り替えをRTOSに任せて必要な処理が必要なタイミングで実施されるようにできます。

タスクの状態

タスクは以下のように、実行状態、実行可能状態、待ち状態、休止中の状態を持ち、様々な要因をもとに遷移していきます。

タスクの状態遷移

実行状態はタスクにCPUが割り付けられた状態です。CPUは1つしかないので実行状態になるタスクもただ1つだけです。
実行状態のときに時間経過待ちや他のタスクの処理待ち、ボタン押下などのイベント発生待ちをする場合、実行状態のタスクはそれをRTOSに伝えて待ち状態に遷移します。
待ち状態のタスクは待っている事象が発生したら実行可能状態に遷移し、CPUの割り付けを待ちます。
OSのスケジューリングルールによって実行可能状態のタスクの中からどのタスクを実行状態にするかが決まります。

休止状態はタスクが全く活動していない状態です。Windowsで言えばタスクマネージャーからタスクをキルした状態だと考えていただけると良いと思います。

タスクのスケジューリング方法

RTOSでは、実行可能状態のタスクのうち優先度が最も高いタスクを実行状態に遷移させます。タスクの切り替えタイミングは、以下の図のようにイベントが発生したタイミングで行われます。
このようなスケジューリング方式はイベント駆動方式と呼ばれています。

この方式では一番優先度の高いタスクの処理が終わるまで必ず実行され、優先度の高いタスクを必要な期限までに終わらせるという保証がしやすくなります。

RTOSのスケジューリング

それに対し、汎用OSではラウンドロビン方式と呼ばれるスケジューリング方式が取られています。ラウンドロビン方式では、タスクにCPUを割り当てる時間を一定間隔で区切り、その時間が過ぎたら処理途中でも別のタスクにCPUの割り当てを切り替えます。
どのタスクも平等に処理され、一定の応答時間を保証できます。また、処理の開始から終了までの時間を平均的にある程度早くすることもできます(タイムクォンタムの設定次第)。対話型のシステムで有効な方式です。

RTOSのメリット・デメリット

メリット

  • 様々な処理が同時並行的に動くようなシステムで、可読性、保守性が高く実装ができる
  • 必要な処理を必要な期限までに終わらせるシステムが実装しやすい
  • 汎用OSと比べて、ハードウェアリソースの乏しいデバイスでも動かせる

デメリット

  • タスク分割単位や優先度の決定、スタックサイズの決定など設計段階で考えることが多く、しかもきちんとリアルタイム性を保持するように考えるのは難しい
  • OSなしでの実装と比べ、プログラムサイズは大きくなりがち
  • OSなしでの実装と比べ、タスクの切り替えによるオーバーヘッドが発生する

まとめ

本記事ではRTOSの概要とタスクスケジューリングについてまとめました。
組み込み開発でRTOSが使われる理由が少しは理解頂けたかと思います。

RTOSでは他にも以下のようなトピックを理解するとより理解が深まると思いますが、記事が長くなってしまうので今回はここまでにしようと思います。

  • タスク切り替え時の動作
  • タスク間通信
  • 排他制御

(汎用OSでも並列処理を行う上で必要なトピックでもあります)

技術イベントの紹介

最後に弊社技術イベントの紹介です。

2024/12/10(火)に弊社の技術イベントを開催します。
実際のIoTプロダクト開発から得られた知見や、時系列データ処理の経験を中心にLTとして共有する場をご提供します。都心の快適な空間で、技術的な対話と交流を深める夜をご一緒しませんか。
弊社のプロダクト開発の実態や、エンジニアの活躍するフィールドを知っていただける絶好の機会となっております。

コンテンツの詳細参照や参加登録は以下のリンクからぜひお願いします!
https://s-mat.connpass.com/event/336943/

参考文献

株式会社エスマット

Discussion