
		TOPPERS/ASPカーネル
		設計メモ

		対応バージョン: Release 1.7.0
		最終更新: 2011年3月31日（作成中）

このドキュメントは，TOPPERS/ASPカーネルの設計メモである．作成中のもので
あり，網羅的ではない．

----------------------------------------------------------------------
 TOPPERS/ASP Kernel
     Toyohashi Open Platform for Embedded Real-Time Systems/
     Advanced Standard Profile Kernel

 Copyright (C) 2005-2011 by Embedded and Real-Time Systems Laboratory
             Graduate School of Information Science, Nagoya Univ., JAPAN
 
 上記著作権者は，以下の(1)〜(4)の条件を満たす場合に限り，本ソフトウェ
 ア（本ソフトウェアを改変したものを含む．以下同じ）を使用・複製・改
 変・再配布（以下，利用と呼ぶ）することを無償で許諾する．
 (1) 本ソフトウェアをソースコードの形で利用する場合には，上記の著作
     権表示，この利用条件および下記の無保証規定が，そのままの形でソー
     スコード中に含まれていること．
 (2) 本ソフトウェアを，ライブラリ形式など，他のソフトウェア開発に使
     用できる形で再配布する場合には，再配布に伴うドキュメント（利用
     者マニュアルなど）に，上記の著作権表示，この利用条件および下記
     の無保証規定を掲載すること．
 (3) 本ソフトウェアを，機器に組み込むなど，他のソフトウェア開発に使
     用できない形で再配布する場合には，次のいずれかの条件を満たすこ
     と．
   (a) 再配布に伴うドキュメント（利用者マニュアルなど）に，上記の著
       作権表示，この利用条件および下記の無保証規定を掲載すること．
   (b) 再配布の形態を，別に定める方法によって，TOPPERSプロジェクトに
       報告すること．
 (4) 本ソフトウェアの利用により直接的または間接的に生じるいかなる損
     害からも，上記著作権者およびTOPPERSプロジェクトを免責すること．
     また，本ソフトウェアのユーザまたはエンドユーザからのいかなる理
     由に基づく請求からも，上記著作権者およびTOPPERSプロジェクトを
     免責すること．
 
 本ソフトウェアは，無保証で提供されているものである．上記著作権者お
 よびTOPPERSプロジェクトは，本ソフトウェアに関して，特定の使用目的
 に対する適合性も含めて，いかなる保証も行わない．また，本ソフトウェ
 アの利用により直接的または間接的に生じたいかなる損害に関しても，そ
 の責任を負わない．
 
 $Id: design.txt 2068 2011-05-06 13:39:26Z ertl-hiro $
----------------------------------------------------------------------

○目次

・TOPPERS/ASPカーネルの実装設計方針
・システム状態とコンテキストの実装
	- カーネル動作状態と非動作状態
	- タスクコンテキストと非タスクコンテキスト
	- 全割込みロック状態と全割込みロック解除状態
	- CPUロック状態とCPUロック解除状態
	- 割込み優先度マスク
	- ディスパッチ禁止状態とディスパッチ許可状態
	- ディスパッチ保留状態
・タスク状態の管理とスケジューリング
	- タスク状態の管理
	- タスクスケジューラ
・タスクディスパッチ処理の実装（★未完成）
	- タスクディスパッチ処理の必要なタイミング
	- タスクディスパッチャの構造
・タスク例外処理機能の実装
	- タスク例外処理ルーチンの実行開始条件とシステム状態（仕様の確認）
	- タスク例外処理ルーチンの呼出し処理
	- タスク例外処理ルーチンの実行開始が必要なタイミング
	- タスク例外処理ルーチンの実行開始処理
	- call_texrtnからdispatchを呼び出す処理について
・CHECKマクロとgoto文の使用
	- CHECKマクロの定義とその使用法
	- 設計意図
	- CHECKマクロを使用してよい条件
	- 問題を生じることがない根拠
・ext_tsk，ext_kerの返り値
・カーネルのデータ構造に対するvolatile宣言について
・型キャストに伴う警告メッセージ
・優先度データキュー機能（未完成）
・性能評価用システム時刻参照機能
	- 必要性と使途
	- API仕様
	- 実装
・タスク例外処理禁止フラグをenatexで実装している理由


○TOPPERS/ASPカーネルの実装設計方針

TOPPERS/ASPカーネル（以下，ASPカーネル）は，TOPPERS新世代カーネルの出発
点となるリアルタイムカーネルである．TOPPERS新世代カーネル仕様の設計方針
と，ASPカーネルの適用対象領域と設計方針については，TOPPERS新世代カーネ
ル統合仕様書に述べられている．

以下では，ASPカーネルの実装設計方針について述べるが，仕様設計方針とも関
連しており，明確に分離できない部分もある．

TOPPERS/ASPカーネルの実装設計を行うにあたり，次の方針を設定する．

(1) ソースコードの読みやすさ・改造しやすさを重視する

ソースコードが読みやすいことは，オープンソースソフトウェアの品質を向上
させる上で最も重要な特性である．ソースコードを理解している技術者が増え
ることで，問題を早期に発見することができ，サポート体制も充実させること
ができる．また，ソースコードが読みやすいことは，シンプルな設計がされて
いることも意味しており，信頼性向上にもつながる．さらに，技術者教育のた
めの教材とする観点からも，ソースコードが読みやすいことは重要となる．

改造しやすいことは，システム毎の要求にあわせたチューニングが行いやすい
ことを意味しており，擦り合わせ型の開発を支援する性質である．また，ASPカー
ネルを基盤としてTOPPERS新世代カーネルシリーズを開発していく上でも，改造
しやすいことは必須の条件である．

(2) 新しいターゲットシステムへのポーティングが容易な構造とする

組込みシステムには多様なハードウェアが用いられるため，それらに容易にポー
ティングできることは重要な性質である．そのために，実行性能に配慮しつつ
ハードウェアを抽象化し，ターゲットシステムに依存する部分（ターゲット依
存部）と依存しない部分（ターゲット非依存部）を明確に分離する．また，開
発環境（コンパイラなど）に依存する部分も明確に分離する．

(3) 検証が容易な構造とする

信頼性を確保するために，検証が容易な構造とする．

具体的には，サービスコールのほとんど全体を割込み禁止で実行することとし，
サービスコールの処理途中で割込みを許可しない．この構造は，読みやすく改
造しやすいソースコードにするためにも有効である．これにより割込み応答性
が犠牲になるが，それによって問題を生じるアプリケーションは少数であり，
やむをえないものと考える．

また，条件コンパイル等によりコンフィギュレーションできる箇所を増やすと，
細かな最適化ができる一方で，検証すべき組合せが増えることから，コンフィ
ギュレーションできる箇所は必要最低限とする．

(4) 実行性能とメモリ使用量に配慮する

上記の方針を満たした上で，高い実行性能と小さいメモリ使用量を達成できる
ような実装を行う．実行性能を向上させる際には，平均性能の向上よりも，最
悪時性能の向上を重視する．

ソースコードの読みやすさを重視すると言っても，実行性能の悪いアルゴリズ
ムを安易に採用することはせず，高い実行性能を達成できるアルゴリズムを用
いる．ただし，新しいターゲットシステムへのポーティングを容易にするため
に大部分をC言語で実装しており，すべてをアセンブリ言語で記述した場合に比
べて実行性能が落ちるのはやむをえない．

メモリ使用量については，RAMの使用量を削減することに重点を置いた設計を行
うが，上記の方針および実行性能とのトレードオフを考慮し，ぎりぎりまでの
削減は行わない．

(5) スケーラビリティに配慮する

様々な規模のシステムに適用できるスケーラビリティをもった構造とする．特
に，小規模なシステムに適用する際に，使用しない機能をカーネルが持ってい
ることによるメモリ使用量の増加が最小限になるように配慮する．

具体的には，アプリケーションとカーネルを1つのロードモジュールにリンクす
る方法（1リンクモデル）を想定し，カーネルを関数単位でライブラリ化して，
使用する関数のみをリンクできる構造とする．これは一種のコンフィギュレー
ションであるが，この方法は，条件コンパイルによるコンフィギュレーション
とは違い，検証工数に与える影響が小さい．

