source: rubycfg_asp/trunk/asp_dcre/doc/design.txt@ 315

Last change on this file since 315 was 315, checked in by coas-nagasima, 7 years ago

SVNプロパティを設定

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
  • Property svn:mime-type set to text/plane; charset=UTF-8
File size: 80.6 KB
Line 
1
2 TOPPERS/ASPカーネル
3 設計メモ
4
5 対応バージョン: Release 1.9.3
6 最終更新: 2014年4月15日(作成中)
7
8このドキュメントは,TOPPERS/ASPカーネルの設計メモである.作成途中のもの
9であり,網羅的ではない.
10
11----------------------------------------------------------------------
12 TOPPERS/ASP Kernel
13 Toyohashi Open Platform for Embedded Real-Time Systems/
14 Advanced Standard Profile Kernel
15
16 Copyright (C) 2005-2014 by Embedded and Real-Time Systems Laboratory
17 Graduate School of Information Science, Nagoya Univ., JAPAN
18
19 上記著作権者は,以下の(1)~(4)の条件を満たす場合に限り,本ソフトウェ
20 ア(本ソフトウェアを改変したものを含む.以下同じ)を使用・複製・改
21 変・再配布(以下,利用と呼ぶ)することを無償で許諾する.
22 (1) 本ソフトウェアをソースコードの形で利用する場合には,上記の著作
23 権表示,この利用条件および下記の無保証規定が,そのままの形でソー
24 スコード中に含まれていること.
25 (2) 本ソフトウェアを,ライブラリ形式など,他のソフトウェア開発に使
26 用できる形で再配布する場合には,再配布に伴うドキュメント(利用
27 者マニュアルなど)に,上記の著作権表示,この利用条件および下記
28 の無保証規定を掲載すること.
29 (3) 本ソフトウェアを,機器に組み込むなど,他のソフトウェア開発に使
30 用できない形で再配布する場合には,次のいずれかの条件を満たすこ
31 と.
32 (a) 再配布に伴うドキュメント(利用者マニュアルなど)に,上記の著
33 作権表示,この利用条件および下記の無保証規定を掲載すること.
34 (b) 再配布の形態を,別に定める方法によって,TOPPERSプロジェクトに
35 報告すること.
36 (4) 本ソフトウェアの利用により直接的または間接的に生じるいかなる損
37 害からも,上記著作権者およびTOPPERSプロジェクトを免責すること.
38 また,本ソフトウェアのユーザまたはエンドユーザからのいかなる理
39 由に基づく請求からも,上記著作権者およびTOPPERSプロジェクトを
40 免責すること.
41
42 本ソフトウェアは,無保証で提供されているものである.上記著作権者お
43 よびTOPPERSプロジェクトは,本ソフトウェアに関して,特定の使用目的
44 に対する適合性も含めて,いかなる保証も行わない.また,本ソフトウェ
45 アの利用により直接的または間接的に生じたいかなる損害に関しても,そ
46 の責任を負わない.
47
48 $Id: design.txt 315 2017-07-23 05:29:40Z coas-nagasima $
49----------------------------------------------------------------------
50
51○目次
52
53・TOPPERS/ASPカーネルの実装設計方針
54・システム状態とコンテキストの実装
55 - カーネル動作状態と非動作状態
56 - タスクコンテキストと非タスクコンテキスト
57 - 全割込みロック状態と全割込みロック解除状態
58 - CPUロック状態とCPUロック解除状態
59 - 割込み優先度マスク
60 - ディスパッチ禁止状態とディスパッチ許可状態
61 - ディスパッチ保留状態
62・タスク状態の管理とスケジューリング
63 - タスク状態の管理
64 - タスクスケジューラ
65・タスクディスパッチ処理の実装
66 - タスクディスパッチ処理の必要なタイミング
67 - タスクディスパッチャの構造
68 - タスクの終了時のタスクディスパッチ
69 - reqflgの導入理由
70・タスク例外処理機能の実装
71 - タスク例外処理ルーチンの実行開始条件とシステム状態(仕様の確認)
72 - タスク例外処理ルーチンの呼出し処理
73 - タスク例外処理ルーチンの実行開始が必要なタイミング
74 - タスク例外処理ルーチンの実行開始処理
75 - call_texrtnからdispatchを呼び出す処理について
76・エラーのチェック順序
77 - エラーの3分類
78 - 静的エラーのチェック順序
79 - 準静的エラーのチェック順序
80・CHECKマクロとgoto文の使用
81 - CHECKマクロの定義とその使用法
82 - 設計意図
83 - CHECKマクロを使用してよい条件
84 - 問題を生じることがない根拠
85・ext_tsk,ext_kerの返り値
86・カーネルのデータ構造に対するvolatile宣言について
87・型キャストに伴う警告メッセージ
88・性能評価用システム時刻参照機能
89 - 必要性と使途
90 - API仕様
91 - 実装
92・タスク例外処理禁止フラグをenatexで実装している理由
93
94
95○TOPPERS/ASPカーネルの実装設計方針
96
97TOPPERS/ASPカーネル(以下,ASPカーネル)は,TOPPERS新世代カーネルの出発
98点となるリアルタイムカーネルである.TOPPERS新世代カーネル仕様の設計方針
99と,ASPカーネルの適用対象領域と設計方針については,TOPPERS新世代カーネ
100ル統合仕様書に述べられている.
101
102以下では,ASPカーネルの実装設計方針について述べるが,仕様設計方針とも関
103連しており,明確に分離できない部分もある.
104
105TOPPERS/ASPカーネルの実装設計を行うにあたり,次の方針を設定する.
106
107(1) ソースコードの読みやすさ・改造しやすさを重視する
108
109ソースコードが読みやすいことは,オープンソースソフトウェアの品質を向上
110させる上で最も重要な特性である.ソースコードを理解している技術者が増え
111ることで,問題を早期に発見することができ,サポート体制も充実させること
112ができる.また,ソースコードが読みやすいことは,シンプルな設計がされて
113いることも意味しており,信頼性向上にもつながる.さらに,技術者教育のた
114めの教材とする観点からも,ソースコードが読みやすいことは重要となる.
115
116改造しやすいことは,システム毎の要求にあわせたチューニングが行いやすい
117ことを意味しており,擦り合わせ型の開発を支援する性質である.また,ASPカー
118ネルを基盤としてTOPPERS新世代カーネルシリーズを開発していく上でも,改造
119しやすいことは必須の条件である.
120
121(2) 新しいターゲットシステムへのポーティングが容易な構造とする
122
123組込みシステムには多様なハードウェアが用いられるため,それらに容易にポー
124ティングできることは重要な性質である.そのために,実行性能に配慮しつつ
125ハードウェアを抽象化し,ターゲットシステムに依存する部分(ターゲット依
126存部)と依存しない部分(ターゲット非依存部)を明確に分離する.また,開
127発環境(コンパイラなど)に依存する部分も明確に分離する.
128
129(3) 検証が容易な構造とする
130
131信頼性を確保するために,検証が容易な構造とする.
132
133具体的には,サービスコールのほとんど全体を割込み禁止で実行することとし,
134サービスコールの処理途中で割込みを許可しない.この構造は,読みやすく改
135造しやすいソースコードにするためにも有効である.これにより割込み応答性
136が犠牲になるが,それによって問題を生じるアプリケーションは少数であり,
137やむをえないものと考える.
138
139また,条件コンパイル等によりコンフィギュレーションできる箇所を増やすと,
140細かな最適化ができる一方で,検証すべき組合せが増えることから,コンフィ
141ギュレーションできる箇所は必要最低限とする.
142
143(4) 実行性能とメモリ使用量に配慮する
144
145上記の方針を満たした上で,高い実行性能と小さいメモリ使用量を達成できる
146ような実装を行う.実行性能を向上させる際には,平均性能の向上よりも,最
147悪時性能の向上を重視する.
148
149ソースコードの読みやすさを重視すると言っても,実行性能の悪いアルゴリズ
150ムを安易に採用することはせず,高い実行性能を達成できるアルゴリズムを用
151いる.ただし,新しいターゲットシステムへのポーティングを容易にするため
152に大部分をC言語で実装しており,すべてをアセンブリ言語で記述した場合に比
153べて実行性能が落ちるのはやむをえない.
154
155メモリ使用量については,RAMの使用量を削減することに重点を置いた設計を行
156うが,上記の方針および実行性能とのトレードオフを考慮し,ぎりぎりまでの
157削減は行わない.
158
159(5) スケーラビリティに配慮する
160
161様々な規模のシステムに適用できるスケーラビリティをもった構造とする.特
162に,小規模なシステムに適用する際に,使用しない機能をカーネルが持ってい
163ることによるメモリ使用量の増加が最小限になるように配慮する.
164
165具体的には,アプリケーションとカーネルを1つのロードモジュールにリンクす
166る方法(1リンクモデル)を想定し,カーネルを関数単位でライブラリ化して,
167使用する関数のみをリンクできる構造とする.これは一種のコンフィギュレー
168ションであるが,この方法は,条件コンパイルによるコンフィギュレーション
169とは違い,検証工数に与える影響が小さい.
170
171また,固定的に使用するRAM領域を減らし,スタックに置ける情報はできる限り
172スタック上に置く.
173
174
175○システム状態とコンテキストの実装
176
177この章では,「TOPPERS新世代カーネル統合仕様書」の「2.5 システム状態とコ
178ンテキスト」の節に規定されているシステム状態とコンテキストの実装方法に
179ついて記述する.
180
181●カーネル動作状態と非動作状態
182
183カーネルの動作状態を管理するために,カーネル動作状態フラグ(kerflg)を
184用意する.kerflgは,スタートアップモジュールでfalse(=0)に初期化する.
185また,カーネル動作の開始時にtrueにし,カーネル動作の終了時にfalseにする.
186
187----------------------------------------
188bool_t kerflg = false;
189----------------------------------------
190
191kerflgは,sns_kerで参照する.カーネル非動作状態でsns_ker以外のサービス
192コールを呼び出した場合の動作は保証する必要がないため,他のサービスコー
193ルではkerflgを参照しない.
194
195●タスクコンテキストと非タスクコンテキスト
196
197タスクコンテキストと非タスクコンテキストの切換えは,ターゲットハードウェ
198アおよびターゲット依存部に委ねる.また,どちらのコンテキストで実行中で
199あるかを判別する関数(sense_context)も,ターゲット依存部で用意すること
200とする.
201
202●全割込みロック状態と全割込みロック解除状態
203
204全割込みロックフラグの管理はターゲット依存部に委ね,全割込みロック状態
205に遷移させるマクロ(SIL_LOC_INT)と,元の状態に戻すマクロ(SIL_UNL_INT)
206は,SILのターゲット依存部で用意することとする.全割込みロック状態である
207か否かを判別する機能は,必要がないために用意していない.
208
209全割込みロック状態でsns_kerとext_ker以外のサービスコールを呼び出した場
210合の動作は保証する必要がないため,サービスコール中で全割込みロック状態
211であることを判別する必要はない.
212
213●CPUロック状態とCPUロック解除状態
214
215CPUロックフラグの管理はターゲット依存部に委ね,CPUロック状態に遷移させ
216る関数(t_lock_cpu/i_lock_cpu/x_lock_cpu)と,CPUロック解除状態に遷移
217させる関数(t_unlock_cpu/i_unlock_cpu/x_unlock_cpu)は,ターゲット依
218存部で用意することとする.また,CPUロック状態であるか否かを判別する関数
219(t_sense_lock/i_sense_lock/x_sense_lock)も,ターゲット依存部で用意
220することとする.
221
222●割込み優先度マスク
223
224割込み優先度マスクの管理はターゲット依存部に委ね,割込み優先度マスクを
225設定する関数(t_set_ipm/i_set_ipm/x_set_ipm)と,それを参照する関数
226(t_get_ipm/i_get_ipm/x_get_ipm)は,ターゲット依存部で用意することと
227する.
228
229ただし,タスクコンテキストの実行中に,割込み優先度マスク全解除状態であ
230ることを効率的に判断するために,割込み優先度マスク全解除状態であること
231を示すフラグ(ipmflg)を用意する.
232
233ipmflgは,カーネルの初期化時にfalseに初期化する.非タスクコンテキストで
234は,割込み優先度マスク全解除状態になることはないため,このフラグを用い
235る必要はなく,フラグの更新も行わない.
236
237----------------------------------------
238bool_t ipmflg;
239----------------------------------------
240
241●ディスパッチ禁止状態とディスパッチ許可状態
242
243ディスパッチ禁止フラグを管理するために,フラグ(disdsp)を用意する.
244disdspは,カーネルの初期化時にfalseに初期化する.
245
246----------------------------------------
247bool_t disdsp;
248----------------------------------------
249
250※ disdspではなく,それを論理反転したenadspを用意した方が,ipmflgや
251dspflgとの整合性の観点からは良かった.
252
253●ディスパッチ保留状態
254
255ディスパッチ保留状態は,非タスクコンテキストの実行中,CPUロック状態,割
256込み優先度マスクが全解除でない状態,ディスパッチ禁止状態のいずれか(ま
257たは,それらが重なった状態)である.
258
259タスクコンテキストの実行中に,ディスパッチ保留状態でないこと(つまり,
260ディスパッチできる状態であること)を効率的に判別するために,割込み優先
261度マスク全解除状態であり,ディスパッチ許可状態である(ディスパッチ禁止
262状態でない)ことを示すフラグ(dspflg)を用意する.すなわち,常に
263「dspflg == (ipmflg && !disdsp)」に設定する.
264
265dspflgは,カーネルの初期化時にtrueに初期化する.また,タスクコンテキス
266トにおいて割込み優先度マスクの値が変更されるか,割込み禁止フラグが変更
267される度に更新する.非タスクコンテキストでは,常にディスパッチ保留状態
268であるため,このフラグを用いる必要はなく,フラグの更新も行わない.
269
270----------------------------------------
271bool_t dspflg;
272----------------------------------------
273
274
275○タスク状態の管理とスケジューリング
276
277●タスク状態の管理
278
279タスク管理ブロック(TCB)中のタスク状態を管理するフィールド(tstat)で
280は,タスク状態が次のいずれであるかを管理する.
281
282 ・実行できる状態
283 ・休止状態
284 ・(狭義の)待ち状態
285 ・強制待ち状態
286 ・二重待ち状態
287
288タスクが実行できる状態の時に,実行状態であるか実行可能状態であるかは,
289このフィールドでは管理せず,実行状態のタスクのTCBを指すポインタ変数
290(p_runtsk)によって判別する.実行状態のタスクがない場合は,p_runtskは
291NULLにする.
292
293----------------------------------------
294TCB *p_runtsk;
295----------------------------------------
296
297p_runtskは,カーネルの初期化時にNULLに初期化し,ディスパッチャにおいて
298更新する.サービスコールの処理の中で自タスクに関する情報を参照する場合
299は,p_runtskを用いる.
300
301●タスクスケジューラ
302
303タスクスケジューラは,実行できる状態のタスクの中から,最も優先順位が高
304いタスク(これを,最高優先順位のタスクと呼ぶ)を決定し,そのタスクの
305TCBを指すポインタ変数(p_schedtsk)を設定する.
306
307----------------------------------------
308TCB *p_schedtsk;
309----------------------------------------
310
311タスクスケジューラに対しては,どのタスクが実行できる状態であるかを知ら
312せる必要がある.そのため,タスクスケジューラは,次の2つの関数を用意する.
313
314 ・タスクが実行できる状態に遷移したことを知らせる関数(make_runnable)
315 ・タスクが実行できる状態から他の状態へ遷移したことを知らせる関数
316 (make_non_runnable)
317
318また,処理の効率化のために,上の2つの関数を用いずにレディキューを直接操
319作してタスクスケジュールを行う関数として,次の2つの関数を用意する.
320
321 ・タスクの優先度の変更(change_priority)
322 ・レディキューの回転(rotate_ready_queue)
323
324
325○タスクディスパッチ処理の実装
326
327●タスクディスパッチ処理の必要なタイミング
328
329タスクディスパッチは,実行状態のタスク(p_runtsk)と最高優先順位のタス
330ク(p_schedtsk)が一致しておらず,ディスパッチ保留状態でない場合に行う.
331このことから,タスクディスパッチ処理を行う必要があるのは,次の3つの場合
332である.
333
334(1) 実行状態のタスクが実行できる状態でなくなる
335
336自タスクを広義の待ち状態に遷移させるサービスコールや,自タスクを終了さ
337せるサービスコールにおいて,タスクディスパッチ処理を行う必要がある.
338
339(2) 最高優先順位のタスクが変化する
340
341タスクの起動,タスクの待ち解除,タスクの強制待ちからの再開,タスクの優
342先度の変更,タスクの優先順位の回転を行うサービスコールにおいて,最高優
343先順位のタスクが変化し,ディスパッチ保留状態でない場合には,タスクディ
344スパッチ処理を行う必要がある.
345
346(3) ディスパッチ保留状態が解除される
347
348ディスパッチ保留状態とは,非タスクコンテキストの実行中,CPUロック状態,
349割込み優先度マスクが全解除でない状態,ディスパッチ禁止状態の総称である
350ため,これらの状態のいずれかが遷移するタイミングで,タスクディスパッチ
351処理を行う必要がある.具体的には,次のタイミングが該当する.
352
353(3-1) 非タスクコンテキストからタスクコンテキストに遷移する
354
355割込みハンドラまたはCPU例外ハンドラからタスクにリターンする際に,タスク
356ディスパッチ処理を行う必要がある.
357
358(3-2) CPUロック状態が解除される
359
360CPUロック状態においては,上記の(1)や(2)の状況を作り出すサービスコールを
361呼び出すことができない.そのため,CPUロック状態の解除時には,タスクディ
362スパッチ処理を行う必要がない.
363
364(3-3) 割込み優先度マスクが全解除される
365
366割込み優先度マスクの変更(chg_ipm)により割込み優先度マスクが全解除され
367る場合に,タスクディスパッチ処理を行う必要がある.
368
369また,タスクの終了,タスク例外処理ルーチンからのリターン,割込みハンド
370ラからのリターン,割込みサービスルーチンからのリターン,タイムイベント
371ハンドラからのリターン,CPU例外ハンドラからのリターンによって,割込み優
372先度マスクが全解除される場合があり,その場合には,タスクディスパッチ処
373理を行う必要がある.ただし,割込みサービスルーチンおよびタイムイベント
374ハンドラからのリターンについては,リターン後も非タスクコンテキストであ
375り,タスクディスパッチ保留状態が継続することから,タスクディスパッチ処
376理を行う必要がない.
377
378(3-4) ディスパッチ許可状態になる
379
380ディスパッチの許可(ena_dsp)において,タスクディスパッチ処理を行う必要
381がある.
382
383また,タスクの終了とタスク例外処理ルーチンからのリターンによって,ディ
384スパッチ許可状態になる場合があり,その場合には,タスクディスパッチ処理
385を行う必要がある.
386
387以上に加えて,カーネルの動作開始時にも,タスクディスパッチ処理を呼び出
388す.
389
390●タスクディスパッチャの構造
391
392タスクディスパッチャの主な機能は,切換え前のタスクのコンテキスト(プロ
393セッサの汎用レジスタ等)をメモリ上に保存し,切換え後のタスクのコンテキ
394ストをメモリ上から復帰することである.ここで,保存/復帰しなければなら
395ないレジスタは,タスクディスパッチャが実行される状況によって,次のよう
396な違いがある.
397
398・タスクが割込み(または,CPU例外)によりプリエンプトされる場合には,す
399 べてのレジスタを保存しなければならない.また,その状態から実行再開す
400 る場合には,すべてのレジスタを復帰しなければならない.
401
402・タスクが自発的にタスクディスパッチャを呼び出す場合には,スクラッチレ
403 ジスタ(caller saved register)以外のレジスタを保存すればよい.また,
404 その状態から実行再開する場合には,スクラッチレジスタ以外のレジスタを
405 復帰すればよい.
406
407・タスクが終了する場合には,どのレジスタも保存する必要がない.
408
409・タスクが実行開始する場合には,どのレジスタも復帰する必要がない.
410
411そこで,それぞれの状況で必要最低限のレジスタのみを保存/復帰するために,
412タスクディスパッチ処理を,(a) コンテキストの保存処理,(b) 実行するタス
413クの選択処理,(c) コンテキストの復帰処理の3つのステップで構成し,(a)と
414(c)のステップについては,タスクディスパッチャが実行される状況毎に用意す
415る.具体的には,次の各処理を行うルーチンを用意する.
416
417(a) コンテキストの保存処理
418 (a-1) タスクが自発的に呼び出した場合の保存処理(dispatch)
419 (a-2) 割込みハンドラの出口で呼び出された場合の保存処理(ret_int)
420 (a-3) CPU例外ハンドラの出口で呼び出された場合の保存処理(ret_exc)
421 (a-4) タスクの終了時の処理(exit_and_dispatch)
422 (a-5) カーネルの動作開始時の処理(start_dispatch)
423(b) 実行するタスクの選択(ディスパッチャ本体,dispatcher)
424(c) コンテキストの復帰処理
425 (c-1) タスクが自発的に呼び出した場合の復帰処理(dispatch_r)
426 (c-2) 割込みハンドラの出口で呼び出された場合の復帰処理(ret_int_r)
427 (c-3) CPU例外ハンドラの出口で呼び出された場合の復帰処理(ret_exc_r)
428 (c-4) タスクの実行開始時の処理(start_r)
429
430●タスクの終了時のタスクディスパッチ
431
432ext_tskによるタスクの終了時に,起動要求がキューイングされていると,同じ
433タスクがすぐに起動される.この場合,タスクディスパッチャにとっては,同
434じタスクへの切換えに見え,タスクディスパッチ処理をスキップ可能に思える
435が,実際には,同じタスクの異なるインスタンスへの切換えであるため,タス
436クディスパッチ処理をスキップしてはならない.
437
438上述のタスクディスパッチャの構造により,タスクの終了時には,それ専用の
439処理(exit_and_dispatch)を呼び出す.この処理では,実行状態のタスクを参
440照せず,コンテキストの保存も行わずに,次に事項するタスクの選択処理を行
441うため,タスクディスパッチ処理がスキップされることはない.
442
443なお,ter_tskによって実行状態のタスクを終了させることはできないため,
444ter_tskではこのような状況は起こらない.
445
446●reqflgの導入理由
447
448割込みハンドラ/CPU例外ハンドラの出口処理に,タスクディスパッチまたはタ
449スク例外処理ルーチンの実行開始を要求することを示すフラグとして,reqflg
450を用意している.非タスクコンテキストにおいて,タスクディスパッチが必要
451になった場合や,タスク例外処理ルーチンの実行開始が必要になった場合には,
452このフラグをセットする.
453
454reqflgを導入した理由は,割込みハンドラ/CPU例外ハンドラの出口処理の典型
455的なケース(タスクディスパッチもタスク例外処理ルーチンの実行開始も必要
456ない場合)を高速化するためである.また,ディスパッチャ本体(dispatcher)
457のアイドル処理も高速化できる.
458
459しかし,割込みハンドラ/CPU例外ハンドラの出口処理全体のオーバヘッドを考
460えると,reqflgによる高速化の効果はそれほど大きくないものと思われる.
461
462
463○タスク例外処理機能の実装
464
465●タスク例外処理ルーチンの実行開始条件とシステム状態(仕様の確認)
466
467タスク例外処理ルーチンは,次の6つの条件が揃った場合に実行が開始される.
468
469 ・タスク例外処理許可状態である
470 ・保留例外要因が0でない
471 ・タスクが実行状態である
472 ・タスクコンテキストが実行されている
473 ・割込み優先度マスク全解除状態である
474 ・CPUロック状態でない
475
476また,タスク例外処理ルーチンの実行開始/リターン時のシステム状態に関す
477る仕様は次の通りである.
478
479 CPUロック 割込み優先度 ディスパッチ
480 フラグ マスク 禁止フラグ
481------------------------------------------------------------
482【タスク例外処理ルーチン】
483実行開始条件 解除 全解除 任意
484実行開始時処理 そのまま そのまま そのまま
485リターン前 原則解除(*1) 原則全解除(*1) 元に戻す
486リターン時処理 解除する 全解除する 元に戻す(*4)
487------------------------------------------------------------
488
489●タスク例外処理ルーチンの呼出し処理
490
491タスク例外処理ルーチンを呼び出す処理は,次の流れとなる.この関数は,
492CPUロック状態で呼び出すことを想定している.
493
494----------------------------------------
495void
496call_texrtn(void)
497{
498 TEXPTN texptn;
499
500 texptn = p_runtsk->texptn;
501 p_runtsk->texptn = 0U;
502
503 タスク例外処理禁止状態にする
504 ディスパッチ禁止フラグを保存する
505
506 CPUロック解除状態にする
507 タスク例外処理ルーチンを呼び出す
508 CPUロック状態にする
509
510 割込み優先度マスク全解除状態にする
511 ディスパッチ禁止フラグを元に戻す
512 タスク例外処理禁止状態にする … (*2)
513
514 必要な場合にはディスパッチを行う … (*1)
515
516 タスク例外処理許可状態にする
517}
518----------------------------------------
519
520(*1)において,必要な場合にタスク切換えを行うのは,割込み優先度マスクを
521全解除にし,ディスパッチ禁止フラグを元に戻した結果,ディスパッチ保留状
522態が解除され,ディスパッチが必要になる場合があるためである.
523
524また,ディスパッチを行う前にタスク例外処理禁止状態にする(*2)理由につい
525ては,「call_texrtnからdispatchを呼び出す処理について」の節を参照するこ
526と.
527
528●タスク例外処理ルーチンの実行開始が必要なタイミング
529
530タスク例外処理ルーチンは,前述の6つの条件が揃った場合に実行開始すべきで
531あるため,6つの条件のいずれかを新たに満たすようになる可能性のあるタイミ
532ングで,タスク例外処理ルーチンの実行開始が必要になる.
533
534以下では,6つの条件のいずれかを新たに満たすようになるタイミングについて
535検討する.
536
537(1) タスク例外処理許可状態である
538
539タスク例外処理の許可(ena_tex)によって,タスク例外処理許可状態になる.
540また,タスク例外処理ルーチンからのリターンによっても,タスク例外処理許
541可状態になる.
542
543(2) 保留例外要因が0でない
544
545タスク例外処理の要求(ras_tex,iras_tex)によって,タスクの保留例外要因
546が0でなくなる.ただし,非タスクコンテキストからのタスク例外処理の要求
547(iras_tex)では,(4)の条件が満たされないため,タスク例外処理ルーチンの
548実行開始は必要ない.
549
550(3) タスクが実行状態である
551
552タスクディスパッチャにより,切換え後のタスクが実行状態になる.
553
554(4) タスクコンテキストが実行されている
555
556割込みハンドラおよびCPU例外ハンドラからのリターンによって,タスクコンテ
557キストに戻る場合がある.
558
559(5) 割込み優先度マスク全解除状態である
560
561割込み優先度マスクの変更(chg_ipm)によって,割込み優先度マスクが全解除
562になる.
563
564また,タスクの終了,タスク例外処理ルーチンからのリターン,割込みハンド
565ラからのリターン,CPU例外ハンドラからのリターンによっても,割込み優先度
566マスクが全解除される場合もある.
567
568ただし,タスクの終了時については,タスクの終了後は別のタスクへ切り換わ
569るため,切り換わった後のタスクに対するタスク例外処理ルーチンの実行開始
570を,タスクディスパッチ後の処理で行えばよい.
571
572(6) CPUロック状態でない
573
574CPUロック状態の解除(unl_cpu,iunl_cpu)によって,CPUロック状態でなくな
575る.ただし,非タスクコンテキストでのCPUロック状態の解除(iunl_cpu)では,
576(4)の条件が満たされないため,タスク例外処理ルーチンの実行開始は必要ない.
577
578また,タスクの終了,タスク例外処理ルーチンからのリターン,割込みハンド
579ラからのリターン,割込みサービスルーチンからのリターン,タイムイベント
580ハンドラからのリターン,CPU例外ハンドラからのリターンによって,CPUロッ
581ク状態でなくなる場合もある.
582
583ただし,割込みサービスルーチンおよびタイムイベントハンドラからのリター
584ンについては,リターン後も非タスクコンテキストの実行が続き,(4)の条件が
585満たされないため,タスク例外処理ルーチンの実行開始は必要ない.
586
587タスクの終了時については,タスクの終了後は別のタスクへ切り換わるため,
588切り換わった後のタスクに対するタスク例外処理ルーチンの実行開始を,タス
589クディスパッチ後の処理で行えばよい.
590
591以上より,重複するケースを考慮すると,タスク例外処理ルーチンの実行開始
592が必要になる可能性があるのは,以下の処理である.
593
594(a) タスク例外処理の許可(ena_tex)… (1)
595(b) タスク例外処理ルーチンの出口処理 … (1)(5)(6)
596(c) タスク例外処理の要求(ras_tex)… (2)
597(d) タスクディスパッチ後の処理 … (3)(5)(6)
598 (d-1) dispatch_r
599 (d-2) ret_int_r
600 (d-3) ret_exc_r
601 (d-4) start_r
602(e) 割込みハンドラの出口処理 … (4)(5)(6)
603(f) CPU例外ハンドラの出口処理 … (4)(5)(6)
604(g) 割込み優先度マスクの変更(chg_ipm)… (5)
605(h) CPUロック状態の解除(unl_cpu)… (6)
606
607この中で(d-4)に関しては,タスクの実行開始直後はタスク例外処理禁止状態で
608あり(自タスクがena_texするまでは,タスク例外が許可されない),(1)の条
609件が満たされないため,タスク例外処理ルーチンの実行開始は必要ない.
610
611また(h)に関しては,次に述べる理由により,CPUロック状態が継続している間
612に他の5つの条件が新たに満たされることはないため,タスク例外処理ルーチン
613の実行開始は必要ない.
614
615CPUロック状態では,まず,タスク例外処理の許可(ena_tex),タスク例外処
616理の要求(ras_tex,iras_tex),割込み優先度マスクの変更(chg_ipm)を行
617うことはできず,タスクディスパッチも起こらない.CPUロック状態でタスク例
618外処理ルーチンからリターンすることはできるが,この場合はCPUロック状態も
619解除され,CPUロック状態が継続しない.CPUロック状態で割込みハンドラから
620リターンした場合も,これと同様である.
621
622最後に,CPUロック状態でCPU例外ハンドラからリターンした場合が問題になる.
623これについては,CPU例外が発生した時に,CPUロック状態であった場合と,
624CPUロック解除状態であった場合に分けて考える.CPUロック解除状態であった
625場合には,CPU例外ハンドラからのリターンにより,CPUロック状態が解除され
626るため,割込みハンドラからリターンした場合と同様である.CPUロック状態で
627あった場合には,起動されるCPU例外ハンドラはカーネル管理外のCPU例外ハン
628ドラであるため,その中で(1)~(3)と(5)の条件が新たに満たされることはない.
629CPU例外ハンドラの実行前後で(4)の条件は保存されることから,CPU例外ハンド
630ラからのリターンによって,(1)~(5)の条件はCPU例外の発生前に戻り,新たに
631満たされることはない.
632
633●タスク例外処理ルーチンの実行開始処理
634
635ここでは,前の節で検討したタスク例外処理ルーチンの実行開始が必要なタイ
636ミングのそれぞれについて,実行開始処理の実装方法について述べる.
637
638(a) タスク例外処理の許可(ena_tex)
639
640ena_texがタスクから呼び出された場合には,自タスクに対して「タスクが実行
641状態である」「タスクコンテキストが実行されている」の2条件は満たされてお
642り,「タスク例外処理許可状態である」の条件はena_texの処理により満たされ
643る.また,「CPUロック状態でない」の条件はena_texの入口でチェックしてい
644る.そのため,「保留例外要因が0でない」と「割込み優先度マスク全解除状態
645である」の2条件が満たされている場合には,タスク例外処理ルーチンを呼び出
646す.
647
648ena_texの本体の処理(エラー処理を除く)は次の通り.
649
650----------------------------------------
651 p_runtsk->enatex = true;
652 if (p_runtsk->texptn != 0U && ipmflg) {
653 call_texrtn();
654 }
655----------------------------------------
656
657(b) タスク例外処理ルーチンの出口処理
658
659タスク例外処理ルーチンの出口処理(call_texrtnの後半)では,自タスクに対
660して「タスクが実行状態である」「タスクコンテキストが実行されている」の
6612条件は満たされており,「タスク例外処理許可状態である」「割込み優先度マ
662スク全解除状態である」「CPUロック状態でない」の3条件が出口処理で満たさ
663れる.そのため,「保留例外要因が0でない」が満たされている場合には,タス
664ク例外処理ルーチンを呼び出す必要がある.
665
666ただし,タスク例外処理ルーチンの出口処理で単純にcall_texrtnを呼び出すと,
667call_texrtnの中からcall_texrtnを呼び出すことになり,タスク例外処理が繰
668り返し要求された場合に,スタックの使用量に上限がなくなる.そこで,タス
669ク例外処理ルーチンの出口処理で「保留例外要因が0でない」場合には,
670call_texrtnの中でループさせる.
671
672修正したcall_texrtnの流れは次の通り.
673
674----------------------------------------
675void
676call_texrtn(void)
677{
678 TEXPTN texptn;
679
680 ディスパッチ禁止フラグを保存する … (*3)
681 タスク例外処理禁止状態にする … (*6)
682
683 do {
684 texptn = p_runtsk->texptn;
685 p_runtsk->texptn = 0U;
686
687 CPUロック解除状態にする
688 タスク例外処理ルーチンを呼び出す
689 CPUロック状態にする
690
691 割込み優先度マスク全解除状態にする
692 ディスパッチ禁止フラグを元に戻す … (*4)
693 タスク例外処理禁止状態にする … (*2)
694
695 必要な場合にはディスパッチを行う … (*1)
696 } while (p_runtsk->texptn != 0U);
697
698 タスク例外処理許可状態にする … (*5)
699}
700----------------------------------------
701
702タスク例外処理ルーチンの呼出し前にディスパッチ禁止フラグを保存する処理
703は,(*4)において元の状態に戻すことから,ループの外(*3)で行うのが効率が
704よい.
705
706タスク例外処理ルーチンの呼出し前にタスク例外処理禁止状態にする処理は,
707(*2)においてタスク例外処理禁止状態にすることから,ループ外(*6)で行うの
708が効率がよい.また,タスク例外処理ルーチンの呼出し後にタスク例外処理許
709可状態にする処理も,ループの外(*5)で行う方が効率がよい.
710
711(c) タスク例外処理の要求(ras_tex)
712
713ras_texがタスクから呼び出された場合で,対象タスクが自タスクの場合には,
714自タスクに対して「タスクが実行状態である」「タスクコンテキストが実行さ
715れている」の2条件は満たされており,「保留例外要因が0でない」の条件は
716ras_texの処理により満たされる(ras_texのパラメータrasptnが0の場合はエラー
717となるため).また,「CPUロック状態でない」の条件はras_texの入口でチェッ
718クしている.そのため,対象タスクが自タスクであり,「タスク例外処理許可
719状態である」と「割込み優先度マスク全解除状態である」の2条件が満たされて
720いる場合には,タスク例外処理ルーチンを呼び出す.
721
722ras_texの本体の処理(エラー処理を除く)は次の通り.
723
724----------------------------------------
725 p_tcb->texptn |= rasptn;
726 if (p_tcb == p_runtsk && p_runtsk->enatex && ipmflg) {
727 call_texrtn();
728 }
729----------------------------------------
730
731(d-1) dispatch_r
732
733dispatch_rにおいては,dispatch_rからのリターン先のタスクに対して,「タ
734スクが実行状態である」「タスクコンテキストが実行されている」の2条件は満
735たされている.また,CPUロック状態でdispatch_rに来ることはないため,
736「CPUロック状態でない」の条件も成立している.そのため,「タスク例外処理
737許可状態である」「保留例外要因が0でない」「割込み優先度マスク全解除状態
738である」の3条件が満たされている場合には,タスク例外処理ルーチンを呼び出
739す必要がある.
740
741これを実現するために,タスクコンテキストからのディスパッチ処理を次のよ
742うに修正する.
743
744----------------------------------------
745void
746dispatch(void)
747{
748 ………
749
750 dispatch_r:
751 スクラッチレジスタを除くすべてのレジスタをスタックから復帰する
752 calltex();
753}
754----------------------------------------
755
756ここでcalltexは,「タスク例外処理許可状態である」「保留例外要因が0でな
757い」「割込み優先度マスク全解除状態である」の3条件が満たされている場合に
758call_texrtnを呼び出す関数である.
759
760----------------------------------------
761void
762calltex(void)
763{
764 if (p_runtsk->enatex && p_runtsk->texptn != 0U && ipmflg) {
765 call_texrtn();
766 }
767}
768----------------------------------------
769
770(d-2) ret_int_r
771
772ret_int_rは,割込みハンドラの出口処理でディスパッチを行ったタスクが,実
773行を再開する際の処理である.そのため,ret_int_rからのリターン先のタスク
774に対して,「タスクが実行状態である」「タスクコンテキストが実行されてい
775る」の2条件は満たされている.また,CPUロック状態でret_int_rに来ることは
776ないため,「CPUロック状態でない」の条件も成立している.そのため,「タス
777ク例外処理許可状態である」「保留例外要因が0でない」「(割込みハンドラか
778らのリターン後に)割込み優先度マスク全解除状態である」の3条件が満たされ
779ている場合には,タスク例外処理ルーチンを呼び出す必要がある.
780
781これを実現するために,割込みハンドラの出入口処理を次のように修正する.
782
783----------------------------------------
784void
785<割込みの出入口処理>(void)
786{
787 ………
788
789 ret_int_r:
790 スクラッチレジスタを除くすべてのレジスタをスタックから復帰する
791 calltex();
792 }
793 ………
794}
795----------------------------------------
796
797(d-3) ret_exc_r
798
799ret_exc_rは,CPU例外ハンドラの出口処理でディスパッチを行ったタスクが,
800実行を再開する際の処理である.
801
802カーネル管理のCPU例外ハンドラの出口処理(ret_exc)は,割込みハンドラの
803出口処理(ret_int)と同様である.これは,CPUロック状態でCPU例外が発生し
804た場合には,カーネル管理外のCPU例外ハンドラとなり,CPU例外ハンドラの出
805口処理でディスパッチを行うことはなく,ret_excにも来ないためである.その
806ため,ret_exc_rからのリターン先のタスクに対して,「CPUロック状態でない」
807の条件が成立している.
808
809このことから,CPU例外ハンドラの出入口処理についても,割込みハンドラの出
810入口処理と同様に,次のように修正すればよい.
811
812----------------------------------------
813void
814<CPU例外の出入口処理>(void)
815{
816 ………
817
818 ret_exc_r:
819 スクラッチレジスタを除くすべてのレジスタを
820 スタックから復帰する
821 calltex();
822 }
823 ………
824}
825----------------------------------------
826
827(e) 割込みハンドラの出口処理
828
829割込みハンドラからタスクへリターンする場合には,リターン先のタスクに対
830して,「タスクが実行状態である」「タスクコンテキストが実行されている」
831の2条件は満たされている.また,CPUロック状態で割込みハンドラが実行され
832ることはないため,「CPUロック状態でない」の条件も成立している.そのため,
833「タスク例外処理許可状態である」「保留例外要因が0でない」「(割込みハン
834ドラからのリターン後に)割込み優先度マスク全解除状態である」の3条件が満
835たされている場合には,タスク例外処理ルーチンを呼び出す必要がある.
836
837割込みハンドラの呼出し前と呼出し後でこれらの条件が変化するのは,実行状
838態のタスクが変化した時と,割込みハンドラ中でiras_texが呼び出された場合
839である.割込みハンドラからはena_texとchg_ipmは呼び出せないため,「タス
840ク例外処理許可状態である」と「(割込みハンドラからのリターン後に)割込
841み優先度マスク全解除状態である」の2条件が変化することはない.
842
843この2つの状況の内,実行状態のタスクが変化するケースは,ret_int_rで考慮
844済みである.割込みハンドラ中でiras_texが呼び出された場合には,reqflgが
845trueになるため,タスク例外処理ルーチンの実行開始処理は,reqflgがtrueの
846時にのみ行えばよい.
847
848これを実現するために,上で修正した割込みハンドラの出入口処理を,さらに
849次のように修正する.
850
851----------------------------------------
852void
853<割込みの出入口処理>(void)
854{
855 ………
856
857 if (タスクコンテキストで割込み発生) {
858 ………
859 if (reqflg) {
860 ………
861
862 ret_int_r:
863 スクラッチレジスタを除くすべてのレジスタをスタックから復帰する
864 }
865 calltex();
866 }
867 }
868 ………
869}
870----------------------------------------
871
872(f) CPU例外ハンドラの出口処理
873
874カーネル管理のCPU例外ハンドラの出口処理は,割込みハンドラの出口処理と同
875様である.
876
877カーネル管理外のCPU例外ハンドラの中では,実行状態のタスクが変化すること
878はなく,iras_texやena_texを呼び出すこともできないため,タスク例外処理ルー
879チンの実行開始条件が新たに満たされることはなく,タスク例外処理ルーチン
880の実行開始は必要ない.
881
882このことから,CPU例外ハンドラの出入口処理についても,割込みハンドラの出
883入口処理と同様に,次のように修正すればよい.
884
885----------------------------------------
886void
887<CPU例外の出入口処理>(void)
888{
889 ………
890 if (タスクコンテキストでCPU例外発生) {
891 ………
892 if (reqflg) {
893
894 ret_exc_r:
895 スクラッチレジスタを除くすべてのレジスタを
896 スタックから復帰する
897 }
898 calltex();
899 }
900 }
901 ………
902}
903----------------------------------------
904
905(g) 割込み優先度マスクの変更
906
907chg_ipmがタスクから呼び出された場合には,自タスクに対して「タスクが実行
908状態である」「タスクコンテキストが実行されている」の2条件は満たされてい
909る.また,「CPUロック状態でない」の条件はchg_ipmの入口でチェックしてい
910る.そのため,パラメータintpriがTIPM_ENAALLであり,「タスク例外処理許可
911状態である」「保留例外要因が0でない」の2条件が満たされている場合には,
912タスク例外処理ルーチンを呼び出す.
913
914chg_ipmの関連部分の処理は次の通り.
915
916----------------------------------------
917 t_set_ipm(intpri);
918 if (intpri == TIPM_ENAALL) {
919 ipmflg = true;
920 ここにタスク切換え処理が入る
921 if (p_runtsk->enatex && p_runtsk->texptn != 0U) {
922 call_texrtn();
923 }
924 }
925----------------------------------------
926
927dispatchを呼び出してタスク切換えを行う場合には,dispatchの出口でタスク
928例外処理ルーチンを呼び出すためここで呼出し処理を行う必要はないが,コー
929ドが複雑になるため,dispatchを呼び出した場合もここでタスク例外処理ルー
930チンの呼出し処理を行っている.
931
932●call_texrtnからdispatchを呼び出す処理について
933
934dispatch_rから(ターゲットによってはcalltexを経由して)call_texrtnを呼
935び出し,call_texrtnからdispatchを呼び出すため,この2つの関数は相互再帰
936呼出しをしている.ここでは,この実装で支障のない理由を説明する.
937
938call_texrtnからdispatchを呼び出すのは,ディスパッチが保留されていない状
939態で呼び出されたタスク例外処理ルーチンが,その実行中にディスパッチ保留
940状態に遷移し,さらにタスクディスパッチを必要とする処理を行い,ディスパッ
941チ保留状態を解除しないままリターンした場合である.この場合,call_texrtn
942の中でディスパッチ保留状態を解除した後に,dispatchを呼び出してタスクディ
943スパッチを行う.つまり,call_texrtnからdispatchを呼び出す処理は,タスク
944例外処理ルーチンの中でディスパッチ保留状態を解除すべきであったのを,解
945除せずにリターンした場合を救済するためのものである.以下,この振舞いを
946「救済ケース」と呼ぶ.
947
948そこで,比較のために,タスク例外処理ルーチンの最後で,正しくディスパッ
949チ保留状態を解除してからリターンした場合の振舞いを考える.この場合には,
950ディスパッチ保留状態を解除するサービスコール(ena_dsp,chg_ipm)の中で,
951dispatchが呼び出されてタスクディスパッチが起こる.以下,この振舞いを
952「正常ケース」と呼ぶ.
953
954正常ケースと救済ケースを比較すると,call_texrtn→タスク例外処理ルーチン
955→サービスコール→dispatchの順でdispatchが呼び出されるか,call_texrtnか
956ら直接dispatchが呼び出されるかの違いということになり,救済ケースの方が
957スタックの使用量は少ない.つまり,正常ケースの動きを想定してスタック領
958域が用意してあれば,救済ケースでも問題なく動作することになる.
959
960ここで,正常ケースと救済ケースで,上記以外に違いがないことが重要である.
961具体的には,call_texrtnの中で,タスク例外処理許可状態にする(p_runtsk
962->enatexをtrueにする)前に,dispatchを呼び出すことが重要である.タスク
963例外処理許可状態にした後にdispatchを呼び出すと,出口のdispatch_rで,再
964びタスク例外処理ルーチンを実行してしまう可能性があり,救済ケースの方が
965スタックの使用量が増えてしまう.
966
967タスク例外処理ルーチンの中で,タスク例外処理許可状態にしたままリターン
968した場合には,call_texrtnから呼び出したdispatchの出口のdispatch_rで再び
969タスク例外処理ルーチンを実行してしまう可能性がある.この場合も,タスク
970例外処理ルーチンの最後で,ディスパッチ保留状態を解除してからリターンし
971た場合よりはスタックの使用量が少ないが,タスク例外処理禁止状態にした後
972にディスパッチ保留状態を解除してリターンした場合よりは,スタックの使用
973量が増える場合がある.そこで,このような状況が起こらないように,
974dispatchを呼ぶ前にp_runtsk->enatexをfalseにする.
975
976
977○エラーのチェック順序
978
979サービスコール内におけるエラーチェックは,以下の順序で行うことを原則と
980する.なお,この節には,保護機能対応カーネルに関する記述が含まれている
981が,それらの記述は将来的にはより適切なドキュメントに移動する予定である.
982
983●エラーの3分類
984
985サービスコールのエラーは,大きく以下の3つに分類することができる.
986
987(a) 静的エラー
988
989対象のカーネルオブジェクトが登録されているか否かや,その状態に依存せず
990に,チェックすることができるエラー.
991
992(b) 準静的エラー
993
994対象のカーネルオブジェクトが登録されていれば,その状態に依存せずにチェッ
995クすることができるエラー.
996
997(c) 動的エラー
998
999対象のカーネルオブジェクトの状態に依存するエラー.
1000
1001ASPカーネルにおいては,(a)と(b)のエラーはクリティカルセクションの外側で,
1002この順序でチェックし,(c)のエラーはクリティカルセクションの内側でチェッ
1003クする.ただし,動的生成機能拡張パッケージでは,(b)のエラー(の一部)は
1004クリティカルセクションの内側で実施する必要がある.
1005
1006●静的エラーのチェック順序
1007
1008静的エラーには,実行コンテキストのエラー(ディスパッチ保留状態からの呼
1009出しエラーも含む),パラメータの範囲等のエラー(対象のカーネルオブジェ
1010クトに依存せずにチェックできるもの)が含まれる.
1011
1012サービスコール中では,最初に実行コンテキストのエラーをチェックし,その
1013後,パラメータの並び順に,範囲等のエラーをチェックする.
1014
1015保護機能対応カーネルでは,パラメータがポインタである場合に,ポインタの
1016指すメモリ領域がアクセス可能であるかをチェックする必要があるが,メモリ
1017領域の設定が静的である場合には,このエラーチェックもここで実施する.た
1018だし,メモリ領域の設定が静的でない場合には,このエラーチェックはクリティ
1019カルセクションの内側で行う必要がある.
1020
1021●准静的エラーのチェック順序
1022
1023准静的エラーのチェックの前に,対象のカーネルオブジェクトの管理ブロック
1024の先頭番地を,ローカル変数に代入する.
1025
1026保護機能対応カーネルでは,この次に,対象のカーネルオブジェクトがアクセ
1027ス可能であるかをチェックする処理を行う.当該サービスコールの呼出しが,
1028システム状態に対するアクセス許可ベクタで保護されている場合にも,この段
1029階でチェックを実施する.
1030
1031その後で,パラメータの範囲等のエラーの中で,対象のカーネルオブジェクト
1032の登録情報(初期化ブロックに含まれている情報)に依存してチェックすべき
1033もののチェックを,パラメータの並び順で実施する.
1034
1035
1036○CHECKマクロとgoto文の使用
1037
1038ASPカーネルの実装においては,サービスコールの静的なエラーをチェックする
1039ために,名称が"CHECK_"で始まる一連のマクロ(これらを,CHECKマクロと総称
1040する)を用いている.
1041
1042CHECKマクロの定義中にはgoto文を含んでいるが,MISRA-Cなどのコーディング
1043ルールではgoto文の使用を禁止しており,goto文を使うべきではないという意
1044見も多い.また,マクロの定義中にgoto文を使用することが問題であるという
1045意見もある.
1046
1047ここでは,定義中にgoto文を含むCHECKマクロを用いる設計意図とそれを使用し
1048てよい条件,CHECKマクロの使用によりソフトウェアの信頼性に問題が生じるこ
1049とがないことを論証する.
1050
1051なお,ASPカーネルのカーネル本体の実装では,CHECKマクロ以外にgoto文を用
1052いている箇所はない(一部のシステムサービスでは,これ以外の方法でgoto文
1053を用いている).
1054
1055●CHECKマクロの定義とその使用法
1056
1057kernel/check.hには,25個のCHECKマクロが定義されているが,いずれも次のパ
1058ターンで定義されている.ここでXXXXXには,チェックしたいエラーの種類を表
1059す文字列が入る.
1060
1061----------------------------------------
1062#define CHECK_XXXXX(<……>) { \
1063 if (<エラー条件>) { \
1064 ercd = <エラーコード>; \
1065 goto error_exit; \
1066 } \
1067}
1068----------------------------------------
1069
1070これらのCHECKマクロは,多くのサービスコールの処理関数中で,次のように使
1071用されている.
1072
1073----------------------------------------
1074ER
1075<サービスコール名>(……)
1076{
1077 <ローカル変数の宣言>
1078 ER ercd;
1079
1080 LOG_XXX_YYY_ENTER(……);
1081 CHECK_XXXXX(……);
1082 CHECK_YYYYY(……);
1083
1084 <サービスコール処理本体>
1085
1086 error_exit:
1087 LOG_XXX_YYY_LEAVE(……);
1088 return(ercd);
1089}
1090----------------------------------------
1091
1092この例では,CHECKマクロを2つ使用しているが,1つのみ使用している場合もあ
1093れば,3つ以上使用している場合もある.また,複数のCHECKマクロの間に,ロー
1094カル変数への代入文が入る場合もある(例えば,ter_tskの処理関数).
1095
1096●設計意図
1097
1098CHECKマクロを使用する意図は,ほとんどのサービスコールで必要な静的エラー
1099のチェックコードをパターン化し,ソースコードの簡潔さを保つことで読みや
1100すさを向上させるとともに,記述ミスの可能性を減らすことである.
1101
1102ちなみに,CHECKマクロを使用しない場合,上に示したサービスコールの処理関
1103数は,次のように記述することになる.
1104
1105----------------------------------------
1106ER
1107<サービスコール名>(……)
1108{
1109 <ローカル変数の宣言>
1110 ER ercd;
1111
1112 LOG_XXX_YYY_ENTER(……);
1113 if (<XXXXXのエラー条件>) {
1114 ercd = <XXXXXのエラーコード>;
1115 }
1116 else {
1117 if (<YYYYYのエラー条件>) {
1118 ercd = <YYYYYのエラーコード>;
1119 }
1120 else {
1121
1122 <サービスコール処理本体>
1123
1124 }
1125 }
1126 LOG_XXX_YYY_LEAVE(……);
1127 return(ercd);
1128}
1129----------------------------------------
1130
1131CHECKマクロの内容を知っているという前提の下では,元のソースコードの方が
1132読みやすいのは明らかである.
1133
1134なお,このようなCHECKマクロの定義には,goto文の使用が不可避である.
1135
1136●CHECKマクロを使用してよい条件
1137
1138CHECKマクロは,次の条件を満たすように使用しなければならない.
1139
1140(1) CHECKマクロは,サービスコール処理関数の先頭部分で,サービスコール入
1141  口のログを出した後,クリティカルセクションに入る前までに,処理関数
1142  のトップレベルで用いる.
1143
1144(2) サービスコール処理関数の末尾部分で,クリティカルセクションを抜けた
1145  後,サービスコール出口のログを出す前に,処理関数のトップレベルに,
1146  error_exitラベルを置く.
1147
1148●問題を生じることがない根拠
1149
1150このような方法でgoto文を使用しても,ソフトウェアの信頼性に問題が生じる
1151ことがないことを主張するには,そもそも,MISRA-Cなどのコーディングルール
1152でgoto文の使用を禁止している根拠,言い換えると,goto文の使用によりソフ
1153トウェアの信頼性に問題が生じる可能性のある理由を明らかにする必要がある.
1154
1155MISRA-Cのドキュメントでは,goto文の使用を禁止する根拠についてあまり明確
1156になっておらず,ここではその根拠を次のように推測する.
1157
1158・処理の流れが複雑になり,プログラムの意図が読みにくくなる(いわゆる,
1159 スパゲッティプログラムになる).
1160
1161CHECKマクロの場合には,goto文をエラー発生時の強制脱出に用いており,ラベ
1162ルもerror_exitとするなど,プログラムの意図は明らかである.一方,このよ
1163うにgoto文をエラー時の強制脱出に用いる場合には,次の点もgoto文の使用を
1164禁止する根拠になりうる.
1165
1166・goto文で強制脱出することにより,脱出時に行わなければならない後処理が
1167 飛ばされるおそれがある.
1168
1169ただしこの点についても,CHECKマクロを上記の使用条件を満たして使う限りは,
1170脱出時に行わなければならない後処理はなく,CHECKマクロの使用によりソフト
1171ウェアの信頼性に問題が生じることはない.
1172
1173問題を生じることがないことを論証するもう1つ方法は,コーディングルールに
1174合致しないプログラムが,コーディングルールに合致したプログラムと等価で
1175あることを示す方法である.
1176
1177すでに「設計意図」の節で述べたように,CHECKマクロを使用したプログラムは,
1178goto文を使用しない等価なプログラムに書き換えることができるが,より一般
1179的には,次のことが言える.
1180
1181else部を持たないif文のthen部の最後に,if文より下(前方)にあり,if文と
1182同じブロック内の同じ階層のラベルへ分岐するgoto文がある場合には,if文と
1183ラベルの間の文をelse部にすることにより,goto文を使わない等価なプログラ
1184ムに書き換えることができる.
1185
1186例として,goto文を使った次のプログラムを考える.
1187
1188----------------------------------------
1189 {
1190 /* if文の前の文 */
1191 if (....) {
1192 /* then部の文 */
1193 goto <ラベル>;
1194 }
1195 /* if文とラベルの間の文 */
1196 <ラベル>:
1197 /* ラベルより後ろの文 */
1198 }
1199----------------------------------------
1200
1201このプログラムは,goto文を使わない次のプログラムと等価である.
1202
1203----------------------------------------
1204 {
1205 /* if文の前の文 */
1206 if (....) {
1207 /* then部の文 */
1208 }
1209 else {
1210 /* if文とラベルの間の文 */
1211 }
1212 /* ラベルより後ろの文 */
1213 }
1214----------------------------------------
1215
1216この条件に合致するgoto文が複数ある場合には,下にあるgoto文から順に上記
1217の方法によって書き換えることで,goto文を使わない等価なプログラムに書き
1218換えることができる.
1219
1220CHECKマクロの使用方法は,上記のgoto文の使用方法に合致するため,goto文を
1221使用しない等価なプログラムに書き換えることができる.よって,このCHECKマ
1222クロにより問題を生じることはない.
1223
1224
1225○ext_tsk,ext_kerの返り値
1226
1227μITRON4.0仕様では,ext_tskはリターンすることのないサービスコールとなっ
1228ているが,TOPPERS新世代カーネルにおいては,このサービスコールの返り値を
1229ER型に変更し,非タスクコンテキストから呼ばれた場合には,E_CTXエラーを返
1230り値としてリターンすることとする.
1231
1232この仕様に対するいくつかの対案を検討したが,以下の理由で採用しない.
1233
1234・JSPカーネルのように,危険の可能性を検出しながら実行を継続する方法は,
1235 信頼性・安全性を重視するシステムでは望ましくない.信頼性・安全性を重
1236 視するシステムでは,危険の可能性を検出したら,早期にリカバリをすべき
1237 である.
1238
1239・ASPカーネルの当初案のカーネルをダウンさせる方法は,アプリケーション側
1240 で回復する余地をなくすという意味で望ましくない.
1241
1242・過去の互換性を保つために,型をvoidとしたまま,非タスクコンテキストか
1243 ら呼ばれた場合にはリターンするとする方法は,カーネルの仕様変更に気づ
1244 きにくくなるという意味で望ましくない.また,他のエラーコード(例えば
1245 E_NOSPT)を返す余地をなくしている.
1246
1247・一種のCPU例外を呼び出すことにする方法は,カーネル仕様全体の整合性を考
1248 えて採用しなかった.ここで一種のCPU例外を導入するよりも,サービスコー
1249 ルがエラーを返した場合に呼ばれるOSEK/VDX OS仕様のエラーフックに相当す
1250 る機能を導入した方が有用性が高いと思われるためである.
1251
1252この変更により,さらに,CPUロック状態やディスパッチ禁止状態でext_tskが
1253呼ばれた場合にもエラーリターンする方法が考えられるが,タスクのメインルー
1254チンからのリターンとext_tskが等価にならないため,採用しない.また,他の
1255処理単位からのリターン方法と整合させる意味もある(例えば,割込みハンド
1256ラからCPUロック状態のままリターンした場合の扱い).
1257
1258これにあわせて,ext_kerについても,返り値をER型に変更する.ASPカーネル
1259では,ext_kerがエラーを返すことはないが,HRPカーネルでは,E_OACVエラー
1260を返す場合がある.
1261
1262なお,μITRON4.0仕様では,exd_tskもリターンすることのないサービスコール
1263となっているが,TOPPERS新世代カーネルでは,exd_tskはサポートしていない.
1264
1265
1266○カーネルのデータ構造に対するvolatile宣言について(クリティカルセクショ
1267ンの出入処理の実現に関する制約)
1268
1269カーネル内のデータ構造は,並行実行される他の処理単位(割込みハンドラや
1270タスク)からもアクセスされる可能性があるため,volatile宣言が必要ではな
1271いかと考えられる.実際,クリティカルセクション内でカーネル変数を読むコー
1272ドが,コンパイラの最適化によりクリティカルセクション外に移動され,それ
1273が原因となった問題事例も報告されている.
1274
1275カーネル内のすべてのデータ構造にvolatile宣言をつける方法は,安全ではあ
1276るが,最適化が抑止されるために,カーネルのサイズや性能には悪影響を与え
1277る.そこでASPカーネルでは,次の方法でvolatile宣言の必要性をなくすことと
1278する.
1279
1280ASPカーネルにおいては,並行実行される他の処理単位から書き換えられる可能
1281性のあるデータ構造は,すべて,CPUロック状態または全割込みロック状態によ
1282るクリティカルセクション内でアクセスしている.クリティカルセクション内
1283でのデータ構造のアクセスが,コンパイラの最適化によりクリティカルセクショ
1284ン外に移動されないようにするには,コンパイラに対して,クリティカルセク
1285ションの出入処理により,メモリ上のデータ構造が書き変わる可能性があるこ
1286とを知らせればよい.
1287
1288具体的には,クリティカルセクションの出入処理を関数によって実現すれば,
1289このような最適化を抑止することができる.しかし,ASPカーネルの多くのター
1290ゲット依存部において,クリティカルセクションの出入処理はマクロやインラ
1291イン関数により実装されており,上のような最適化を抑止できない.
1292
1293そこで,クリティカルセクションの出入処理を実現する場合には,メモリ上の
1294データ構造が書き変わる可能性があることを,何らかの方法でコンパイラに知
1295らせなければならないという制約を設ける.GNU開発環境では,次のいずれかの
1296方法でこの制約を満たすことができる.
1297
1298(a) クリティカルセクションの出入処理の全体または出入処理の本質的な部分
1299 (具体的には,割込み禁止/許可する処理)を(インラインでない)通常
1300 の関数により実現する.
1301
1302(b) クリティカルセクションの出入処理の本質的な部分をインラインアセンブ
1303 ラによって実現している場合には,そのインラインアセンブラのclobber変
1304 数リストに"memory"を追加する.
1305
1306(c) クリティカルセクションの出入処理の本質的な部分が,マクロやインライ
1307 ン関数呼出しで実現している場合には,クリティカルセクションに入る処
1308 理の最後と出る処理の先頭に,Asm("":::"memory")という記述を入れる.
1309
1310なお,この制約が適用されるクリティカルセクションの出入処理は,以下のも
1311のである.
1312
1313 SIL_LOC_INT
1314 SIL_UNL_INT
1315 t_lock_cpu, i_lock_cpu, x_lock_cpu
1316 t_unlock_cpu, i_unlock_cpu, x_unlock_cpu
1317
1318
1319○型キャストに伴う警告メッセージ
1320
1321GCCで-O2オプションをつけてコンパイルした場合に,カーネルのソースコード
1322中の数箇所で,次の警告メッセージが出る(GCCのバージョンにもよる).
1323
1324warning: dereferencing type-punned pointer will break strict-aliasing rules
1325
1326これは,GCCに-O2オプションをつけると,コンパイラがC言語のstrict
1327aliasing ruleを前提とするためである.コンパイラにstrict aliasing ruleを
1328適用させないためには,GCCのオプションに-fno-strict-aliasingを指定すれば
1329よい.これにより,警告メッセージは抑止されるが,strict aliasing ruleを
1330前提とした最適化は行われなくなる.
1331
1332ASPカーネルに実装においては,strict aliasing ruleを前提とした最適化を行っ
1333てもよく,この警告メッセージを無視しても差し支えない.以下では,この警
1334告メッセージを無視しても差し支えない理由を述べる.
1335
1336警告メッセージが出る例として,semaphore.c中の次の行について検討する.
1337
1338 wobj_make_wait((WOBJCB *) p_semcb, (WINFO_WOBJ *) &winfo_sem);
1339
1340この警告メッセージの原因は,直接的には,&winfo_semを(WINFO_WOBJ *)にキャ
1341ストしていることであるが,本質的な原因は,このコードがC言語のstrict
1342aliasing ruleに従わない代入文の原因になる可能性があり,このルールに依存
1343した最適化が誤った結果を引き起こす可能性があることである.
1344
1345C言語のstrict aliasing ruleは,互換性のない異なる型を通して,オーバラッ
1346プするメモリ領域をアクセスする代入文を使用してはならないというものであ
1347る(使用した場合の振舞いは未定義になる).このケースでは,(WINFO_SEM
1348*)型のポインタ経由と(WINFO_WOBJ *)型のポインタ経由で,オーバラップする
1349メモリ領域をアクセスする代入文を使用してはならないことになる.また,警
1350告メッセージの原因になってはいないが,(SEMCB *)型のポインタ経由と
1351(WOBJCB *)型のポインタ経由で,オーバラップするメモリ領域をアクセスする
1352代入文を使用してはならない.
1353
1354ASPカーネルの実装においては,semaphore.c中の関数においては,(SEMCB *)型
1355および(WINFO_SEM *)型のポインタを使用しており,(WOBJCB *)型および
1356(WINFO_WOBJ *)型のポインタ経由でメモリ領域をアクセスすることはない.一
1357方,そこから呼び出されるwait.c中の関数においては,(WOBJCB *)型および
1358(WINFO_WOBJ *)型のポインタを使用しており,(SEMCB *)型および(WINFO_SEM
1359*)型のポインタ経由でメモリ領域をアクセスすることはない.
1360
1361strict aliasing ruleに従わない代入文の問題点は,このルールに依存した最
1362適化が誤った結果を引き起こす可能性があることであるが,異なるコンパイル
1363単位をまたいで最適化が行われることはないため,これにより問題が起こるこ
1364とはないと言うことができる.
1365
1366同じ議論は,他のソースファイル(eventflag.c,dataqueue.c,pridataq.c,
1367mailbox.c,mempfix.c)中の警告メッセージについても,そのまま当てはまる.
1368
1369
1370○性能評価用システム時刻参照機能
1371
1372●必要性と使途
1373
1374ASPカーネルには,ASPカーネル上で動作するタスクやASPカーネル自身の性能を
1375計測するために,システム時刻より精度の高い性能評価用システム時刻を読み
1376出す機能をサポートする.性能評価用システム時刻は,マイクロ秒単位で表現
1377されるが,実際の精度はターゲット依存である.
1378
1379この機能を用いてあるプログラムの実行時間を計測するには,その実行直前と
1380実行直後に性能評価用システム時刻を読み出し,その差を求める.そのため,
1381性能評価用システム時刻は常に相対値を使用し,性能評価用システム時刻の絶
1382対値を使用することは想定していない.
1383
1384●API仕様
1385
1386性能評価用システム時刻参照機能では,次のデータ型を用いる.
1387
1388 SYSUTM 性能評価用システム時刻(符号無し整数,単位はマイクロ秒,
1389 32ビット以上)
1390
1391SYSUTM型は,ターゲット非依存部においてulong_t型(すなわち,unsigned
1392long型)に定義されており,そのサイズは,32ビット以上で,ターゲット定義
1393である.
1394
1395性能評価用システム時刻がSYSUTM型で表現できる範囲を超えた(つまり,オー
1396バフローした)場合,性能評価用システム時刻は0に戻る.評価対象プログラム
1397の実行前後の性能評価用システム時刻の差を求める場合には,計測する時間が
1398SYSUTM型で表現できる範囲である限り,0に戻ることを特別に考慮する必要はな
1399い.
1400
1401SYSUTM型が32ビットの場合,性能評価用システム時刻は約71分でオーバフロー
1402する.そのため,この機能を71分を越える時間の測定に使った場合の動作は保
1403証されない.
1404
1405性能評価用システム時刻参照機能のためのサービスコールの仕様については,
1406「TOPPERS新世代カーネル統合仕様書」の「4.6.1 システム時刻管理」の節を参
1407照すること.
1408
1409●実装
1410
1411時刻をマイクロ秒単位で取得するために,周期的なタイムティックを供給する
1412タイマの現在値(タイマはカウントアップするものと仮定する)を読み出し,
1413それをマイクロ秒単位に換算した値に,現在のシステム時刻(ミリ秒単位で表
1414現される)を1000倍した値を加えたものを性能評価用システム時刻とする.現
1415在のシステム時刻を1000倍する際に,オーバフローが発生する可能性があるが,
1416無視してかまわない.
1417
1418ただし,システム時刻の現在値とタイマの現在値を一貫した状態で読み出すの
1419は容易ではない.両方の値を順に読み出すと,読出しの間にタイマがオーバフ
1420ローして割込み要求が発生した場合に,片方はオーバフロー前の値,もう片方
1421はオーバフロー後の値を読んでしまい,誤った性能評価用システム時刻を取得
1422してしまう.
1423
1424この問題を解決する方法はいくつか考えられるが,どの方法を採用するの決定
1425にあたり,次の要求事項を設定した.
1426
1427(1) 多くのターゲットシステムで実現できること.
1428
1429(2) サービスコールの実行時間が可能な限り一定となること.言い換えると,
1430 条件によってサービスコールの実行時間が大きく変動しないこと.
1431
1432(3) サービスコール中の可能な限り同じタイミングの時刻を返すこと.言い換
1433 えると,条件によって時刻を読み取るタイミングが変動しないこと.
1434
1435(4) 調整する必要のあるパラメータを最小限とすること.
1436
1437これらの要求事項を満たす方法として,次の方法を用いることにした.
1438
1439まず,NMIを除くすべての割込みを禁止した状態で,システム時刻の現在値,タ
1440イマの現在値(1回目),タイマ割込み要求の有無,タイマの現在値(2回目)
1441を,この順で読み出す.割込みを禁止しているため,この間にシステム時刻の
1442現在値が変化することはなく,システム時刻の現在値を読み出す順番はどこで
1443もよい.また,タイマの現在値の2回目の読出しは,タイマ割込み要求があった
1444場合にのみ必要となるが,(2)の要求から,タイマ割込み要求の有無によらず読
1445み出すこととする.
1446
1447これらの値を読み出した後,割込み禁止を解除し,次の処理を行う.まず,タ
1448イマ割込み要求がなかった場合には,システム時刻の現在値と,1回目に読んだ
1449タイマの現在値は一貫した値であることが保証できるため,これらの値から性
1450能評価用システム時刻の現在値を求める.
1451
1452次にタイマ割込み要求があった場合には,1回目に読んだタイマの現在値が,タ
1453イマ割込み要求発生前の値(オーバフロー前の値)である場合と,発生後の値
1454(オーバフロー後の値)である場合の両方の可能性が考えられる.このどちら
1455の場合であったかを,2回目に読んだタイマの現在値を使って,次のように決定
1456する.2回目の値は,タイマ割込み要求発生後の値(オーバフロー後の値)であ
1457ることが保証できるため,1回目の値が2回目の値よりも大きい場合には,その
1458間にオーバフローがあったものと推測できる.つまり,1回目の値はオーバフロー
1459前の値ということになり,システム時刻の現在値と一貫した値であるとして性
1460能評価用システム時刻の現在値を求める.逆に,1回目の値が2回目の値と同じ
1461かそれより小さい場合には,1回目の値はオーバフロー後の値であると推測でき
1462る.この場合には,次のタイムティックのシステム時刻を求め,その値と1回目
1463の値が一貫した値であるとして性能評価用システム時刻の現在値を求める.
1464
1465ここで,タイマ割込み要求があった場合には,2回目に読んだタイマの現在値を
1466用いる方法が考えられるが,(3)の要求を満たさなくなるために採用しなかった.
1467また,JSPカーネルと同様の方法は,(4)の要求を満たさないために採用しなかっ
1468た.
1469
1470上で「推測できる」としたのは,この推測が成り立たなくなるケースがあるた
1471めである.この推測が成り立たなくなるケースは,次の2つの場合に分けて分析
1472することができる.
1473
1474(a) 1回目の値がオーバフロー後の値であるにもかかわらず,1回目の値が2回目
1475 の値よりも大きくなる場合
1476
1477このようなケースは,タイマ割込みが要求されているにもかかわらずサービス
1478されない状態が長時間続くか,タイマの現在値を1回目に読んでから2回目に読
1479むまでの間に長い時間がかかった結果,その間に(再度)オーバフローが発生
1480した場合に起こる.つまり,タイマ割込みがサービスされない時間が,タイム
1481ティックの周期よりも長くなった場合である.このような場合には,システム
1482時刻の更新も正しく行われなくなる.
1483
1484(b) 1回目の値がオーバフロー前の値であるにもかかわらず,1回目の値が2回目
1485 の値と同じかそれよりも小さくなる場合
1486
1487このようなケースは,タイマの現在値を1回目に読んでから2回目に読むまでの
1488間に,タイマがほぼ1周分カウントアップした場合に起こる.この場合も,タイ
1489マ割込みが禁止されている時間が,タイムティックの周期よりも長かったこと
1490になり,システム時刻の更新が正しく行えなくなる.
1491
1492いずれのケースも,タイマ割込みが長時間禁止されている,タイマ割込みより
1493も優先度の高い割込み処理が長時間続けて実行された,シミュレーション環境
1494においてシミュレータのプロセスが長時間スケジュールされなかったなどの理
1495由で,システム時刻の更新が正しく行えない状況に相当する.そこでこの状況
1496を,サービスコール使用上の注意事項に盛り込む.
1497
1498実際のコードにおいては,システム時刻の現在値は変数に保持されていないた
1499め(上位桁はcurrent_timeに保持されているが,下位桁を保持する変数がな
1500い),次のタイムティックのシステム時刻を用いて計算している.そのため,
1501タイマの現在値がオーバフロー後の値であると判断した場合を除いては,タイ
1502ムティックの周期時間を,求めた性能評価用システム時刻から減算する.この
1503処理により,サービスコールの実行時間が変動することになるが,if文の内容
1504が(コンパイラの最適化を仮定すると)定数値の減算1回なので,変動はわずか
1505である.
1506
1507このサービスコールは,任意の状態から呼び出すことができるため,SILの全割
1508込みロック機能を用いて,サービスコール内部のクリティカルセクションを実
1509現する.
1510
1511
1512○タスク例外処理禁止フラグをenatexで実装している理由
1513
1514タスク例外処理禁止フラグは,TCB中のenatexフィールド(タスク例外処理許可
1515状態であることを示す)の形で保持している.このフィールドをdistexとせず
1516enatexとしたのは,JSPカーネルにおいてタスクディスパッチ禁止フラグを
1517enadspの形で保持したのと整合させたためである.
1518
1519ASPカーネルでは,enadspはdisdspに変更になったことから,enatexもdistexに
1520変更した方が良かったと思われる.
1521
1522以上
Note: See TracBrowser for help on using the repository browser.