五藤テレスコープのMARK-X赤道儀用モータードライブについて ― 2014年05月03日 09時03分04秒
五藤テレスコープが限定個数でMARK-X赤道儀用のモータードライブの発売を発表しました。
本家の五藤光学がモータードライブのサポートをしないので私自身、元社員としてモータードライブの提供について勝手にお手伝いしていました。
前もって弁解しておきますが、社内資料を持ち出したとかそういうことで解析したのではなく、自分のものを分解して解析しています。
以下は音叉式モータードライブのモータ、P型モータードライブのモータ、自作のコントローラ内蔵一体型モータードライブとそれぞれの回転止めピン。コントローラ内蔵一体型のモータードライブをお買い上げいただいた方からいただいた写真です。
現在では自分が使っているMARK-X赤道儀のモータードライブは完全オリジナルで自作しましたし、20台ほどのコントローラ、モータードライブの提供もさせていただきました。
メインで使用しているものはSP赤道儀の架台と連結させてMD-5のモータとベルトでベースモデルの微動軸と連結して駆動させています。
天文ガイドでもオリジナルのモータードライブの製作記事と基板配布などの動きもありました。このモータードライブも一時期私自身も使っていました。
星の家製のモータードライブもありましたね。
さて、そのような経緯があるMARK-X赤道儀用のモータードライブですが、今回の五藤テレスコープの発表では「語られていない」ことを想像で勝手に補足説明させていただきます。
一番の説明不足は以下です。
●新しいコントローラでは古いモーターの駆動はできません
マイクロステップ駆動ができるのは現実的にはバイポーラステッピングモータのみです。※ユニポーラに対応したマイクロステップ駆動のコントローラICがないからです。オリジナルで設計すればもちろんユニポーラでのマイクロステップ駆動も対応できるとは思います。
古いモータはユニポーラステッピングモータを高い減速比の内蔵ギアと更にモーターケース内に減速ギアを設けて使っています。
これは定電圧駆動で電流をあまり流さないようにするための措置で、相抵抗140Ω(P型)から100Ω(コメットトラッカー)程度のPM型ユニポーラステッピングモータを使っているためです。
当時の電源電圧は12Vが主流でした。
これで相当たり100mA前後が流れますが、当時の五藤のコントローラは2-2相励磁で駆動していたので、200mA前後の電流が流れる計算です。
PICマイコンだけはマイクロステップ駆動はできませんのでおそらく、東芝のコントローラICである、TB6608FNGを使っているものと思われます。この根拠はもうひとつあって、去年の胎内星祭で五藤テレスコープはブースを出していましたがそのお隣のお隣ではモータードライブの自作の権威である、外山保広さんがTB6608FNGでモーター駆動の実験をされていたからです。
外山さんは天文ガイドでのMARK-X赤道儀のモータードライブの記事も書かれていました。
バイポーラモータでマイクロステップ駆動をするためにはモータの相抵抗は余り高いものは適しません。先の東芝のコントローラはチョッパーによる定電流駆動に対応しています。
この場合、相抵抗は10Ω程度が適切だとのご教授もいただいておりました。
それはビクセンのMT-1をバイポーラ接続にしてこのコントローラでは脱調するということを言われていたからです。MT-1は相抵抗が20Ωですがバイポーラ接続で使うとその倍の40Ωになってしまいます。これではだめだということです。
古い五藤光学のモータはユニポーラモータで相抵抗が100Ω前後ですからバイポーラ接続すると200Ω前後になり、全く駆動できないでしょう。
なお、PICマイコンで云々ということを発表されていますが、マイクロステップ駆動により通常のパルスを1/32に分解します。つまり供給クロック周波数は通常の32倍で供給する必要がありますが、PICで任意の周波数に調整可能ですので、PICを使っているのは単にクロック周波数発生部分でしょう。
●なぜ、太陽時、月時、星景モード対応にしないか
驚いたことに、いまどき、平均太陽時、月時、星景モード駆動に対応していません。PICマイコンでクロック生成するなら全く難しい技術は不要です。
意味不明です。こればっかりは理由がわかりません。オートガイダー対応を喜ぶ人もいると思いますが、これも最近のデジタルカメラの進歩から余り有効なスペックではありません。
短時間露出をコンポジットすることが一般的になっているからです。
一方でオートガイダー対応しなければいけないような長焦点の望遠鏡を使っている人はとっくに他の赤道儀を使っているか、自分たちで改造しているか、自作している人たちです。
MARK-X赤道儀を使っているけどモータードライブが欲しい人というのは「初心者」な人たちなはずです。ヤフオクでMARK-X手に入れたけどモータードライブないな、というような人たち。
昔から変わらず、五藤はマーケティングができていません。
どうせ出すなら2軸ではなく1軸のモータードライブだったはずです。それでも太陽時、月時、星景モード対応は必須。
手前味噌ですが、こんなモータードライブを出して欲しかったですね。コントローラ内蔵で電源供給するだけで駆動できるコンパクトなモータードライブです。
冒頭の写真の方もモータードライブ持ってるのに当方のコントローラ内蔵一体型のモータードライブを購入されたわけです。古いモータードライブは「コレクション」で実稼動は小さい一体型も使われるのでしょう。
私自身、コメットトラッカー持ってましたが、邪魔なんですよモーターに接続するケーブルが。
1軸でしか使わない時も大きくて存在そのものが邪魔。
気楽に「ぽん着け」でさくっと撮影に入りたいのにコントローラと二本のケーブルが邪魔で使いたくなくなる。
MARK-X自身のコンセプトと正反対のオプションだったわけです。
そのことが全く分かっていないのが、今回発表されたモータードライブです。
●ということでMARK-X対応はもちろん、ビクセン用の対応など今後も行います
本家、五藤光学、五藤テレスコープのモータードライブがたいしたものではなかったので、今後も対応はさせて頂きます。
五藤よりも当方のモータードライブ、コントローラが優れている点は以下です。
(1) 太陽時、月時、星景モード対応
(2) コメットトラッカーモード対応(しかし微調整要)
(3) 五藤以外の各社赤道儀に対応可能
(4) 五藤の古いモータ、音叉式モータードライブのモータなどにも対応可能
(5) リモートスイッチ外付けで可変速での増速・逆転可能
(6) リモートスイッチコネクタ経由でオートガイダー対応も可能(要・自作)
(7) 1軸モータ2組で2軸コントロールも可能
当方のモータードライブで劣っている点はマイクロステップ駆動できないところ。これは追加設計対応を考えています。
それと一番の劣っている点は個人での製造能力。品質管理ができていないところ。これはある意味致命的。
復刻:キーワードガイダンス第9回「リエントラント性とスレッドセーフ」 ― 2014年05月05日 21時46分04秒
初出:技術評論社「組込みプレスVol.11」(平成20(2008)年5月9日発売)
●Linuxライターの作業手順とリエントラント性
突然ですが、「組込みプレス」と「Software Design」という2種類の雑誌があります。
通常はこれら各々の読み手となるターゲットは異なるのですが、たまたまどちらの雑誌もLinuxの特集を行うことになったとします。
さらに、両方の編集部から一人のLinuxを得意とするライターに執筆の
依頼が届いたとします。
大雑把にはLinuxというカテゴリの記事ですが、それぞれ詳細な記事の目的は異なります。
締め切り日も近く、どうしても並行して執筆作業をする必要がありましたが、どちらの仕事もそのライターは引き受けてしまいました。
そのLinuxライターは一人、執筆に使う道具も同じです。
ですが、それぞれの記事はそれぞれの依頼元の意向に沿った形で執筆し、また、提出する原稿の内容はそれぞれ独立している必要があります。
もし同じ原稿用紙に1行ごとにそれぞれの雑誌の記事を書き込んでいく
などということをしたらどうなるでしょうか。
記事の内容としてはとても脈絡のないものになってしまい、依頼元の要求どおりの出力は得られないことでしょう。
一人のライターがそれぞれの雑誌の記事をそれぞれの意向に沿った形で提出できる状態がリエントラント性を持った処理であるということが言えます。
●複数のアプリケーションから同じ処理を呼び出す
ソフトウェアの開発に話を戻します。
ここで、ライターを共通のプログラム、依頼元の意向をプログラムに対しての引数、独立した出力結果を得る為に内部で使用するメモ用紙を独立したリソース、出力結果もまた独立したリソースと読み替えて見ます。
つまり、プログラム実行コードの実体は一つしかなくとも、それぞれの引数を独立して解釈でき、それに応じたリソースを使用して、複数の独立した結果を出せることが「リエントラント性がある」ということが出来ます。
それぞれの処理の手続きの流れを以下に示します。
<組込みプレス出版処理>
①プログラム(kpress.exe)開始
②処理Aを行う
③サブルーチンLinuxWriter(組込みプレス用執筆依頼)を呼び出す
④出力結果を貰う
⑤処理Bを行う
⑥プログラム(kpress.exe)終了
<Software Design出版処理>
①プログラム(sdesign.exe)開始
②処理αを行う
③サブルーチンLinuxWriter(Software Design用執筆依頼)を呼び出す
④出力結果を貰う
⑤プログラム(sdesign.exe)終了
最初のプログラムkpress.exeと次のプログラムsdesign.exeは共に同じ手続きを処理するサブルーチンLinuxWriter()を呼び出しています。
●共通のプログラムはどこに実体があるか
ここで、2つのプログラムを「プロセス」と表現しなおしましょう。
プロセスの場合、kpress.exeとsdesign.exeの両方のプロセスの中にLinuxWriter()がクローンとしてそれぞれに実体が存在し、静的なメモリはもちろんのこと、スタック上の変数などは独立した仮想メモリ空間に存在できます。
この時、LinuxWriter()はスレッドセーフである必要はありません。
ところが「技術評論社プロセス」という一つのプロセスが「組込みプレス出版スレッド」と「SoftwareDesign出版スレッド」を生成、実行してLinuxWriter()を使用する場合はLinuxWriter()はスレッドセーフである必要があります。
では、この2つのプログラムがスレッドや仮想記憶をサポートしないRTOS上で動作するタスクだったとします。
同様に呼び出されるLinuxWriter()はスレッドセーフである必要があります。
更に、LinuxWriter()も独立したタスクとして扱うことにすると、「スレッドセーフ」という表現は使わず、「リエントラント性がある」という言葉を使います。
●まとめ
最後にリエントラント性とスレッドセーフについて整理します。
・プロセス単位で使う共通処理は必ずしもスレッドセーフである必要はない。
・同じメモリ空間を共有しているマルチタスク環境や同じプロセスから生成される複数のスレッドから呼び出させる共通処理はスレッドセーフである必要がある。
・スレッドセーフという言葉はプログラムの一部に存在する共通処理部分に対して使われる。
・リエントラント性という言葉は抽象的な話の中やタスクやスレッド自体が 複数の処理依頼を同時に受け付けることが出来るような場合に使用する。
※最近はこのような場合でもスレッドセーフを使う事も多い。
●Linuxライターの作業手順とリエントラント性
突然ですが、「組込みプレス」と「Software Design」という2種類の雑誌があります。
通常はこれら各々の読み手となるターゲットは異なるのですが、たまたまどちらの雑誌もLinuxの特集を行うことになったとします。
さらに、両方の編集部から一人のLinuxを得意とするライターに執筆の
依頼が届いたとします。
大雑把にはLinuxというカテゴリの記事ですが、それぞれ詳細な記事の目的は異なります。
締め切り日も近く、どうしても並行して執筆作業をする必要がありましたが、どちらの仕事もそのライターは引き受けてしまいました。
そのLinuxライターは一人、執筆に使う道具も同じです。
ですが、それぞれの記事はそれぞれの依頼元の意向に沿った形で執筆し、また、提出する原稿の内容はそれぞれ独立している必要があります。
もし同じ原稿用紙に1行ごとにそれぞれの雑誌の記事を書き込んでいく
などということをしたらどうなるでしょうか。
記事の内容としてはとても脈絡のないものになってしまい、依頼元の要求どおりの出力は得られないことでしょう。
一人のライターがそれぞれの雑誌の記事をそれぞれの意向に沿った形で提出できる状態がリエントラント性を持った処理であるということが言えます。
●複数のアプリケーションから同じ処理を呼び出す
ソフトウェアの開発に話を戻します。
ここで、ライターを共通のプログラム、依頼元の意向をプログラムに対しての引数、独立した出力結果を得る為に内部で使用するメモ用紙を独立したリソース、出力結果もまた独立したリソースと読み替えて見ます。
つまり、プログラム実行コードの実体は一つしかなくとも、それぞれの引数を独立して解釈でき、それに応じたリソースを使用して、複数の独立した結果を出せることが「リエントラント性がある」ということが出来ます。
それぞれの処理の手続きの流れを以下に示します。
<組込みプレス出版処理>
①プログラム(kpress.exe)開始
②処理Aを行う
③サブルーチンLinuxWriter(組込みプレス用執筆依頼)を呼び出す
④出力結果を貰う
⑤処理Bを行う
⑥プログラム(kpress.exe)終了
<Software Design出版処理>
①プログラム(sdesign.exe)開始
②処理αを行う
③サブルーチンLinuxWriter(Software Design用執筆依頼)を呼び出す
④出力結果を貰う
⑤プログラム(sdesign.exe)終了
最初のプログラムkpress.exeと次のプログラムsdesign.exeは共に同じ手続きを処理するサブルーチンLinuxWriter()を呼び出しています。
●共通のプログラムはどこに実体があるか
ここで、2つのプログラムを「プロセス」と表現しなおしましょう。
プロセスの場合、kpress.exeとsdesign.exeの両方のプロセスの中にLinuxWriter()がクローンとしてそれぞれに実体が存在し、静的なメモリはもちろんのこと、スタック上の変数などは独立した仮想メモリ空間に存在できます。
この時、LinuxWriter()はスレッドセーフである必要はありません。
ところが「技術評論社プロセス」という一つのプロセスが「組込みプレス出版スレッド」と「SoftwareDesign出版スレッド」を生成、実行してLinuxWriter()を使用する場合はLinuxWriter()はスレッドセーフである必要があります。
では、この2つのプログラムがスレッドや仮想記憶をサポートしないRTOS上で動作するタスクだったとします。
同様に呼び出されるLinuxWriter()はスレッドセーフである必要があります。
更に、LinuxWriter()も独立したタスクとして扱うことにすると、「スレッドセーフ」という表現は使わず、「リエントラント性がある」という言葉を使います。
●まとめ
最後にリエントラント性とスレッドセーフについて整理します。
・プロセス単位で使う共通処理は必ずしもスレッドセーフである必要はない。
・同じメモリ空間を共有しているマルチタスク環境や同じプロセスから生成される複数のスレッドから呼び出させる共通処理はスレッドセーフである必要がある。
・スレッドセーフという言葉はプログラムの一部に存在する共通処理部分に対して使われる。
・リエントラント性という言葉は抽象的な話の中やタスクやスレッド自体が 複数の処理依頼を同時に受け付けることが出来るような場合に使用する。
※最近はこのような場合でもスレッドセーフを使う事も多い。
復刻:キーワードガイダンス第10回「再起呼び出し禁止」 ― 2014年05月06日 06時46分24秒
初出:技術評論社「組込みプレスVol.12」(平成20(2008)年8月8日発売)
●スマートなプログラミング
プログラミングテクニックとして「再帰呼び出し」という方法があります。
「リカーシブコール(recursive call)」とも呼びます。
関数などの処理の中で自分自身(の関数)を呼び出すという方法で、
抽象的な概念のアルゴリズムを表現するのに便利です。
例えば1からnまでの整数の足し算を行うC言語関数については
再帰呼び出しを使うと以下のようになります。
int SumFunc(int n)
{
if( n<=1 ) return n;
return(n+SumFunc(n-1));
}
再帰呼び出しではない方法で上記の処理を行う場合はforループなどを
使用した処理が必要になります。
例えば、以下のようになります。
int SumFunc1(int n)
{
int sum;
for(sum=0;n>0;n--)
{
sum += n;
}
return sum;
}
●無限と有限
再帰呼び出しのメリットは以下になります。
(1) 複雑なアルゴリズムを少ないソースコードで表現できる
(2) 概念的には無限のパターンの処理を記述できる
少ないソースコードで任意の引数に応じた結果を返す処理を記述できるため、ソフトウェアの開発効率としては非常に高いことになります。
同じアルゴリズムの考え方は言語にもCPUにも依存しません。
メリットだけを考えると非常に有効な手法となります。
しかし、再帰呼び出しには以下のデメリットがあります。
(a) 関数コールを繰り返すため、スタック領域を大量に消費する
(b) 例題のような処理の場合には単なるループ処理よりも処理時間を消費するつまり、メモリ領域や処理時間の制限がある組込みシステム開発では使ってはいけないテクニックの一つです。
組込みシステムでは常に「有限」を意識する必要があるからです。
●アルゴリズムの移植の問題
なお、自ら新規に開発する場合には再帰呼び出しを封印することは可能ですが、他のシステム開発で実装されたアルゴリズムを流用するような場合には注意が必要です。
複雑なアルゴリズムが再帰呼び出しで実装される可能性として例えば、画像処理、圧縮処理、暗号処理などが考えられます。
このようなアルゴリズムの流用を行う場合には、予めスタック使用量が
変動するかどうか、スタックの最大消費量はどれ位になるかなどの検討を事前に行う必要があります。
●組込み開発流実装例
さて、再帰呼び出しの例として1からnまでの整数の足し算を挙げました。
組込みソフトウェア開発で頻繁に行われる実装例を以下に示します。
int SumFunc2(int n)
{
#define MAX (sizeof(tbl)/sizeof(int))
#define ERROR -1
const int tbl[] = { 1,3,6 };
if( n>=1 && n<=MAX )
return tbl[n-1];
else
return ERROR;
}
CPUでは計算させずに予め計算結果を格納した配列を確保して、
計算結果は取り出すだけとする方法です。
この方法のメリットは以下になります。
(1) ループ処理を行わないため、処理速度がほぼ一定
(2) スタック消費量が一定
デメリットとしては以下になります。
(a) 処理できるパターンが有限
(b) 処理パターンとROMの使用量が比例して増加する
実際にはそれぞれの手法のメリットとデメリットを考慮して実装すること
になりますが、再帰呼び出しは許容できないケースが多くなるでしょう。
●スマートなプログラミング
プログラミングテクニックとして「再帰呼び出し」という方法があります。
「リカーシブコール(recursive call)」とも呼びます。
関数などの処理の中で自分自身(の関数)を呼び出すという方法で、
抽象的な概念のアルゴリズムを表現するのに便利です。
例えば1からnまでの整数の足し算を行うC言語関数については
再帰呼び出しを使うと以下のようになります。
int SumFunc(int n)
{
if( n<=1 ) return n;
return(n+SumFunc(n-1));
}
再帰呼び出しではない方法で上記の処理を行う場合はforループなどを
使用した処理が必要になります。
例えば、以下のようになります。
int SumFunc1(int n)
{
int sum;
for(sum=0;n>0;n--)
{
sum += n;
}
return sum;
}
●無限と有限
再帰呼び出しのメリットは以下になります。
(1) 複雑なアルゴリズムを少ないソースコードで表現できる
(2) 概念的には無限のパターンの処理を記述できる
少ないソースコードで任意の引数に応じた結果を返す処理を記述できるため、ソフトウェアの開発効率としては非常に高いことになります。
同じアルゴリズムの考え方は言語にもCPUにも依存しません。
メリットだけを考えると非常に有効な手法となります。
しかし、再帰呼び出しには以下のデメリットがあります。
(a) 関数コールを繰り返すため、スタック領域を大量に消費する
(b) 例題のような処理の場合には単なるループ処理よりも処理時間を消費するつまり、メモリ領域や処理時間の制限がある組込みシステム開発では使ってはいけないテクニックの一つです。
組込みシステムでは常に「有限」を意識する必要があるからです。
●アルゴリズムの移植の問題
なお、自ら新規に開発する場合には再帰呼び出しを封印することは可能ですが、他のシステム開発で実装されたアルゴリズムを流用するような場合には注意が必要です。
複雑なアルゴリズムが再帰呼び出しで実装される可能性として例えば、画像処理、圧縮処理、暗号処理などが考えられます。
このようなアルゴリズムの流用を行う場合には、予めスタック使用量が
変動するかどうか、スタックの最大消費量はどれ位になるかなどの検討を事前に行う必要があります。
●組込み開発流実装例
さて、再帰呼び出しの例として1からnまでの整数の足し算を挙げました。
組込みソフトウェア開発で頻繁に行われる実装例を以下に示します。
int SumFunc2(int n)
{
#define MAX (sizeof(tbl)/sizeof(int))
#define ERROR -1
const int tbl[] = { 1,3,6 };
if( n>=1 && n<=MAX )
return tbl[n-1];
else
return ERROR;
}
CPUでは計算させずに予め計算結果を格納した配列を確保して、
計算結果は取り出すだけとする方法です。
この方法のメリットは以下になります。
(1) ループ処理を行わないため、処理速度がほぼ一定
(2) スタック消費量が一定
デメリットとしては以下になります。
(a) 処理できるパターンが有限
(b) 処理パターンとROMの使用量が比例して増加する
実際にはそれぞれの手法のメリットとデメリットを考慮して実装すること
になりますが、再帰呼び出しは許容できないケースが多くなるでしょう。
復刻:キーワードガイダンス第11回「ハイブリッドOS」 ― 2014年05月08日 22時55分51秒
初出:技術評論社「組込みプレスVol.13」(平成20(2008)年11月7日発売)
●いいとこどり
「ハイブリッド」という言葉が一般的に使われるようになった代表としては「ハイブリッドカー」(Hybrid Car)が挙げられます。
電気で動作する「モーター」とガソリンなどの燃料を使った「エンジン」(内燃機関)の複数の動力源を使い分けるなど、複数の技術を応用した車のことです。
エンジンとモータを駆動源として切り替えるタイプとエンジンを単なるモータ駆動のための発電機として使うタイプに大きく分けられます。
前者を「パラレル方式」、後者を「シリーズ方式」と呼びます。
ハイブリッドカーはエンジンと電気モータの「いいとこどり」をした車ということができます。
同じように開発効率および移植性の高さとリアルタイム性能の追求という二つの「いいとこどり」を狙った技術が「ハイブリッドOS」です。
●ハイブリッド化の意義
ハイブリッドOSはひとつのプロセッサでμITRONとLinuxを共存させたり、μITRONとWindowsCEを共存させるというようなことが行われます。
つまり、リアルタイム性能が高いとされる、μITRONと他のOSとの組み合わせで語られることが多いようです。
この場合のLinuxまたはWindowsCEなどのOSをここでは便宜的に「ゲストOS」と呼びましょう。
このようなことを行う視点は二つあります。
(1) μITRON資産重視
これはμITRONで開発した資産をそのまま使い、μITRON上で対応していないプロトコルスタックなどの追加開発を行うのが大変という場合。
この大変な開発がゲストOSでは既に解決済みであることが重要です。
TCP/IP関連のプロトコルスタック、ファイルシステムとの連携などについて目を向けられることが多くなります。
(2) リアルタイム性能補完重視
ゲストOSでの豊富な開発資産があるが、製品性能的にリアルタイム性能が要求されるイベント処理を追加実装したい場合。
●ハイブリッド化が必要なシステム
(1)の視点でハイブリッド化を行う場合、既にリアルタイム性能が要求されるデバイス制御を伴った実装が行われているということが重要です。
つまり、ベースシステムがそれほどリアルタイム性能を追求していないμITRONシステムなのであれば、ハイブリッド化するまでもなく、ゲストOS環境に乗り換えれば済む話です。
(2)の視点でも同様の側面があります
リアルタイム性能を引き上げたいというのは既存の機能の性能改善という意味ではなく、独立したハードウェアイベントの処理について語る必要があるというところが重要です。
つまり、例えばTCP/IPの通信性能を上げたいとか、WWWサーバの応答性を上げたいという意味のリアルタイム性能補完ではなく、特定の追加デバイスの割り込み応答性能がLinuxのデバイスドライバでは満たせない、というような場合にのみ有効だということです。
そのような特殊デバイスや特殊イベント処理の対応予定もないのにハイブリッドOSを使う必要はありません。
●ハイブリッドOSの実装の基本は「パラレル方式」
以上のようにハイブリッドOSの利用目的のポイントを考えるとハイブリッドカーでの「シリーズ方式」は選択肢からは外れます。
μITRON上でイベント検知してその応答処理をLinux側のプロセスで処理するというような構成はあまり意味がないからです。
リアルタイム性能が要求されるデバイスに対する処理は全てμITRONで完結させて、WWWサーバ経由での表示処理などの少しくらい遅れても構わない処理のためにその情報をμITRONからLinuxのプロセスに通知するというような使い方になります。
このような目的を踏まえてハイブリッドOSには主に以下の機能が実装されます。
(a) ゲストOS起動機能
(b) 割り込み調停機能
(c) OS間通信機能
割り込み調停機能部分でデバイス単位の割り込み処理対象OSを確定するようにパラレル制御します。
●いいとこどり
「ハイブリッド」という言葉が一般的に使われるようになった代表としては「ハイブリッドカー」(Hybrid Car)が挙げられます。
電気で動作する「モーター」とガソリンなどの燃料を使った「エンジン」(内燃機関)の複数の動力源を使い分けるなど、複数の技術を応用した車のことです。
エンジンとモータを駆動源として切り替えるタイプとエンジンを単なるモータ駆動のための発電機として使うタイプに大きく分けられます。
前者を「パラレル方式」、後者を「シリーズ方式」と呼びます。
ハイブリッドカーはエンジンと電気モータの「いいとこどり」をした車ということができます。
同じように開発効率および移植性の高さとリアルタイム性能の追求という二つの「いいとこどり」を狙った技術が「ハイブリッドOS」です。
●ハイブリッド化の意義
ハイブリッドOSはひとつのプロセッサでμITRONとLinuxを共存させたり、μITRONとWindowsCEを共存させるというようなことが行われます。
つまり、リアルタイム性能が高いとされる、μITRONと他のOSとの組み合わせで語られることが多いようです。
この場合のLinuxまたはWindowsCEなどのOSをここでは便宜的に「ゲストOS」と呼びましょう。
このようなことを行う視点は二つあります。
(1) μITRON資産重視
これはμITRONで開発した資産をそのまま使い、μITRON上で対応していないプロトコルスタックなどの追加開発を行うのが大変という場合。
この大変な開発がゲストOSでは既に解決済みであることが重要です。
TCP/IP関連のプロトコルスタック、ファイルシステムとの連携などについて目を向けられることが多くなります。
(2) リアルタイム性能補完重視
ゲストOSでの豊富な開発資産があるが、製品性能的にリアルタイム性能が要求されるイベント処理を追加実装したい場合。
●ハイブリッド化が必要なシステム
(1)の視点でハイブリッド化を行う場合、既にリアルタイム性能が要求されるデバイス制御を伴った実装が行われているということが重要です。
つまり、ベースシステムがそれほどリアルタイム性能を追求していないμITRONシステムなのであれば、ハイブリッド化するまでもなく、ゲストOS環境に乗り換えれば済む話です。
(2)の視点でも同様の側面があります
リアルタイム性能を引き上げたいというのは既存の機能の性能改善という意味ではなく、独立したハードウェアイベントの処理について語る必要があるというところが重要です。
つまり、例えばTCP/IPの通信性能を上げたいとか、WWWサーバの応答性を上げたいという意味のリアルタイム性能補完ではなく、特定の追加デバイスの割り込み応答性能がLinuxのデバイスドライバでは満たせない、というような場合にのみ有効だということです。
そのような特殊デバイスや特殊イベント処理の対応予定もないのにハイブリッドOSを使う必要はありません。
●ハイブリッドOSの実装の基本は「パラレル方式」
以上のようにハイブリッドOSの利用目的のポイントを考えるとハイブリッドカーでの「シリーズ方式」は選択肢からは外れます。
μITRON上でイベント検知してその応答処理をLinux側のプロセスで処理するというような構成はあまり意味がないからです。
リアルタイム性能が要求されるデバイスに対する処理は全てμITRONで完結させて、WWWサーバ経由での表示処理などの少しくらい遅れても構わない処理のためにその情報をμITRONからLinuxのプロセスに通知するというような使い方になります。
このような目的を踏まえてハイブリッドOSには主に以下の機能が実装されます。
(a) ゲストOS起動機能
(b) 割り込み調停機能
(c) OS間通信機能
割り込み調停機能部分でデバイス単位の割り込み処理対象OSを確定するようにパラレル制御します。
復刻:キーワードガイダンス第12回「assert禁止」 ― 2014年05月09日 00時03分59秒
初出:技術評論社「組込みプレスVol.14」(平成21(2009)年2月21日発売)
●論理エラー検出手法
C言語およびC++言語のプログラミングテクニックの一つとしてassert()マクロの利用があります。
例えば、ポインタを引数とする関数を利用する場合、引数となるポインタがNULLではないということを「期待」しているような場合、以下のようになります。
void funcp ( void * p )
{
assert( NULL != p );
//ポインタ正常時処理
}
このように作られた関数に対して引数にNULLを与えて関数コールすると以下のようなエラーメッセージが出力されます。
atest: asserttest.c:22: funcp: Assertion `((void *)0) != p' failed.
assert()マクロはこのように論理的に期待している状態と異なる状態を検出してプログラムの実行を停止させることが出来るのです。
非常に便利な機能であることは確かで、デバッグ後には「-DNDEBUG」をCコンパイラの引数に指定するなどの方法で簡単にassert()が生成するコードを削除することができます。
ソースコード上にassert()を挿入して単体テストの効率を上げるというような発想の下に積極的な活用が薦められることも少なくありません。
●性善説のassert
先の例のようにポインタがNULLになるような条件というものは一過性のデバッグ時期にのみ発生する不具合と言い切ることが出来るでしょうか。
呼び出し側で値を保証すべきであり、呼び出された側ではそのようなチェックを行う必要は本当にないのでしょうか。
これは不具合のないプログラム実装が行われるべき、という考えが前提の「性善説」が基本になっているという言い方もできます。
仮想記憶システムではない環境では複数のタスクから共通のRAM領域へのアクセスが可能であることから、関係のない処理の想定外の不具合によって自関数のみが使用しているはずのポインタ変数がクリアされるような現象が発生しないとは言い切れません。
「いつの間にか」、「突然に」、「想定外に」NULLに変わってしまうというような現象が発生することになります。
assert()でチェックしている「デバッグ時期」には現象が発生せず、assert()を無効にした「リリース後」に発生する不具合かもしれません。
●assertとfail-safe
一方で、組込み機器は実行時に不正な状況に陥ってもシステムを停止させずに安定稼動させる必要があるという命題を持っています。
「デバッグ時にのみ存在する不正な状態」とは限定すべきではなく、「常に不正な状態を回避する実装」が要求されます。
いわゆるfail-safe(フェイルセーフ)の概念が必要で、不正な状況に陥っても単純にプログラムを停止させて「プログラマに知らせる」のではなく、安全な状態でシステムを落ち着かせる必要があります。
この意味においてはassertの概念と組込みシステム開発におけるfail-safeの概念は根本から食い違っていると言えます。
したがって、デバッグ時にのみ論理的に不正な状態を検出するassert()は組込みシステム開発では使用すべきではなく、常に論理的な不正を検出して対応を行うための機能をリリース版として実装する必要があるわけです。
最後に先ほどの関数のassert()を使わない場合のコードは以下のようになります。
void funcp ( void * p )
{
if( isvalid(p) ) {
//ポインタ正常時処理
}
else {
//ポインタ不正時処理
}
}
●論理エラー検出手法
C言語およびC++言語のプログラミングテクニックの一つとしてassert()マクロの利用があります。
例えば、ポインタを引数とする関数を利用する場合、引数となるポインタがNULLではないということを「期待」しているような場合、以下のようになります。
void funcp ( void * p )
{
assert( NULL != p );
//ポインタ正常時処理
}
このように作られた関数に対して引数にNULLを与えて関数コールすると以下のようなエラーメッセージが出力されます。
atest: asserttest.c:22: funcp: Assertion `((void *)0) != p' failed.
assert()マクロはこのように論理的に期待している状態と異なる状態を検出してプログラムの実行を停止させることが出来るのです。
非常に便利な機能であることは確かで、デバッグ後には「-DNDEBUG」をCコンパイラの引数に指定するなどの方法で簡単にassert()が生成するコードを削除することができます。
ソースコード上にassert()を挿入して単体テストの効率を上げるというような発想の下に積極的な活用が薦められることも少なくありません。
●性善説のassert
先の例のようにポインタがNULLになるような条件というものは一過性のデバッグ時期にのみ発生する不具合と言い切ることが出来るでしょうか。
呼び出し側で値を保証すべきであり、呼び出された側ではそのようなチェックを行う必要は本当にないのでしょうか。
これは不具合のないプログラム実装が行われるべき、という考えが前提の「性善説」が基本になっているという言い方もできます。
仮想記憶システムではない環境では複数のタスクから共通のRAM領域へのアクセスが可能であることから、関係のない処理の想定外の不具合によって自関数のみが使用しているはずのポインタ変数がクリアされるような現象が発生しないとは言い切れません。
「いつの間にか」、「突然に」、「想定外に」NULLに変わってしまうというような現象が発生することになります。
assert()でチェックしている「デバッグ時期」には現象が発生せず、assert()を無効にした「リリース後」に発生する不具合かもしれません。
●assertとfail-safe
一方で、組込み機器は実行時に不正な状況に陥ってもシステムを停止させずに安定稼動させる必要があるという命題を持っています。
「デバッグ時にのみ存在する不正な状態」とは限定すべきではなく、「常に不正な状態を回避する実装」が要求されます。
いわゆるfail-safe(フェイルセーフ)の概念が必要で、不正な状況に陥っても単純にプログラムを停止させて「プログラマに知らせる」のではなく、安全な状態でシステムを落ち着かせる必要があります。
この意味においてはassertの概念と組込みシステム開発におけるfail-safeの概念は根本から食い違っていると言えます。
したがって、デバッグ時にのみ論理的に不正な状態を検出するassert()は組込みシステム開発では使用すべきではなく、常に論理的な不正を検出して対応を行うための機能をリリース版として実装する必要があるわけです。
最後に先ほどの関数のassert()を使わない場合のコードは以下のようになります。
void funcp ( void * p )
{
if( isvalid(p) ) {
//ポインタ正常時処理
}
else {
//ポインタ不正時処理
}
}
最近のコメント