また，固定的に使用するRAM領域を減らし，スタックに置ける情報はできる限り
スタック上に置く．


○システム状態とコンテキストの実装

この章では，「TOPPERS新世代カーネル統合仕様書」の「2.5 システム状態とコ
ンテキスト」の節に規定されているシステム状態とコンテキストの実装方法に
ついて記述する．

●カーネル動作状態と非動作状態

カーネルの動作状態を管理するために，カーネル動作状態フラグ（kerflg）を
用意する．kerflgは，スタートアップモジュールでfalse（＝0）に初期化する．
また，カーネル動作の開始時にtrueにし，カーネル動作の終了時にfalseにする．

----------------------------------------
bool_t	kerflg = false;
----------------------------------------

kerflgは，sns_kerで参照する．カーネル非動作状態でsns_ker以外のサービス
コールを呼び出した場合の動作は保証する必要がないため，他のサービスコー
ルではkerflgを参照しない．

●タスクコンテキストと非タスクコンテキスト

タスクコンテキストと非タスクコンテキストの切換えは，ターゲットハードウェ
アおよびターゲット依存部に委ねる．また，どちらのコンテキストで実行中で
あるかを判別する関数（sense_context）も，ターゲット依存部で用意すること
とする．

●全割込みロック状態と全割込みロック解除状態

全割込みロックフラグの管理はターゲット依存部に委ね，全割込みロック状態
に遷移させるマクロ（SIL_LOC_INT）と，元の状態に戻すマクロ（SIL_UNL_INT）
は，SILのターゲット依存部で用意することとする．全割込みロック状態である
か否かを判別する機能は，必要がないために用意していない．

全割込みロック状態でsns_kerとext_ker以外のサービスコールを呼び出した場
合の動作は保証する必要がないため，サービスコール中で全割込みロック状態
であることを判別する必要はない．

●CPUロック状態とCPUロック解除状態

CPUロックフラグの管理はターゲット依存部に委ね，CPUロック状態に遷移させ
る関数（t_lock_cpu／i_lock_cpu／x_lock_cpu）と，CPUロック解除状態に遷移
させる関数（t_unlock_cpu／i_unlock_cpu／x_unlock_cpu）は，ターゲット依
存部で用意することとする．また，CPUロック状態であるか否かを判別する関数
（t_sense_lock／i_sense_lock／x_sense_lock）も，ターゲット依存部で用意
することとする．

●割込み優先度マスク

割込み優先度マスクの管理はターゲット依存部に委ね，割込み優先度マスクを
設定する関数（t_set_ipm／i_set_ipm／x_set_ipm）と，それを参照する関数
（t_get_ipm／i_get_ipm／x_get_ipm）は，ターゲット依存部で用意することと
する．

ただし，タスクコンテキストの実行中に，割込み優先度マスク全解除状態であ
ることを効率的に判断するために，割込み優先度マスク全解除状態であること
を示すフラグ（ipmflg）を用意する．

ipmflgは，カーネルの初期化時にfalseに初期化する．非タスクコンテキストで
は，割込み優先度マスク全解除状態になることはないため，このフラグを用い
る必要はなく，フラグの更新も行わない．

----------------------------------------
bool_t	ipmflg;
----------------------------------------

●ディスパッチ禁止状態とディスパッチ許可状態

ディスパッチ禁止フラグを管理するために，フラグ（disdsp）を用意する．
disdspは，カーネルの初期化時にfalseに初期化する．

----------------------------------------
bool_t	disdsp;
----------------------------------------

※ disdspではなく，それを論理反転したenadspを用意した方が，ipmflgや
dspflgとの整合性の観点からは良かった．

●ディスパッチ保留状態

ディスパッチ保留状態は，非タスクコンテキストの実行中，CPUロック状態，割
込み優先度マスクが全解除でない状態，ディスパッチ禁止状態のいずれか（ま
たは，それらが重なった状態）である．

タスクコンテキストの実行中に，ディスパッチ保留状態でないことを効率的に
判別するために，割込み優先度マスク全解除状態であり，ディスパッチ許可状
態である（ディスパッチ禁止状態でない）ことを示すフラグ（dspflg）を用意
する．すなわち，常に「dspflg == (ipmflg && !disdsp)」に設定する．

dspflgは，カーネルの初期化時にtrueに初期化する．また，タスクコンテキス
トにおいて割込み優先度マスクの値が変更されるか，割込み禁止フラグが変更
される度に更新する．非タスクコンテキストでは，常にディスパッチ保留状態
であるため，このフラグを用いる必要はなく，フラグの更新も行わない．

----------------------------------------
bool_t	dspflg;
----------------------------------------


○タスク状態の管理とスケジューリング
	
●タスク状態の管理

タスク管理ブロック（TCB）中のタスク状態を管理するフィールド（tstat）で
は，タスク状態が次のいずれであるかを管理する．

　・実行できる状態
　・休止状態
　・（狭義の）待ち状態
　・強制待ち状態
　・二重待ち状態

タスクが実行できる状態の時に，実行状態であるか実行可能状態であるかは，
このフィールドでは管理せず，実行状態のタスクのTCBを指すポインタ変数
（p_runtsk）によって判別する．実行状態のタスクがない場合は，p_runtskは
NULLにする．

----------------------------------------
TCB	*p_runtsk;
----------------------------------------

p_runtskは，カーネルの初期化時にNULLに初期化し，ディスパッチャにおいて
更新する．サービスコールの処理の中で自タスクに関する情報を参照する場合
は，p_runtskを用いる．

●タスクスケジューラ

タスクスケジューラは，実行できる状態のタスクの中から，最も優先順位が高
いタスク（これを，最高優先順位のタスクと呼ぶ）を決定し，そのタスクの
TCBを指すポインタ変数（p_schedtsk）を設定する．

----------------------------------------
TCB	*p_schedtsk;
----------------------------------------

タスクスケジューラに対しては，どのタスクが実行できる状態であるかを知ら
せる必要がある．そのため，タスクスケジューラは，次の2つの関数を用意する．

　・タスクが実行できる状態に遷移したことを知らせる関数（make_runnable）
　・タスクが実行できる状態から他の状態へ遷移したことを知らせる関数
	（make_non_runnable）

また，処理の効率化のために，上の2つの関数を用いずにレディキューを直接操
作してタスクスケジュールを行う関数として，次の2つの関数を用意する．

　・タスクの優先度の変更（change_priority）
　・レディキューの回転（rotate_ready_queue）


○タスクディスパッチ処理の実装

●タスクディスパッチ処理の必要なタイミング

タスクディスパッチは，実行状態のタスク（p_runtsk）と最高優先順位のタス
ク（p_schedtsk）が一致しておらず，ディスパッチ保留状態でない場合に行う．
このことから，タスクディスパッチ処理を行う必要があるのは，次の3つの場合
である．

(1) 実行状態のタスクが実行できる状態でなくなる

自タスクを広義の待ち状態に遷移させるサービスコールや，自タスクを終了さ
せるサービスコールにおいて，タスクディスパッチ処理を行う必要がある．

(2) 最高優先順位のタスクが変化する

タスクの起動，タスクの待ち解除，タスクの強制待ちからの再開，タスクの優
先度の変更，タスクの優先順位の回転を行うサービスコールにおいて，最高優
先順位のタスクが変化し，ディスパッチ保留状態でない場合には，タスクディ
スパッチ処理を行う必要がある．

(3) ディスパッチ保留状態が解除される

ディスパッチ保留状態とは，非タスクコンテキストの実行中，CPUロック状態，
割込み優先度マスクが全解除でない状態，ディスパッチ禁止状態の総称である
ため，これらの状態のいずれかが遷移するタイミングで，タスクディスパッチ
処理を行う必要がある．具体的には，次のタイミングが該当する．

(3-1) 非タスクコンテキストからタスクコンテキストに遷移する

割込みハンドラまたはCPU例外ハンドラからタスクにリターンする際に，タスク
ディスパッチ処理を行う必要がある．

(3-2) CPUロック状態が解除される

CPUロック状態においては，上記の(1)や(2)の状況を作り出すサービスコールを
呼び出すことができないため，CPUロック状態の解除時には，タスクディスパッ
チ処理を行う必要がない．

