リアルタイム カーネル

2023年6月18日日曜日

17. 組み込みシステム

t f B! P L


はじめに

今月のインターフェースは「ゼロから作るOS」とのこと。
組み込みエンジニアだと、コンテキストスイッチあたりは心がざわつくじゃないですか。
それで、30年前に作ったリアルタイム カーネル(もどき)を Windows環境(x86)で動くように手直ししてみました。Excelでも制御工学でもないですが、組み込みエンジニア向けってことで。。


Visual Studioでブレークポイントを設定して、動作確認している様子ですが、Bloggerにアップすると画像がぼけてしまいます。Twitterの画像の方(→こちら)が鮮明です。

ダウンロード

詳細説明は、後ほど追記するとして、ソースコードはこちら→リアルタイムカーネル
コメント含めて500行未満です。
開発環境:Visual Studio 2019(無料版) x86モードのコンソールアプリ

目的

リアルタイムOSのカーネルがどのように動作するのかをPCで確認できるようにリアルタイムカーネル(もどき)をWindowsに実装しました。
Visual studioのブレークポイントを使って、振る舞いを確認していくと、カーネルの動作が見えてきます。

まずは用語の説明から

リアルタイムOS周りの説明では、専門用語がいくつか出てきて、「分かるやつだけ分かればいい」になりがちなので、最初に説明しておきます。

タスク

非同期に発生する外部または内部のイベントに応じて切り替える処理の単位をタスクと呼びます。各タスクには、優先順位を定め、この優先順位に従って処理がスケジューリングされます。今回提示したプログラムでは、Cの関数 (task_A(), task_B(), task_C(), task_D()) をタスクとして扱っています。

タスクコントロールブロック(TCB)

タスクを管理するデータ。複数のタスクを優先順位の順に繋げたリンクリストの構造で管理する。

スタック

スタックはCPUが管理するLIFO型のデータ構造です。
Cのプログラムで、関数を呼び出した時の戻りアドレスや、引数、呼び出した関数のローカル変数はスタック上に確保されます。また、割り込みが発生した場合も、戻りアドレスやCPUの各レジスタはスタック上に退避します。リアルタイムOSでは、スタックの構造や使われ方を利用してタスクを切り替えます。

コンテキスト

タスクの処理に使われているCPUのレジスタやスタックなどのリソースのこと。
これらのリソースをスタックに退避することでタスクを切り替えますが、この処理をコンテキストスイッチと呼びます。

ディスパッチ

コンテキストを退避し、タスクを切り替えることをディスパッチと呼びます。

リアルタイムカーネル

このブログでは、タスクコントロールブロックで管理された情報をもとに、優先順位に従って処理をスケジューリングし、タスクを切り替える処理をリアルタイムカーネルと呼んでいます。

リアルタイムOS

上記のリアルタイムカーネルに周辺デバイスのドライバーやセマフォなどの調停機能、様々なサービスコールを統合したソフトウエア。パッケージソフトウエアになると、開発環境も統合され、本当の導入目的は開発環境の確保ではないか?と思うこともしばしば。(個人の感想)

注意事項

  • Visual studio 2019 x86モードのコンソールアプリでのみ動作確認しています。(x64モードでは動作確認していません。)
  • リアルタイムOSは、非同期に発生する外部または内部のイベントに対応する処理(タスク)の起動を定められた優先順位に従ってスケジューリングします。
    通常、組み込みシステムでは、外部または内部のイベントは、割り込みによって通知されますが、カーネル処理中は、タスク・スケジューリングの一貫性を保つために割り込みを禁止するなどの対処が必要です。

    今回のリアルタイムカーネルは、カーネルの動作をPC上で確認することを目的としているため、その対処ができていません。このため、今回Windows上に実装した状態でのリアルタイムカーネル(もどき)は、実用的なものではないことをご理解いただいたうえでお読みください。

リアルタイムカーネルの機能概要

リアルタイムカーネルは、各タスクに設定した優先順位により、各タスクを停止 (STOP)、実行 (RUN)、実行可能 (READY)、中断 (SUSPEND)の 4つ のステータスで管理しつつ、イベントにより起動を要求されたタスクの中で、最も優先順位の高いタスクに実行権を与えることでタスクを切り替えていきます。

