タスク

タスクとは

今回からは、リアルタイムOSが提供する機能について説明します。

毎回サンプルプログラムを示しながら、リアルタイムOSが提供する機能について具体的に説明していきます。なお、本稿で説明するサンプルプログラムは、T-Engineフォーラムが無償でソースコードを配布しているT-Kernel 2.0で実際に動作させることができます。

T-Kernel 2.0は、ARM11を搭載したT-Engineリファレンスボードに対応していますが、Windows PCで動作するCPUエミュレータ(ARM11をエミュレートします)や統合開発環境(Eclipse)も用意してあります。Windows PCがあればT-Kernel 2.0のプログラムを動作させることができますので、是非、毎回プログラムを実行して実際のリアルタイムOSの動作を確認してみてください。

タスクとは?

一般に、組み込み機器にはいろいろな機能が必要とされます。例えば、センサーの値を読み取って結果を解析するとか、画面に図形を描画するとか、ネットワーク経由で外部と通信するとか。これらはそれぞれが独立して動作できる機能です。

T-Kernel 2.0(以降、単にT-Kernelとします)には、このような複数の処理を並列して実行させる機能があります。T-Kernelでは、このような並列に実行する際の処理の単位を「タスク」と呼びます。T-Kernelで動作させるプログラムは、一つのタスクの中では逐次的に実行されますが、異なるタスクの間では並行して実行されることになります。

ただし、「並行して実行される」というのはアプリケーションからみた概念的な動作であり、実際にはT-Kernelが自動的に複数のタスクを切り換えて実行しています。

図1

【T-Kernelが機能毎のプログラムをタスクとして実行】

では、具体的にはどうやってタスクを記述するのか?

T-KernelのタスクはC言語の関数として記述できます。C言語で関数を作って、それをT-Kernelに登録すれば、後はT-Kernelがタスクとして実行してくれます。

例えば、以下の例では5行目〜12行目と14行目〜21行目の関数がタスクとして実行されるプログラムになります。2つとも一定時間毎にメッセージを出力するだけのシンプルなタスクですが、基本的なタスクの書き方はこのプログラムに含まれています。この基本さえおさえておけば、T-Kernelを使ったマルチタスクのプログラムを開発することができます。

【リスト1:タスクAのdoWorkAはタスクBのdoWorkBが完了してから実行する】

									#include <basic.h>							  /* 型などの基本的な定義    */
									#include <tk/tkernel.h>						  /* T-Kernel関連の定義      */
									#include <tm/tmonitor.h>						  /* モニタ機能を使う場合の定義 */

									void	taskA( INT stacd, VP exinf )			/* タスクA(関数名は任意)	   */
									{
										while(1){
											tm_putstring( (UB*)"I'm Task A.¥n" );
											tk_dly_tsk( 1000 );						/* 処理を1秒間保留      */
										}
										tk_ext_tsk();								/* タスクの終了        */
									}

									void	taskB( INT stacd, VP exinf )			/* タスクB(関数名は任意)	  */
									{
										while(1){
											tm_putstring( (UB*)"I'm Task B.¥n" );
											tk_dly_tsk( 2000 );						/* 処理を2秒間保留      */
										}
										tk_ext_tsk();								/* タスクの終了        */
									}

									EXPORT	INT	usermain( void )					/* 初期タスクから呼ばれる関数 */
									{
										T_CTSK	ctskA = { NULL, TA_HLNG|TA_RNG0, taskA, 1, 4*1024 };
										T_CTSK	ctskB = { NULL, TA_HLNG|TA_RNG0, taskB, 1, 4*1024 };
										ID	tskIdA;									/* タスクAの識別子      */
										ID	tskIdB;									/* タスクBの識別子      */

										tskIdA = tk_cre_tsk( &ctskA );				/* T-KernelにタスクAを登録 */
										tk_sta_tsk( tskIdA, 0 );					/* タスクAの実行を開始    */

										tskIdB = tk_cre_tsk( &ctskB );				/* T-KernelにタスクBを登録 */
										tk_sta_tsk( tskIdB, 0 );					/* タスクBの実行を開始    */

										tk_slp_tsk(TMO_FEVR);						/* 起床待ち状態に移行     */
										return 0;
									}
									
図2

【2つのタスクの基本動作】

タスク間通信

組み込みシステムの場合は、各タスクが連携して一つの処理単位となることも多いものです。その時に使用するのが、「タスク間通信」と呼ばれるものです。タスク間通信を上手に使いこなせると、組み込みシステムの設計が楽になることと、実際に動いた時は、とても感慨深いものになります。ぜひ使いこなせるようにしましょう。

イベントフラグによる割り込みとの同期

タスク間通信で、イベントフラグを使用すると、「〇〇がセットされたら、△△をする」と言った処理が簡単に「システマチック」に組み込むことができます。これらの処理は、以前から「フラグで待つ」とか、「ポーリングで待つ」などで活用されていました。RTOSでは、データのサイズに応じて、イベントフラグやセマフォ、メールボックス、データキュー、メッセージバッファ、ランデブーなどの仕組みが用意されています。

組み込みシステムでは、周辺機能や外部の要因に対する外部割り込み機能が数多く実装されています。これを有効に活用するために必要なのが、「割り込み」です。例外という言い方もします。この割り込みを使用して、どうシステムを構築していくかで、システムの性能やパフォーマンスも改善することがあります。

もちろん、RTOSを使用した場合と、そうでない場合の差は、0ではありませんが、扱いやすさはかなり違います。