(3-3) 割込み優先度マスクが全解除される

割込み優先度マスクの変更（chg_ipm）により割込み優先度マスクが全解除され
る場合に，タスクディスパッチ処理を行う必要がある．

また，タスクの終了，タスク例外処理ルーチンからのリターン，割込みハンド
ラからのリターン，割込みサービスルーチンからのリターン，タイムイベント
ハンドラからのリターン，CPU例外ハンドラからのリターンによって，割込み優
先度マスクが全解除される場合があり，その場合には，タスクディスパッチ処
理を行う必要がある．ただし，割込みサービスルーチンおよびタイムイベント
ハンドラからのリターンについては，リターン後も非タスクコンテキストであ
り，タスクディスパッチ保留状態が継続することから，タスクディスパッチ処
理を行う必要がない．

(3-4) ディスパッチ許可状態になる

ディスパッチの許可（ena_dsp）において，タスクディスパッチ処理を行う必要
がある．

また，タスクの終了とタスク例外処理ルーチンからのリターンによって，ディ
スパッチ許可状態になる場合があり，その場合には，タスクディスパッチ処理
を行う必要がある．

以上に加えて，カーネルの動作開始時にも，タスクディスパッチ処理を呼び出
す．

●タスクディスパッチャの構造

タスクディスパッチャの主な機能は，切換え前のタスクのコンテキスト（プロ
セッサの汎用レジスタ等）をメモリ上に保存し，切換え後のタスクのコンテキ
ストをメモリ上から復帰することである．ここで，保存／復帰しなければなら
ないレジスタは，タスクディスパッチャが実行される状況によって，次のよう
な違いがある．

・タスクが割込み（または，CPU例外）によりプリエンプトされる場合には，す
　べてのレジスタを保存しなければならない．また，その状態から実行再開す
　る場合には，すべてのレジスタを復帰しなければならない．

・タスクが自発的にタスクディスパッチャを呼び出す場合には，スクラッチレ
　ジスタ（caller saved register）以外のレジスタを保存すればよい．また，
　その状態から実行再開する場合には，スクラッチレジスタ以外のレジスタを
　復帰すればよい．

・タスクが終了する場合には，どのレジスタも保存する必要がない．

・タスクが実行開始する場合には，どのレジスタも復帰する必要がない．

そこで，それぞれの状況で必要最低限のレジスタのみを保存／復帰するために，
タスクディスパッチ処理を，(a) コンテキストの保存処理，(b) 実行するタス
クの選択処理，(c) コンテキストの復帰処理の3つのステップで構成し，(a)と
(c)のステップについては，タスクディスパッチャが実行される状況毎に用意す
る．具体的には，次の各処理を行うルーチンを用意する．

(a) コンテキストの保存処理
	(a-1) タスクが自発的に呼び出した場合の保存処理（dispatch）
	(a-2) 割込みハンドラの出口で呼び出された場合の保存処理（ret_int）
	(a-3) CPU例外ハンドラの出口で呼び出された場合の保存処理（ret_exc）
	(a-4) タスクの終了時の処理（exit_and_dispatch）
	(a-5) カーネルの動作開始時の処理（start_dispatch）
(b) 実行するタスクの選択（ディスパッチャ本体，dispatcher）
(c) コンテキストの復帰処理
	(c-1) タスクが自発的に呼び出した場合の復帰処理（dispatch_r）
	(c-2) 割込みハンドラの出口で呼び出された場合の復帰処理（ret_int_r）
	(c-3) CPU例外ハンドラの出口で呼び出された場合の復帰処理（ret_exc_r）
	(c-4) タスクの実行開始時の処理（start_r）


○タスク例外処理機能の実装

●タスク例外処理ルーチンの実行開始条件とシステム状態（仕様の確認）

タスク例外処理ルーチンは，次の6つの条件が揃った場合に実行が開始される．

　・タスク例外処理許可状態である
　・保留例外要因が0でない
　・タスクが実行状態である
　・タスクコンテキストが実行されている
　・割込み優先度マスク全解除状態である
　・CPUロック状態でない

また，タスク例外処理ルーチンの起動／リターン時のシステム状態に関する仕
様は次の通りである．

				CPUロック		割込み優先度	ディスパッチ
				フラグ			マスク			禁止フラグ
------------------------------------------------------------
【タスク例外処理ルーチン】
実行開始条件	解除			全解除			任意
実行開始時処理	そのまま		そのまま		そのまま
リターン前		原則解除(*1)	原則全解除(*1)	元に戻す
リターン時処理	解除する		全解除する		元に戻す(*4)
------------------------------------------------------------

●タスク例外処理ルーチンの呼出し処理

タスク例外処理ルーチンを呼び出す処理は，次の流れとなる．この関数は，
CPUロック状態で呼び出すことを想定している．

----------------------------------------
void
call_texrtn(void)
{
	TEXPTN	texptn;

	texptn = p_runtsk->texptn;
	p_runtsk->texptn = 0U;

	タスク例外処理禁止状態にする
	ディスパッチ禁止フラグを保存する

	CPUロック解除状態にする
	タスク例外処理ルーチンを呼び出す
	CPUロック状態にする

	割込み優先度マスク全解除状態にする
	ディスパッチ禁止フラグを元に戻す
	タスク例外処理禁止状態にする … (*2)

	必要な場合にはディスパッチを行う … (*1)

	タスク例外処理許可状態にする
}
----------------------------------------

(*1)において，必要な場合にタスク切換えを行うのは，割込み優先度マスクを
全解除にし，ディスパッチ禁止フラグを元に戻した結果，ディスパッチ保留状
態が解除され，ディスパッチが必要になる場合があるためである．

また，ディスパッチを行う前にタスク例外処理禁止状態にする(*2)理由につい
ては，「call_texrtnからdispatchを呼び出す処理について」の節を参照するこ
と．

●タスク例外処理ルーチンの実行開始が必要なタイミング

タスク例外処理ルーチンは，前述の6つの条件が揃った場合に実行開始すべきで
あるため，6つの条件のいずれかを新たに満たすようになる可能性のあるタイミ
ングで，タスク例外処理ルーチンの実行開始が必要になる．

以下では，6つの条件のいずれかを新たに満たすようになるタイミングについて
検討する．

(1) タスク例外処理許可状態である

タスク例外処理の許可（ena_tex）によって，タスク例外処理許可状態になる．
また，タスク例外処理ルーチンからのリターンによっても，タスク例外処理許
可状態になる．

(2) 保留例外要因が0でない

タスク例外処理の要求（ras_tex，iras_tex）によって，タスクの保留例外要因
が0でなくなる．ただし，非タスクコンテキストからのタスク例外処理の要求
（iras_tex）では，(4)の条件が満たされないため，タスク例外処理ルーチンの
実行開始は必要ない．

(3) タスクが実行状態である

タスクディスパッチャにより，切換え後のタスクが実行状態になる．

(4) タスクコンテキストが実行されている

割込みハンドラおよびCPU例外ハンドラからのリターンによって，タスクコンテ
キストに戻る場合がある．

(5) 割込み優先度マスク全解除状態である

割込み優先度マスクの変更（chg_ipm）によって，割込み優先度マスクが全解除
になる．

また，タスクの終了，タスク例外処理ルーチンからのリターン，割込みハンド
ラからのリターン，CPU例外ハンドラからのリターンによっても，割込み優先度
マスクが全解除される場合もある．

ただし，タスクの終了時については，タスクの終了後は別のタスクへ切り換わ
るため，切り換わった後のタスクに対するタスク例外処理ルーチンの実行開始
を，タスクディスパッチ後の処理で行えばよい．

(6) CPUロック状態でない

CPUロック状態の解除（unl_cpu，iunl_cpu）によって，CPUロック状態でなくな
る．ただし，非タスクコンテキストでのCPUロック状態の解除（iunl_cpu）では，
(4)の条件が満たされないため，タスク例外処理ルーチンの実行開始は必要ない．

また，タスクの終了，タスク例外処理ルーチンからのリターン，割込みハンド
ラからのリターン，割込みサービスルーチンからのリターン，タイムイベント
ハンドラからのリターン，CPU例外ハンドラからのリターンによって，CPUロッ
ク状態でなくなる場合もある．

