オリジナルOS「MicrOS」の設計と実装(1) ―― MicrOSの概要とソースの概観
MicrOSは__wait関数と__active関数の処理に内部割り込みを使用します.この割り込み処理の中で内部割り込みを発生させたタスクは現在状態をTCBにセーブします.このとき,コンパイラのレジスタ使用規約で,ワーキング・レジスタかアーギュメント・レジスタとされるレジスタはセーブされません.これらはレジスタ使用規約上,コールされた関数側ではセーブしなくても良いレジスタになっているからです.MicrOSはこの領域を,システム・コールを処理するときのワーク・エリアとして利用します.TCBに現在状態をセーブした後,アクティブ系のシステム・コールのときはReadyプールに登録します(ウェイト系のシステム・コールの場合は__wait関数をコールする前にウェイトに必要な処理は済んでいる).こうした処理の後,ディスパッチャにジャンプします.
なお,システム・コールであってもコールされたシステム・コールの中で複数のタスクのウェイトを解除するような処理については,__active関数の代わりに__activex関数とこの内部割り込み関数へのコールを組み合わせなければなりません.
また,V850-MicrOSの内部割り込み処理はこのようになってはいません.CA850の割り込み処理機構を利用しているためです.V850-MicrOSではTCBのワーキング・レジスタなどの領域にはレジスタをセーブせず,単なるワーク・エリアになっています.そのため,すべてのレジスタはスタック領域にセーブするようになっています(V850-MicrOSの割り込み処理はコンパイラの割り込み処理とMicrOSの割り込み処理が重複しており,アセンブリ・コード版のMicrOSに比べてオーバヘッドが大きい).
MicrOSがこのような処理方法をとっているのはシステム拡張性のためです.アプリケーション・システムが新機能をシステム・コールの形で組み込む場合には,この規約を守ればよいだけなので簡単です.V850-MicrOSでは割り込み処理にCA850の処理機構を利用したので,__wait関数や__active関数もC言語で組み込みました.また,__wait関数や__active関数からディスパッチャまでの処理をアセンブリ・コードで組み込むことにより,かなり高性能の組み込みOSとなります.もっとも,現在組み込まれているシステム・コールの処理は単純なので,すべてアセンブリ・コードにしたところでたかがしれています.
組み込みOSは,一度作成してしまえば再利用可能な部分の多いプログラムです.できるだけ高性能であるべきで,アセンブリ・コードを使う要素の高いプログラムと言えるでしょう.この辺りの詳細な説明は次回以降にあらためて行います.
なお,V850-MicrOSのTCBの先頭部分はリスト13のような形式になっています.
リスト13 TCBの先頭部分
typedef struct _TCB_T{ struct _TCB_T *next; /* next link */ struct _TCB_T *prior; /* prior link */ union{ struct _TCB_T *tcb; /* (unused) */ struct{ unsigned char typ; /* task type */ unsigned char pri; /* execution priority */ unsigned char sts; /* task status */ unsigned char prs; /* orginal priority(unused) */ } s; } u; unsigned int p0; unsigned int p1; unsigned int p2; unsigned int p3; unsigned int sp; unsigned int psw; unsigned int pcr; unsigned int pc; } _TCB;
1-5.システム・コールの処理
MicrOSのシステム・コールには次のようなタイプのものがあります.
- その場でサービスを完了するもの
- その場でサービスが完了するが,データを蓄えなければならないもの
- データを蓄えるにあたって可変個のデータを保持しなければいけないもの
- タスクのウェイトが伴うもの
具体的にMicrOSのシステム・コールを示しながら,こうしたタイプの処理方法の概要を以下に示します.
まず,その場でサービスが完了するものの代表は__getCurTcb関数です.これは,関数をコールしたタスクのTCBアドレスを返す関数です.タスクが実行するとき,TCBのアドレスはシステム・コントロール・ブロックにセーブされています.それを持ってきて返り値とするだけです.この処理には割り込み禁止にするなどといった排他制御も不要です.
排他制御が必要なシステム・コールがあります.例えばイベント・フラグ更新用サービス・リクエストのsnd_evt関数です.イベント・フラグはどの処理で必要とされるか分からず,複数のタスクで別々に必要とされるのかもしれません.このようなシステム・コールについては,アプリケーション側でイベント・フラグを構造体として定義し,イベント・フラグのアドレスをsnd_evtで指定するような処理方法を採用します.イベント・フラグを定義するシステム・コールがopn_evtです.このシステム・コールの第1パラメータがイベント・フラグを定義する構造体_EVTCBです.opnDentはイベント・フラグを初期化します.snd_evtの第1パラメータにこの構造体を定義したアドレスを指定してイベント・フラグを更新します.