オリジナルOS「MicrOS」の設計と実装(1) ―― MicrOSの概要とソースの概観
1-3.組み込みOSの基本機能とMicrOS
組み込みOSには二つの役割があります.一つは「マルチプログラミング環境を提供すること」,もう一つは「組み込みOSを組み込んだアプリケーション・システムに対して,サービスを行うこと」です.この二つの役割をできるだけ短時間で,無駄なく処理することが要求されます.MicrOSはマルチタスク制御方式によってマルチプログラミング環境を提供します.
アプリケーション・システムは起動時にコールされる__aplMain関数(リスト12)の中で,ハードウェアの初期設定やアプリケーション・システムの初期設定とともに,__task関数によってタスクを登録します.__task関数に指定するパラメータはタスクの処理を行う関数と,その処理に必要なスタックのサイズ,および処理を行うときのプライオリティ(優先順位)です.MicrOSには,あらかじめシステム制御に必要な領域として定義されたシステム・コントロール・ブロックがあります.__task関数はシステム・コントロール・ブロックの中にタスクを管理するのに必要な領域を確保して,その先頭アドレスを返り値として__aplMainに通知します.この領域をTCB(Task Control Block)といいます.
リスト12 aplMain関数(Application.c内)
/*========================================================= | application main | +=========================================================* void aplMain(void) { /*** Driver Initialize(after hardware_initialize) ***/ PMCT.6 = 0; /* LED pct6 *//*** application system initialize ***/
_initTimer();
tmcnt = __sec(1); /* LED on/off cycle default 1sec */
LEDsw = 0;
opn_sem((sem0 = &semcb0), 1);
mbx1_rqbl = 0; /* mbx_task:link set */
nrel_flag = 0; /* led_task:update */
#if USING_UART
_uartopen(_UART_1S|_UART_NP|_UART_8L|_UART_LF|_UART_EI|_UART
_RCVECHO,
9600, uartbuf, UARTSSZ, UARTRSZ);
#endif#if USING_DEBUG
__dbginit(debugbuf, DBGBFSZ);
__dbgselect(0x00000001);
#endif
/*** Entry Task(after driver initialize) ***/
/*** System Timer Task ***/
#if USING_TIMERTASK
__aplTCBList[0] = __task(&__tmr_task, 4*64, 3);
#endif#if DEBUG_FUNC
__aplTCBList[2] = __task(&debug_task, 4*256, 5);
__aplTCBList[3] = __task(&debug_task_sub, 4*64, 6);
#else /* DEBUG_FUNC */#if USING_CMDTASK
/*** System Console Task(128:_sprintf using stack) ***/
__aplTCBList[1] = __task(&__cmd, 4*64+128, SYSNPRI-1);
_uartsetcb(_UART_RCVCB_TASK, __aplTCBList[1]);
#endif
#if USING_CMDCB
_uartsetcb(_UART_RCVCB_CHAR, __cmdcb);
#endif
/*** entry Application Task[TCB *_task(int (*proc)(), int stkSize, int pri)] ***/
__aplTCBList[3] = __task(&mbx_task, 4*256, 6);
__aplTCBList[4] = __task(&led_task, 4*256, 4);
__aplTCBList[5] = __task(&evt_or_task, 4*256, 6);
__aplTCBList[6] = __task(&evt_and_task, 4*256, 5);
// __aplTCBList[6] = __task(&_task, 4*256, 5);
#endif /* DEBUG_FUNC */
}
TCBにはスタック領域とタスクを制御する情報が含まれます.タスクを制御する情報はタスクの先頭部分に置かれ,TCBの上限アドレスからタスクの制御情報に向かってスタックが使われます.TCBというときにはタスクの制御情報の部分だけを示すことがあるので注意してください.
通常のCプログラムはmain関数からスタートしますが,組み込みシステムではこのmainに相当する関数が複数あることがあります.これらがすべてmainの名称を持っていたら,システムを構成する関数を同時にビルドする組み込みシステムでは区別ができないので,こうしたmain関数にも"main"以外の名称を付けて,タスク処理プログラム部分とします.
_aplMain関数の処理が終了するとMicrOSの最終的な初期設定を行って,マルチプログラミングの制御が開始されます.マルチプログラミングは日本語では並行処理といいますが,登録されたタスクが完全に並列に実行されているわけではありません.タスクの処理を短い時間で切り替えながら実行しているのです.このときに処理が中断されてその次に処理するまでの間,CPUの処理経過を保持しておかなければなりません.その保持に使われるのがTCBです.保持する情報とは,具体的にはCPUのレジスタ類です.
タスクが処理するときの状態を,MicrOSは次の三つで管理します(図2).
図2 タスクの実行状態
- Ready状態 :処理を実行できる状態
- Run状態 :タスクが処理を行っている状態
- Wait状態 :タスクが処理を中断しなければならない状態
Ready状態のタスクはシステム・コントロール・ブロックのReadyプール(プライオリティ・テーブルともいう)にリンクされています.タスクが登録された状態はReady状態です.
Run状態になることを,「タスクに実行権を割り当てる」と表現することがあります.Run状態のタスクは一つしか存在しません.Readyプールの中から最も実行優先度の高いタスクに実行権が与えられ,TCBにセーブされているCPUの状態が書き込まれます.この処理を行うのがディスパッチャです.これによってタスクの処理が継続します.
Run状態のタスクがシステム・コールを発行し,そのシステム・コールが__wait関数を使っているときにタスクはWait状態になります.ほかのタスクからこのタスクを指定した_active関数または割り込み処理の中で,このタスクを指定した_activex関数が実行されたときにWait状態は解除されます.Wait状態が解除されるとタスクはReadyに戻ります.この動作にはハードウェアの割り込み機構が大いに寄与しています.