ただし，割込みサービスルーチンおよびタイムイベントハンドラからのリター
ンについては，リターン後も非タスクコンテキストの実行が続き，(4)の条件が
満たされないため，タスク例外処理ルーチンの実行開始は必要ない．

タスクの終了時については，タスクの終了後は別のタスクへ切り換わるため，
切り換わった後のタスクに対するタスク例外処理ルーチンの実行開始を，タス
クディスパッチ後の処理で行えばよい．

以上より，重複するケースを考慮すると，タスク例外処理ルーチンの実行開始
が必要になる可能性があるのは，以下の処理である．

(a) タスク例外処理の許可（ena_tex）… (1)
(b) タスク例外処理ルーチンの出口処理 … (1)(5)(6)
(c) タスク例外処理の要求（ras_tex）… (2)
(d) タスクディスパッチ後の処理 … (3)(5)(6)
	(d-1) dispatch_r
	(d-2) ret_int_r
	(d-3) ret_exc_r
	(d-4) start_r
(e) 割込みハンドラの出口処理 … (4)(5)(6)
(f) CPU例外ハンドラの出口処理 … (4)(5)(6)
(g) 割込み優先度マスクの変更（chg_ipm）… (5)
(h) CPUロック状態の解除（unl_cpu）… (6)

この中で(d-4)に関しては，タスクの実行開始直後はタスク例外処理禁止状態で
あり（自タスクがena_texするまでは，タスク例外が許可されない），(1)の条
件が満たされないため，タスク例外処理ルーチンの実行開始は必要ない．

また(h)に関しては，次に述べる理由により，CPUロック状態が継続している間
に他の5つの条件が新たに満たされることはないため，タスク例外処理ルーチン
の実行開始は必要ない．

CPUロック状態では，まず，タスク例外処理の許可（ena_tex），タスク例外処
理の要求（ras_tex，iras_tex），割込み優先度マスクの変更（chg_ipm）を行
うことはできず，タスクディスパッチも起こらない．CPUロック状態でタスク例
外処理ルーチンからリターンすることはできるが，この場合はCPUロック状態も
解除され，CPUロック状態が継続しない．CPUロック状態で割込みハンドラから
リターンした場合も，これと同様である．

最後に，CPUロック状態でCPU例外ハンドラからリターンした場合が問題になる．
これについては，CPU例外が発生した時に，CPUロック状態であった場合と，
CPUロック解除状態であった場合に分けて考える．CPUロック解除状態であった
場合には，CPU例外ハンドラからのリターンにより，CPUロック状態が解除され
るため，割込みハンドラからリターンした場合と同様である．CPUロック状態で
あった場合には，起動されるCPU例外ハンドラはカーネル管理外のCPU例外ハン
ドラであるため，その中で(1)〜(3)と(5)の条件が新たに満たされることはない．
CPU例外ハンドラの実行前後で(4)の条件は保存されることから，CPU例外ハンド
ラからのリターンによって，(1)〜(5)の条件はCPU例外の発生前に戻り，新たに
満たされることはない．

●タスク例外処理ルーチンの実行開始処理

ここでは，前の節で検討したタスク例外処理ルーチンの実行開始が必要なタイ
ミングのそれぞれについて，実行開始処理の実装方法について述べる．

(a) タスク例外処理の許可（ena_tex）

ena_texがタスクから呼び出された場合には，自タスクに対して「タスクが実行
状態である」「タスクコンテキストが実行されている」の2条件は満たされてお
り，「タスク例外処理許可状態である」の条件はena_texの処理により満たされ
る．また，「CPUロック状態でない」の条件はena_texの入口でチェックしてい
る．そのため，「保留例外要因が0でない」と「割込み優先度マスク全解除状態
である」の2条件が満たされている場合には，タスク例外処理ルーチンを呼び出
す．

ena_texの本体の処理（エラー処理を除く）は次の通り．

----------------------------------------
		p_runtsk->enatex = true;
		if (p_runtsk->texptn != 0U && ipmflg) {
			call_texrtn();
		}
----------------------------------------

(b) タスク例外処理ルーチンの出口処理

タスク例外処理ルーチンの出口処理（call_texrtnの後半）では，自タスクに対
して「タスクが実行状態である」「タスクコンテキストが実行されている」の
2条件は満たされており，「タスク例外処理許可状態である」「割込み優先度マ
スク全解除状態である」「CPUロック状態でない」の3条件が出口処理で満たさ
れる．そのため，「保留例外要因が0でない」が満たされている場合には，タス
ク例外処理ルーチンを呼び出す必要がある．

ただし，タスク例外処理ルーチンの出口処理で単純にcall_texrtnを呼び出すと，
call_texrtnの中からcall_texrtnを呼び出すことになり，タスク例外処理が繰
り返し要求された場合に，スタックの使用量に上限がなくなる．そこで，タス
ク例外処理ルーチンの出口処理で「保留例外要因が0でない」場合には，
call_texrtnの中でループさせる．

修正したcall_texrtnの流れは次の通り．

----------------------------------------
void
call_texrtn(void)
{
	TEXPTN	texptn;

	ディスパッチ禁止フラグを保存する … (*3)
	タスク例外処理禁止状態にする … (*6)

	do {
		texptn = p_runtsk->texptn;
		p_runtsk->texptn = 0U;

		CPUロック解除状態にする
		タスク例外処理ルーチンを呼び出す
		CPUロック状態にする

		割込み優先度マスク全解除状態にする
		ディスパッチ禁止フラグを元に戻す … (*4)
		タスク例外処理禁止状態にする … (*2)

		必要な場合にはディスパッチを行う … (*1)
	}  while (p_runtsk->texptn != 0U);

	タスク例外処理許可状態にする … (*5)
}
----------------------------------------

タスク例外処理ルーチンの呼出し前にディスパッチ禁止フラグを保存する処理
は，(*4)において元の状態に戻すことから，ループの外(*3)で行うのが効率が
よい．

タスク例外処理ルーチンの呼出し前にタスク例外処理禁止状態にする処理は，
(*2)においてタスク例外処理禁止状態にすることから，ループ外(*6)で行うの
が効率がよい．また，タスク例外処理ルーチンの呼出し後にタスク例外処理許
可状態にする処理も，ループの外(*5)で行う方が効率がよい．

(c) タスク例外処理の要求（ras_tex）

ras_texがタスクから呼び出された場合で，対象タスクが自タスクの場合には，
自タスクに対して「タスクが実行状態である」「タスクコンテキストが実行さ
れている」の2条件は満たされており，「保留例外要因が0でない」の条件は
ras_texの処理により満たされる（ras_texのパラメータrasptnが0の場合はエラー
となるため）．また，「CPUロック状態でない」の条件はras_texの入口でチェッ
クしている．そのため，対象タスクが自タスクであり，「タスク例外処理許可
状態である」と「割込み優先度マスク全解除状態である」の2条件が満たされて
いる場合には，タスク例外処理ルーチンを呼び出す．

ras_texの本体の処理（エラー処理を除く）は次の通り．

----------------------------------------
		p_tcb->texptn |= rasptn;
		if (p_tcb == p_runtsk && p_runtsk->enatex && ipmflg) {
			call_texrtn();
		}
----------------------------------------

(d-1) dispatch_r

dispatch_rにおいては，dispatch_rからのリターン先のタスクに対して，「タ
スクが実行状態である」「タスクコンテキストが実行されている」の2条件は満
たされている．また，CPUロック状態でdispatch_rに来ることはないため，
「CPUロック状態でない」の条件も成立している．そのため，「タスク例外処理
許可状態である」「保留例外要因が0でない」「割込み優先度マスク全解除状態
である」の3条件が満たされている場合には，タスク例外処理ルーチンを呼び出
す必要がある．

これを実現するために，タスクコンテキストからのディスパッチ処理を次のよ
うに修正する．

----------------------------------------
void
dispatch(void)
{
	………

  dispatch_r:
	スクラッチレジスタを除くすべてのレジスタをスタックから復帰する
	calltex();
}
----------------------------------------

ここでcalltexは，「タスク例外処理許可状態である」「保留例外要因が0でな
い」「割込み優先度マスク全解除状態である」の3条件が満たされている場合に
call_texrtnを呼び出す関数である．