タスクステータスの状態遷移

RUN

RUNス テータスのタスクは常にただ 1つであり、その時点で起動要求のあったタスクの中の最も優先順位の高いタスクがRUNステータスになります。RUNステータスになったタスクには実行権が与えられます。

READY

起動要求があった時点で、より優先順位が高いタスクが実行されていた場合は、起動要求タスクは実行可能状態であるREADYステータスとして管理します。READYステータスになるタスクは複数になるため、優先順位順に並べたTCBのリンクリストで管理します。

SUSPEN

より優先順位が高いタスクの起動要求により、実行中のタスクの処理が中断された場合は、そのタスクは、SUSPENDステータスとして管理します。SUSPENDステータスになるタスクも複数になるため、優先順位順に並べたTCBのリンクリストで管理します。

STOP

STOPステータスのタスクは、処理要求がないか処理が終了した状態のタスクです。

リアルタイムカーネルの起動タイミング 

リアルタイムカーネルは、非同期に発生する外部または内部のイベントに対応するタスクの起動を定められた優先順位に従ってスケジューリングします。

リアルタイムカーネルが呼び出されるのは下記の①②のケースです。

 ① タスクの起動要求 が発行された時
 ② 実行中のタスクが終了した時 

①のタスクの起動要求(task_sw)は、イベントに同期しているため、アプリケーション側で記述ができます。

一方、②のタスク終了時の再スケジューリング処理は暗黙のルールです。タスクを切り替えるディスパッチ処理は、起動するタスクから戻ってきたら自動的に再スケジューリングが実行されるように、再スケジューリング処理(reschedule)のエントリアドレスをスタックに積んでからタスクを切り替えることでこの処理を実現しています。



リアルタイムカーネルのコンテキストスイッチ

今回のリアルタイムカーネル(もどき)のコンテキストスイッチは非常に簡単です。

実は、一般的なリアルタイムOSには、タスクの状態として、Run, Ready, Suspend, Stop 以外に Wait があります。 Wait は、優先順位が最も高いタスクが、下位のタスクに一時的に実行権を渡している状態です。

今回のリアルタイムカーネル(もどき)には、このWaitの機能を実装していません。

Waitの状態がないと、常に優先順位が最も高いタスクが実行されることになります。そうすると、タスクごとにスタックを持つ必要がなくなり、また、レジスタの退避などもコンパイラがやってくれるので、コンテキストスイッチは非常に簡単なもので済みます。

リアルタイムカーネルの動作を確認

動画のリアルタイムカーネルの動作を確認していきます。

Visual studioでブレークポイントを使って、振る舞いを確認した動画ですが、カーネルの動作が見えてきます。

プログラムですが、main() で4つのタスク task_A, task_B, task_C, task_D を生成し、task_sw() により task_A を起動しています。

タスクの優先順位は、D > A > C > B です。

task_A では、 task_sw() により task_B, task_C, task_D を起動します。

ブレークポイントは、以下に設定しています。

 ・各タスク( task_A, task_B, task_C, task_D )の先頭

 ・リアルタイムカーネル
   タスク起動処理 task_sw()
   再スケジューリング処理 reschedule()
   タスクを切り替えるディスパッチ処理 dispatch()

動画の右側のウインドウにブレークポイントで止まった関数名が表示されます。
その動作を図にすると下記になります。


優先順位 D > A > C > B に従って各タスクが起動されています。
タスク起動の際、Suspendタスクの再起動は既に実行されていたため、dispatchを経由せずReturnで起動できます。また、Readyタスクの起動では、dispatch経由での起動になります。

組み込み向けに修正するには

本来、タスクスケジューリングの一貫性を確保するために、リアルタイムカーネルの処理は、割り込み禁止状態で実行し、起動するタスクから割り込みを許可状態に戻すべきです。

具体的には、task_sw や reschedule で、ソースコード上でコメントアウトしている _disable と _enable で割り込みの制御が必要です。

また、dispatchでは、フラグレジスタへの設定値を、割り込みを許可する設定データにしてスタックに積んだうえで、x86の iret に相当する命令で次のタスクに実行権を渡す構成に変更が必要です。


最後までお読みいただき、ありがとうございました。
リアルタイムカーネルの理解のお役に立ったでしょうか。


QooQ