
【設計しながら学ぶRTOSの基本編】第6回 タスクからRTOSに制御が移る時
こんにちは、南角です。
前回の宿題から続けましょう。
単方向リストの場合は実装方法によっては挿入時には先頭に挿入して、取り出す時はリストを最後まで辿ります。このたどる時間がリストの要素に左右されます。
逆の実装で挿入時にたどって、取り出す時は先頭からという実装も可能です。
この場合は取り出す時の時間は一定ですが、挿入処理の時間が要素の数に左右されます。
では、挿入時も取り出す時も要素の数(システムレディキューにおいては実行可能なタスク数)に左右されないためにはどうすればよいでしょうか?
というのが前回の宿題でした。
応えは簡単で、上記の単方向リストの二つのやり方を両方採用すればよいわけです。
つまり制御構造にリストの先頭と最後の要素へのポインタを持たせればよいわけです。
細かい説明は省略します。つぎのサンプルプログラムを見てください。
レディキューの場合はdataに相当する部分がTCBになります。
/* 双方向リスト */
struct QUEUE_ELEMENT {
struct QUEUE_ELEMENT *prevp; // older data
struct QUEUE_ELEMENT *nextp; // newer data
int data;
};
static struct QUEUE_ELEMENT *qhead = NULL; // dequeu here
static struct QUEUE_ELEMENT *qtail = NULL; // enqueue here
void enqueue(int);
int dequeue(void);
void enqueue(int data) {
struct QUEUE_ELEMENT *newqp;
/* allocate new element */
newqp = (struct QUEUE_ELEMENT*)malloc(sizeof(struct QUEUE_ELEMENT));
newqp->nextp = NULL; // newer data
/* set data */
newqp->data = data;
if (qhead == NULL) { // first data
qhead = newqp;
qtail = newqp;
newqp->prevp = NULL;
}
else {
qtail->nextp = newqp;
newqp->prevp = qtail;
qtail = newqp;
}
return;
}
int dequeue(void) {
struct QUEUE_ELEMENT *targetqp;
int data;
targetqp = qhead;
/* get data */
data = qhead->data;
if (qhead->nextp == NULL && qhead->prevp == NULL) { // last data
qtail = NULL;
qhead = NULL;
}
else {
qhead->nextp->prevp = NULL; // indicate top data
qhead = qhead->nextp;
}
/* free old element */
free(targetqp);
return data;
}
ここであらためてタスク(アプリケーション)からRTOSに制御が移るタイミングを考えてみましょう。
次の3つの場合があります。
- (1) システムクロック(システムチック)タイマー割り込み
- (2) ドライバの終わり
- (3) システムコール
(1) はRTOS専用の割り込みです。RTOSはこの周期割り込みを基準にdelayやsleepを実施します。またドライバの割り込みが全く発生しない場合でも、少なくともこの周期ではタスクからRTOSに制御が移ることは保証します。
基本的にこの割り込みはRTOS専用の割り込みですが、VxWorksではこの割り込み発生時に呼び出してほしい処理を登録(コールバック)することもできます。
(2) 各種ドライバの割り込み処理(ISR)の終わりには、原則としてRTOSを呼び出します。なぜならドライバの割り込みが何らかのイベントの終了である場合は、それを待っていたタスクの状態が変化する可能性があるからです。
たとえばハードディスからデータの読み込みをreadの中のsemTakeで待っていたタスクが、ドライバからsemGiveにより待ちを解除された(待ち状態から実行可能状態に状態が変化)場合もあり、再スケジューリングの必要があるからです。
なお、VxWorksにおいてはintConnectを用いてISRを登録した場合は、ドライバの割り込み処理の最後に自動的にVxWorksのスケジューラを呼び出し、intVecSetを用いてISRを登録した場合はスケジューラを呼び出しません。
(ただし例外もあります、たとえばVxWorksはハードウェア的に割り込みベクターテーブルをサポートしていないRISC系のCPUに対してもソフトウェア的に割り込みベクターテーブルを提供していますが、その場合たとえばMIPSではintVecSetを使用しても、ISRの最後でスケジューラを呼び出していました。)
(3) タスクからシステムコールを呼び出す場合ですが、この場合は同じシステムコールでもOSに制御が移る場合と移らない場合があります。たとえばexitの場合は、それまで実行していたタスクが終了するわけですから、かならずOSのスケジューラが呼び出されます。
一方semTakeのような場合はもしセマフォの数が1以上であれば、単にその数を1減らすだけで、単なる関数呼び出しのようにそのまま実行を続けます。しかしセマフォの数が0の場合は、そのタスクはレディキューからそのセマフォの解放待ち行列に繋ぎかえられるわけですから、もちろんRTOSに制御が移ります。
なお、タスクからOSのサービスを呼び出す場合にソフトウェア割り込み(いわゆるSVC - スーパーバイザーコールですね)を使用するRTOSもありますが、割り込み処理には時間がかかるため通常のVxWorksではこの方式は使用しないため説明を省略します。
さて今回までで、不十分ながらRTOS内部の基本的な部分の説明を終えましたが、説明がばらばらになってしまいました。
次回で今までの説明をまとめて全体像を組み立ててみたいと思います。
また今まで、ほとんど触れてこなかったマルチコアの特にコア間の排他制御に関して述べてみたいと思います。
では、また次回。
設計しながら学ぶRTOSの基本編 第6回 おわり
■著者プロフィール
南角 茂樹(なんかく しげき)
大阪電気通信大学 総合情報学部 メディアコンピュータシステム学科 准教授、同大学院総合情報学研究科(コンピュータサイエンス専攻)、エイシップ・ソリューションズ株式会社 研究顧問。
慶應義塾大学工学部数理工学科卒業後、大手電機会社を経て現職。組み込みシステムおよびリアルタイムOSを専門とし、著書、解説記事、発表・講演、登録特許等多数。
ページの先頭へ戻る »