----------------------------------------
void
calltex(void)
{
	if (p_runtsk->enatex && p_runtsk->texptn != 0U && ipmflg) {
		call_texrtn();
	}
}
----------------------------------------

(d-2) ret_int_r

ret_int_rは，割込みハンドラの出口処理でディスパッチを行ったタスクが，実
行を再開する際の処理である．そのため，ret_int_rからのリターン先のタスク
に対して，「タスクが実行状態である」「タスクコンテキストが実行されてい
る」の2条件は満たされている．また，CPUロック状態でret_int_rに来ることは
ないため，「CPUロック状態でない」の条件も成立している．そのため，「タス
ク例外処理許可状態である」「保留例外要因が0でない」「（割込みハンドラか
らのリターン後に）割込み優先度マスク全解除状態である」の3条件が満たされ
ている場合には，タスク例外処理ルーチンを呼び出す必要がある．

これを実現するために，割込みハンドラの出入口処理を次のように修正する．

----------------------------------------
void
<割込みの出入口処理>(void)
{
		………

			  ret_int_r:
				スクラッチレジスタを除くすべてのレジスタをスタックから復帰する
				calltex();
			}
		………
}
----------------------------------------

(d-3) ret_exc_r

ret_exc_rは，CPU例外ハンドラの出口処理でディスパッチを行ったタスクが，
実行を再開する際の処理である．

カーネル管理のCPU例外ハンドラの出口処理（ret_exc）は，割込みハンドラの
出口処理（ret_int）と同様である．これは，CPUロック状態でCPU例外が発生し
た場合には，カーネル管理外のCPU例外ハンドラとなり，CPU例外ハンドラの出
口処理でディスパッチを行うことはなく，ret_excにも来ないためである．その
ため，ret_exc_rからのリターン先のタスクに対して，「CPUロック状態でない」
の条件が成立している．

このことから，CPU例外ハンドラの出入口処理についても，割込みハンドラの出
入口処理と同様に，次のように修正すればよい．

----------------------------------------
void
<CPU例外の出入口処理>(void)
{
			………

				  ret_exc_r:
					スクラッチレジスタを除くすべてのレジスタを
												スタックから復帰する
					calltex();
				}
			………
}
----------------------------------------

(e) 割込みハンドラの出口処理

割込みハンドラからタスクへリターンする場合には，リターン先のタスクに対
して，「タスクが実行状態である」「タスクコンテキストが実行されている」
の2条件は満たされている．また，CPUロック状態で割込みハンドラが実行され
ることはないため，「CPUロック状態でない」の条件も成立している．そのため，
「タスク例外処理許可状態である」「保留例外要因が0でない」「（割込みハン
ドラからのリターン後に）割込み優先度マスク全解除状態である」の3条件が満
たされている場合には，タスク例外処理ルーチンを呼び出す必要がある．

割込みハンドラの呼出し前と呼出し後でこれらの条件が変化するのは，実行状
態のタスクが変化した時と，割込みハンドラ中でiras_texが呼び出された場合
である．割込みハンドラからはena_texとchg_ipmは呼び出せないため，「タス
ク例外処理許可状態である」と「（割込みハンドラからのリターン後に）割込
み優先度マスク全解除状態である」の2条件が変化することはない．

この2つの状況の内，実行状態のタスクが変化するケースは，ret_int_rで考慮
済みである．割込みハンドラ中でiras_texが呼び出された場合には，reqflgが
trueになるため，タスク例外処理ルーチンの実行開始処理は，reqflgがtrueの
時にのみ行えばよい．

これを実現するために，上で修正した割込みハンドラの出入口処理を，さらに
次のように修正する．

----------------------------------------
void
<割込みの出入口処理>(void)
{
	………

	if (タスクコンテキストで割込み発生) {
		………
		if (reqflg) {
			………

			  ret_int_r:
				スクラッチレジスタを除くすべてのレジスタをスタックから復帰する
			}
			calltex();
		}
	}
	………
}
----------------------------------------

(f) CPU例外ハンドラの出口処理

カーネル管理のCPU例外ハンドラの出口処理は，割込みハンドラの出口処理と同
様である．

カーネル管理外のCPU例外ハンドラの中では，実行状態のタスクが変化すること
はなく，iras_texやena_texを呼び出すこともできないため，タスク例外処理ルー
チンの実行開始条件が新たに満たされることはなく，タスク例外処理ルーチン
の実行開始は必要ない．

このことから，CPU例外ハンドラの出入口処理についても，割込みハンドラの出
入口処理と同様に，次のように修正すればよい．

----------------------------------------
void
<CPU例外の出入口処理>(void)
{
		………
		if (タスクコンテキストでCPU例外発生) {
		………
			if (reqflg) {

				  ret_exc_r:
					スクラッチレジスタを除くすべてのレジスタを
												スタックから復帰する
				}
				calltex();
			}
		}
		………
}
----------------------------------------

(g) 割込み優先度マスクの変更

chg_ipmがタスクから呼び出された場合には，自タスクに対して「タスクが実行
状態である」「タスクコンテキストが実行されている」の2条件は満たされてい
る．また，「CPUロック状態でない」の条件はchg_ipmの入口でチェックしてい
る．そのため，パラメータintpriがTIPM_ENAALLであり，「タスク例外処理許可
状態である」「保留例外要因が0でない」の2条件が満たされている場合には，
タスク例外処理ルーチンを呼び出す．

chg_ipmの関連部分の処理は次の通り．

----------------------------------------
	t_set_ipm(intpri);
	if (intpri == TIPM_ENAALL) {
		ipmflg = true;
		ここにタスク切換え処理が入る
		if (p_runtsk->enatex && p_runtsk->texptn != 0U) {
			call_texrtn();
		}
	}
----------------------------------------

dispatchを呼び出してタスク切換えを行う場合には，dispatchの出口でタスク
例外処理ルーチンを呼び出すためここで呼出し処理を行う必要はないが，コー
ドが複雑になるため，dispatchを呼び出した場合もここでタスク例外処理ルー
チンの呼出し処理を行っている．

●call_texrtnからdispatchを呼び出す処理について

dispatch_rから（ターゲットによってはcalltexを経由して）call_texrtnを呼
び出し，call_texrtnからdispatchを呼び出すため，この2つの関数は相互再帰
呼出しをしている．ここでは，この実装で支障のない理由を説明する．

call_texrtnからdispatchを呼び出すのは，ディスパッチが保留されていない状
態で呼び出されたタスク例外処理ルーチンが，その実行中にディスパッチ保留
状態に遷移し，さらにタスクディスパッチを必要とする処理を行い，ディスパッ
チ保留状態を解除しないままリターンした場合である．この場合，call_texrtn
の中でディスパッチ保留状態を解除した後に，dispatchを呼び出してタスクディ
スパッチを行う．つまり，call_texrtnからdispatchを呼び出す処理は，タスク
例外処理ルーチンの中でディスパッチ保留状態を解除すべきであったのを，解
除せずにリターンした場合を救済するためのものである．以下，この振舞いを
「救済ケース」と呼ぶ．

そこで，比較のために，タスク例外処理ルーチンの最後で，正しくディスパッ
チ保留状態を解除してからリターンした場合の振舞いを考える．この場合には，
ディスパッチ保留状態を解除するサービスコール（ena_dsp，chg_ipm）の中で，
dispatchが呼び出されてタスクディスパッチが起こる．以下，この振舞いを
「正常ケース」と呼ぶ．

正常ケースと救済ケースを比較すると，call_texrtn→タスク例外処理ルーチン
→サービスコール→dispatchの順でdispatchが呼び出されるか，call_texrtnか
ら直接dispatchが呼び出されるかの違いということになり，救済ケースの方が
スタックの使用量は少ない．つまり，正常ケースの動きを想定してスタック領
域が用意してあれば，救済ケースでも問題なく動作することになる．

ここで，正常ケースと救済ケースで，上記以外に違いがないことが重要である．
具体的には，call_texrtnの中で，タスク例外処理許可状態にする（p_runtsk
->enatexをtrueにする）前に，dispatchを呼び出すことが重要である．タスク
例外処理許可状態にした後にdispatchを呼び出すと，出口のdispatch_rで，再
びタスク例外処理ルーチンを実行してしまう可能性があり，救済ケースの方が
スタックの使用量が増えてしまう．

タスク例外処理ルーチンの中で，タスク例外処理許可状態にしたままリターン
した場合には，call_texrtnから呼び出したdispatchの出口のdispatch_rで再び
タスク例外処理ルーチンを実行してしまう可能性がある．この場合も，タスク
例外処理ルーチンの最後で，ディスパッチ保留状態を解除してからリターンし
た場合よりはスタックの使用量が少ないが，タスク例外処理禁止状態にした後
にディスパッチ保留状態を解除してリターンした場合よりは，スタックの使用
量が増える場合がある．そこで，このような状況が起こらないように，
dispatchを呼ぶ前にp_runtsk->enatexをfalseにする．


○CHECKマクロとgoto文の使用

ASPカーネルの実装においては，サービスコールの静的なエラーをチェックする
ために，名称が"CHECK_"で始まる一連のマクロ（これらを，CHECKマクロと総称
する）を用いている．

CHECKマクロの定義中にはgoto文を含んでいるが，MISRA-Cなどのコーディング
ルールではgoto文の使用を禁止しており，goto文を使うべきではないという意
見も多い．また，マクロの定義中にgoto文を使用することが問題であるという
意見もある．

ここでは，定義中にgoto文を含むCHECKマクロを用いる設計意図とそれを使用し
てよい条件，CHECKマクロの使用によりソフトウェアの信頼性に問題が生じるこ
とがないことを論証する．

なお，ASPカーネルのカーネル本体の実装では，CHECKマクロ以外にgoto文を用
いている箇所はない（一部のシステムサービスでは，これ以外の方法でgoto文
を用いている）．

●CHECKマクロの定義とその使用法

kernel/check.hには，25個のCHECKマクロが定義されているが，いずれも次のパ
ターンで定義されている．ここでXXXXXには，チェックしたいエラーの種類を表
す文字列が入る．

----------------------------------------
#define CHECK_XXXXX(<……>) {				\
	if (<エラー条件>) {						\
		ercd = <エラーコード>;				\
		goto error_exit;					\
	}										\
}
----------------------------------------

これらのCHECKマクロは，多くのサービスコールの処理関数中で，次のように使
用されている．

----------------------------------------
ER
<サービスコール名>(……)
{
	<ローカル変数の宣言>
	ER		ercd;

	LOG_XXX_YYY_ENTER(……);
	CHECK_XXXXX(……);
	CHECK_YYYYY(……);

	<サービスコール処理本体>

  error_exit:
	LOG_XXX_YYY_LEAVE(……);
	return(ercd);
}
----------------------------------------

この例では，CHECKマクロを2つ使用しているが，1つのみ使用している場合もあ
れば，3つ以上使用している場合もある．また，複数のCHECKマクロの間に，ロー
カル変数への代入文が入る場合もある（例えば，ter_tskの処理関数）．

●設計意図

CHECKマクロを使用する意図は，ほとんどのサービスコールで必要な静的エラー
のチェックコードをパターン化し，ソースコードの簡潔さを保つことで読みや
すさを向上させるとともに，記述ミスの可能性を減らすことである．

ちなみに，CHECKマクロを使用しない場合，上に示したサービスコールの処理関
数は，次のように記述することになる．

----------------------------------------
ER
<サービスコール名>(……)
{
	<ローカル変数の宣言>
	ER		ercd;

	LOG_XXX_YYY_ENTER(……);
	if (<XXXXXのエラー条件>) {
		ercd = <XXXXXのエラーコード>;
	}
	else {
		if (<YYYYYのエラー条件>) {
			ercd = <YYYYYのエラーコード>;
		}
		else {

			<サービスコール処理本体>

		}
	}
	LOG_XXX_YYY_LEAVE(……);
	return(ercd);
}
----------------------------------------

CHECKマクロの内容を知っているという前提の下では，元のソースコードの方が
読みやすいのは明らかである．

なお，このようなCHECKマクロの定義には，goto文の使用が不可避である．

●CHECKマクロを使用してよい条件

CHECKマクロは，次の条件を満たすように使用しなければならない．

(1) CHECKマクロは，サービスコール処理関数の先頭部分で，サービスコール入
　　口のログを出した後，クリティカルセクションに入る前までに，処理関数
　　のトップレベルで用いる．

(2) サービスコール処理関数の末尾部分で，クリティカルセクションを抜けた
　　後，サービスコール出口のログを出す前に，処理関数のトップレベルに，
　　error_exitラベルを置く．

●問題を生じることがない根拠

このような方法でgoto文を使用しても，ソフトウェアの信頼性に問題が生じる
ことがないことを主張するには，そもそも，MISRA-Cなどのコーディングルール
でgoto文の使用を禁止している根拠，言い換えると，goto文の使用によりソフ
トウェアの信頼性に問題が生じる可能性のある理由を明らかにする必要がある．

MISRA-Cのドキュメントでは，goto文の使用を禁止する根拠についてあまり明確
になっておらず，ここではその根拠を次のように推測する．

・処理の流れが複雑になり，プログラムの意図が読みにくくなる（いわゆる，
　スパゲッティプログラムになる）．

CHECKマクロの場合には，goto文をエラー発生時の強制脱出に用いており，ラベ
ルもerror_exitとするなど，プログラムの意図は明らかである．一方，このよ
うにgoto文をエラー時の強制脱出に用いる場合には，次の点もgoto文の使用を
禁止する根拠になりうる．

・goto文で強制脱出することにより，脱出時に行わなければならない後処理が
　飛ばされるおそれがある．

ただしこの点についても，CHECKマクロを上記の使用条件を満たして使う限りは，
脱出時に行わなければならない後処理はなく，CHECKマクロの使用によりソフト
ウェアの信頼性に問題が生じることはない．

問題を生じることがないことを論証するもう1つ方法は，コーディングルールに
合致しないプログラムが，コーディングルールに合致したプログラムと等価で
あることを示す方法である．

すでに「設計意図」の節で述べたように，CHECKマクロを使用したプログラムは，
goto文を使用しない等価なプログラムに書き換えることができるが，より一般
的には，次のことが言える．

else部を持たないif文のthen部の最後に，if文より下（前方）にあり，if文と
同じブロック内の同じ階層のラベルへ分岐するgoto文がある場合には，if文と
ラベルの間の文をelse部にすることにより，goto文を使わない等価なプログラ
ムに書き換えることができる．

例として，goto文を使った次のプログラムを考える．

----------------------------------------
	{
		/* if文の前の文 */
		if (....) {
			/* then部の文 */
			goto <ラベル>;
		}
		/* if文とラベルの間の文 */
	  <ラベル>:
		/* ラベルより後ろの文 */
	}
----------------------------------------

このプログラムは，goto文を使わない次のプログラムと等価である．

----------------------------------------
	{
		/* if文の前の文 */
		if (....) {
			/* then部の文 */
		}
		else {
			/* if文とラベルの間の文 */
		}
		/* ラベルより後ろの文 */
	}
----------------------------------------

この条件に合致するgoto文が複数ある場合には，下にあるgoto文から順に上記
の方法によって書き換えることで，goto文を使わない等価なプログラムに書き
換えることができる．

CHECKマクロの使用方法は，上記のgoto文の使用方法に合致するため，goto文を
使用しない等価なプログラムに書き換えることができる．よって，このCHECKマ
クロにより問題を生じることはない．


○ext_tsk，ext_kerの返り値

μITRON4.0仕様では，ext_tskはリターンすることのないサービスコールとなっ
ているが，TOPPERS新世代カーネルにおいては，このサービスコールの返り値を
ER型に変更し，非タスクコンテキストから呼ばれた場合には，E_CTXエラーを返
り値としてリターンすることとする．

この仕様に対するいくつかの対案を検討したが，以下の理由で採用しない．

・JSPカーネルのように，危険の可能性を検出しながら実行を継続する方法は，
　信頼性・安全性を重視するシステムでは望ましくない．信頼性・安全性を重
　視するシステムでは，危険の可能性を検出したら，早期にリカバリをすべき
　である．

・ASPカーネルの当初案のカーネルをダウンさせる方法は，アプリケーション側
　で回復する余地をなくすという意味で望ましくない．

・過去の互換性を保つために，型をvoidとしたまま，非タスクコンテキストか
　ら呼ばれた場合にはリターンするとする方法は，カーネルの仕様変更に気づ
　きにくくなるという意味で望ましくない．また，他のエラーコード（例えば
　E_NOSPT）を返す余地をなくしている．

・一種のCPU例外を呼び出すことにする方法は，カーネル仕様全体の整合性を考
　えて採用しなかった．ここで一種のCPU例外を導入するよりも，サービスコー
　ルがエラーを返した場合に呼ばれるOSEK/VDX OS仕様のエラーフックに相当す
　る機能を導入した方が有用性が高いと思われるためである．

この変更により，さらに，CPUロック状態やディスパッチ禁止状態でext_tskが
呼ばれた場合にもエラーリターンする方法が考えられるが，タスクのメインルー
チンからのリターンとext_tskが等価にならないため，採用しない．また，他の
処理単位からのリターン方法と整合させる意味もある（例えば，割込みハンド
ラからCPUロック状態のままリターンした場合の扱い）．

これにあわせて，ext_kerについても，返り値をER型に変更する．ASPカーネル
では，ext_kerがエラーを返すことはないが，HRPカーネルでは，E_OACVエラー
を返す場合がある．

なお，μITRON4.0仕様では，exd_tskもリターンすることのないサービスコール
となっているが，TOPPERS新世代カーネルでは，exd_tskはサポートしていない．


○カーネルのデータ構造に対するvolatile宣言について（クリティカルセクショ
ンの出入処理の実現に関する制約）

カーネル内のデータ構造は，並行実行される他の処理単位（割込みハンドラや
タスク）からもアクセスされる可能性があるため，volatile宣言が必要ではな
いかと考えられる．実際，クリティカルセクション内でカーネル変数を読むコー
ドが，コンパイラの最適化によりクリティカルセクション外に移動され，それ
が原因となった問題事例も報告されている．

カーネル内のすべてのデータ構造にvolatile宣言をつける方法は，安全ではあ
るが，最適化が抑止されるために，カーネルのサイズや性能には悪影響を与え
る．そこでASPカーネルでは，次の方法でvolatile宣言の必要性をなくすことと
する．

ASPカーネルにおいては，並行実行される他の処理単位から書き換えられる可能
性のあるデータ構造は，すべて，CPUロック状態または全割込みロック状態によ
るクリティカルセクション内でアクセスしている．クリティカルセクション内
でのデータ構造のアクセスが，コンパイラの最適化によりクリティカルセクショ
ン外に移動されないようにするには，コンパイラに対して，クリティカルセク
ションの出入処理により，メモリ上のデータ構造が書き変わる可能性があるこ
とを知らせればよい．

具体的には，クリティカルセクションの出入処理を関数によって実現すれば，
このような最適化を抑止することができる．しかし，ASPカーネルの多くのター
ゲット依存部において，クリティカルセクションの出入処理はマクロやインラ
イン関数により実装されており，上のような最適化を抑止できない．

そこで，クリティカルセクションの出入処理を実現する場合には，メモリ上の
データ構造が書き変わる可能性があることを，何らかの方法でコンパイラに知
らせなければならないという制約を設ける．GNU開発環境では，次のいずれかの
方法でこの制約を満たすことができる．

(a) クリティカルセクションの出入処理の全体または出入処理の本質的な部分
	（具体的には，割込み禁止／許可する処理）を（インラインでない）通常
	の関数により実現する．

(b) クリティカルセクションの出入処理の本質的な部分をインラインアセンブ
	ラによって実現している場合には，そのインラインアセンブラのclobber変
	数リストに"memory"を追加する．

(c) クリティカルセクションの出入処理の本質的な部分が，マクロやインライ
	ン関数呼出しで実現している場合には，クリティカルセクションに入る処
	理の最後と出る処理の先頭に，Asm("":::"memory")という記述を入れる．

なお，この制約が適用されるクリティカルセクションの出入処理は，以下のも
のである．

	SIL_LOC_INT
	SIL_UNL_INT
	t_lock_cpu, i_lock_cpu, x_lock_cpu
	t_unlock_cpu, i_unlock_cpu, x_unlock_cpu


○型キャストに伴う警告メッセージ

GCCで-O2オプションをつけてコンパイルした場合に，カーネルのソースコード
中の数箇所で，次の警告メッセージが出る（GCCのバージョンにもよる）．

warning: dereferencing type-punned pointer will break strict-aliasing rules

これは，GCCに-O2オプションをつけると，コンパイラがC言語のstrict
aliasing ruleを前提とするためである．コンパイラにstrict aliasing ruleを
適用させないためには，GCCのオプションに-fno-strict-aliasingを指定すれば
よい．これにより，警告メッセージは抑止されるが，strict aliasing ruleを
前提とした最適化は行われなくなる．

ASPカーネルに実装においては，strict aliasing ruleを前提とした最適化を行っ
てもよく，この警告メッセージを無視しても差し支えない．以下では，この警
告メッセージを無視しても差し支えない理由を述べる．

警告メッセージが出る例として，semaphore.c中の次の行について検討する．

	wobj_make_wait((WOBJCB *) p_semcb, (WINFO_WOBJ *) &winfo_sem);

この警告メッセージの原因は，直接的には，&winfo_semを(WINFO_WOBJ *)にキャ
ストしていることであるが，本質的な原因は，このコードがC言語のstrict
aliasing ruleに従わない代入文の原因になる可能性があり，このルールに依存
した最適化が誤った結果を引き起こす可能性があることである．

C言語のstrict aliasing ruleは，互換性のない異なる型を通して，オーバラッ
プするメモリ領域をアクセスする代入文を使用してはならないというものであ
る（使用した場合の振舞いは未定義になる）．このケースでは，(WINFO_SEM
*)型のポインタ経由と(WINFO_WOBJ *)型のポインタ経由で，オーバラップする
メモリ領域をアクセスする代入文を使用してはならないことになる．また，警
告メッセージの原因になってはいないが，(SEMCB *)型のポインタ経由と
(WOBJCB *)型のポインタ経由で，オーバラップするメモリ領域をアクセスする
代入文を使用してはならない．

ASPカーネルの実装においては，semaphore.c中の関数においては，(SEMCB *)型
および(WINFO_SEM *)型のポインタを使用しており，(WOBJCB *)型および
(WINFO_WOBJ *)型のポインタ経由でメモリ領域をアクセスすることはない．一
方，そこから呼び出されるwait.c中の関数においては，(WOBJCB *)型および
(WINFO_WOBJ *)型のポインタを使用しており，(SEMCB *)型および(WINFO_SEM
*)型のポインタ経由でメモリ領域をアクセスすることはない．

strict aliasing ruleに従わない代入文の問題点は，このルールに依存した最
適化が誤った結果を引き起こす可能性があることであるが，異なるコンパイル
単位をまたいで最適化が行われることはないため，これにより問題が起こるこ
とはないと言うことができる．

同じ議論は，他のソースファイル（eventflag.c，dataqueue.c，pridataq.c，
mailbox.c，mempfix.c）中の警告メッセージについても，そのまま当てはまる．


○優先度データキュー機能（未完成）

●導入の経緯

●API仕様

CRE_PDQ(ID pdqid, { ATR pdqatr, uint_t pdqcnt, PRI maxdpri, void *pdqmb });
ER snd_pdq(ID pdqid, intptr_t data, PRI datapri);
ER psnd_pdq(ID pdqid, intptr_t data, PRI datapri);
ER ipsnd_pdq(ID pdqid, intptr_t data, PRI datapri);
ER tsnd_pdq(ID pdqid, intptr_t data, PRI datapri, TMO tmout);
ER rcv_pdq(ID pdqid, intptr_t *p_data, PRI *p_datapri);
ER prcv_pdq(ID pdqid, intptr_t *p_data, PRI *p_datapri);
ER trcv_pdq(ID pdqid, intptr_t *p_data, PRI *p_datapri, TMO tmout);
ER ini_pdq(ID pdqid);
ER ref_pdq(ID pdqid, T_RPDQ *pk_rpdq);

●実装

＜未完成＞


○性能評価用システム時刻参照機能

●必要性と使途

ASPカーネルには，ASPカーネル上で動作するタスクやASPカーネル自身の性能を
計測するために，システム時刻より精度の高い性能評価用システム時刻を読み
出す機能をサポートする．性能評価用システム時刻は，マイクロ秒単位で表現
されるが，実際の精度はターゲット依存である．

この機能を用いてあるプログラムの実行時間を計測するには，その実行直前と
実行直後に性能評価用システム時刻を読み出し，その差を求める．そのため，
性能評価用システム時刻は常に相対値を使用し，性能評価用システム時刻の絶
対値を使用することは想定していない．

●API仕様

性能評価用システム時刻参照機能では，次のデータ型を用いる．

	SYSUTM		性能評価用システム時刻（符号無し整数，単位はマイクロ秒，
				32ビット以上）

SYSUTM型は，ターゲット非依存部においてulong_t型（すなわち，unsigned
long型）に定義されており，そのサイズは，32ビット以上で，ターゲット定義
である．

性能評価用システム時刻がSYSUTM型で表現できる範囲を超えた（つまり，オー
バフローした）場合，性能評価用システム時刻は0に戻る．評価対象プログラム
の実行前後の性能評価用システム時刻の差を求める場合には，計測する時間が
SYSUTM型で表現できる範囲である限り，0に戻ることを特別に考慮する必要はな
い．

SYSUTM型が32ビットの場合，性能評価用システム時刻は約71分でオーバフロー
する．そのため，この機能を71分を越える時間の測定に使った場合の動作は保
証されない．

性能評価用システム時刻参照機能のためのサービスコールの仕様については，
「TOPPERS新世代カーネル統合仕様書」の「4.6.1 システム時刻管理」の節を参
照すること．

●実装

時刻をマイクロ秒単位で取得するために，周期的なタイムティックを供給する
タイマの現在値（タイマはカウントアップするものと仮定する）を読み出し，
それをマイクロ秒単位に換算した値に，現在のシステム時刻（ミリ秒単位で表
現される）を1000倍した値を加えたものを性能評価用システム時刻とする．現
在のシステム時刻を1000倍する際に，オーバフローが発生する可能性があるが，
無視してかまわない．

ただし，システム時刻の現在値とタイマの現在値を一貫した状態で読み出すの
は容易ではない．両方の値を順に読み出すと，読出しの間にタイマがオーバフ
ローして割込み要求が発生した場合に，片方はオーバフロー前の値，もう片方
はオーバフロー後の値を読んでしまい，誤った性能評価用システム時刻を取得
してしまう．

この問題を解決する方法はいくつか考えられるが，どの方法を採用するの決定
にあたり，次の要求事項を設定した．

(1) 多くのターゲットシステムで実現できること．

(2) サービスコールの実行時間が可能な限り一定となること．言い換えると，
    条件によってサービスコールの実行時間が大きく変動しないこと．

(3) サービスコール中の可能な限り同じタイミングの時刻を返すこと．言い換
    えると，条件によって時刻を読み取るタイミングが変動しないこと．

(4) 調整する必要のあるパラメータを最小限とすること．

これらの要求事項を満たす方法として，次の方法を用いることにした．

まず，NMIを除くすべての割込みを禁止した状態で，システム時刻の現在値，タ
イマの現在値（1回目），タイマ割込み要求の有無，タイマの現在値（2回目）
を，この順で読み出す．割込みを禁止しているため，この間にシステム時刻の
現在値が変化することはなく，システム時刻の現在値を読み出す順番はどこで
もよい．また，タイマの現在値の2回目の読出しは，タイマ割込み要求があった
場合にのみ必要となるが，(2)の要求から，タイマ割込み要求の有無によらず読
み出すこととする．

これらの値を読み出した後，割込み禁止を解除し，次の処理を行う．まず，タ
イマ割込み要求がなかった場合には，システム時刻の現在値と，1回目に読んだ
タイマの現在値は一貫した値であることが保証できるため，これらの値から性
能評価用システム時刻の現在値を求める．

次にタイマ割込み要求があった場合には，1回目に読んだタイマの現在値が，タ
イマ割込み要求発生前の値（オーバフロー前の値）である場合と，発生後の値
（オーバフロー後の値）である場合の両方の可能性が考えられる．このどちら
の場合であったかを，2回目に読んだタイマの現在値を使って，次のように決定
する．2回目の値は，タイマ割込み要求発生後の値（オーバフロー後の値）であ
ることが保証できるため，1回目の値が2回目の値よりも大きい場合には，その
間にオーバフローがあったものと推測できる．つまり，1回目の値はオーバフロー
前の値ということになり，システム時刻の現在値と一貫した値であるとして性
能評価用システム時刻の現在値を求める．逆に，1回目の値が2回目の値と同じ
かそれより小さい場合には，1回目の値はオーバフロー後の値であると推測でき
る．この場合には，次のタイムティックのシステム時刻を求め，その値と1回目
の値が一貫した値であるとして性能評価用システム時刻の現在値を求める．

ここで，タイマ割込み要求があった場合には，2回目に読んだタイマの現在値を
用いる方法が考えられるが，(3)の要求を満たさなくなるために採用しなかった．
また，JSPカーネルと同様の方法は，(4)の要求を満たさないために採用しなかっ
た．

上で「推測できる」としたのは，この推測が成り立たなくなるケースがあるた
めである．この推測が成り立たなくなるケースは，次の2つの場合に分けて分析
することができる．

(a) 1回目の値がオーバフロー後の値であるにもかかわらず，1回目の値が2回目
    の値よりも大きくなる場合

このようなケースは，タイマ割込みが要求されているにもかかわらずサービス
されない状態が長時間続くか，タイマの現在値を1回目に読んでから2回目に読
むまでの間に長い時間がかかった結果，その間に（再度）オーバフローが発生
した場合に起こる．つまり，タイマ割込みがサービスされない時間が，タイム
ティックの周期よりも長くなった場合である．このような場合には，システム
時刻の更新も正しく行われなくなる．

(b) 1回目の値がオーバフロー前の値であるにもかかわらず，1回目の値が2回目
    の値と同じかそれよりも小さくなる場合

このようなケースは，タイマの現在値を1回目に読んでから2回目に読むまでの
間に，タイマがほぼ1周分カウントアップした場合に起こる．この場合も，タイ
マ割込みが禁止されている時間が，タイムティックの周期よりも長かったこと
になり，システム時刻の更新が正しく行えなくなる．

いずれのケースも，タイマ割込みが長時間禁止されている，タイマ割込みより
も優先度の高い割込み処理が長時間続けて実行された，シミュレーション環境
においてシミュレータのプロセスが長時間スケジュールされなかったなどの理
由で，システム時刻の更新が正しく行えない状況に相当する．そこでこの状況
を，サービスコール使用上の注意事項に盛り込む．

実際のコードにおいては，システム時刻の現在値は変数に保持されていないた
め（上位桁はcurrent_timeに保持されているが，下位桁を保持する変数がな
い），次のタイムティックのシステム時刻を用いて計算している．そのため，
タイマの現在値がオーバフロー後の値であると判断した場合を除いては，タイ
ムティックの周期時間を，求めた性能評価用システム時刻から減算する．この
処理により，サービスコールの実行時間が変動することになるが，if文の内容
が（コンパイラの最適化を仮定すると）定数値の減算1回なので，変動はわずか
である．

このサービスコールは，任意の状態から呼び出すことができるため，SILの全割
込みロック機能を用いて，サービスコール内部のクリティカルセクションを実
現する．


○タスク例外処理禁止フラグをenatexで実装している理由

タスク例外処理禁止フラグは，TCB中のenatexフィールド（タスク例外処理許可
状態であることを示す）の形で保持している．このフィールドをdistexとせず
enatexとしたのは，JSPカーネルにおいてタスクディスパッチ禁止フラグを
enadspの形で保持したのと整合させたためである．

ASPカーネルでは，enadspはdisdspに変更になったことから，enatexもdistexに
変更した方が良かったと思われる．

以上
