(toppers-users 323) Re: sprintf は使えますか?

Takayuki WAKABAYASHI takayuki @ ertl.ics.tut.ac.jp
2001年 11月 18日 (日) 01:36:26 JST


豊橋技術科学大学の若林です。

"tetsuji okashiwa" <okashiwa @ potato2.hokkai.net>さんは書きました:
 > sin関数の計算を行って、結果をsprintfを用いて文字列に直し、
 > syslogコマンドまたは、serial_writeコマンドなどを使って
 > 小数点の答えをCRTに出力しようとしました。
 > 
 > sinの計算はできていましたが(答えを100培して整数型
 > で表示してみるとちゃんと合っていました)、sprintfを使って、
 > yの小数点の文字列を表示しようとすると文字化けしてできませんでした。

syslogに与えられた文字列や数値は、非同期で表示されています。
char buf[8]をグローバル変数として取得するか、syslog_writeの
メッセージプライオリティを上げることで回避できます。

なんでsyslogが非同期で動いているのかと言うと、
シリアルインタフェースがせいぜい9600-115.2kで動くのに対し、
CPUは33-200MHzと高速に動くため、関数内ですべての文字を出力
しようとすると著しく速度が遅くなってしまいます。また最大
実行時間が引数の文字列の長さに比例し、実時間性/予測可能性
を欠いてしまいます。

そのため、実際にはsyslog_write内ではsyslogバッファに
書式文字列へのポインタと最大5つの"VP_INTの引数"を書き込んでおき、
後々logtaskがこのバッファから情報を読み出して出力しています。
 #常に4バイトx6個の転送が起こるので、最悪実行時間は
 #定数時間となり、高い予測可能性を確保できる

ここで注意しなければいけないのは、バッファには書式文字列と
引数のみが渡るという点です。この仕様のため、実際にvsprintfの
文字列展開を行っているのはlogtaskです。ですから、引数に文字列
へのポインタを与えた場合、logtaskがvsprintfを実行するまで変更
してはいけません。

多分、このプログラムはタスクの一部だと思いますが、syslog_writeの
後に関数を抜け、何度か関数を出たり入ったりしているうちにbufに
相当するスタック領域が変更された後、logtaskが動き出してすでに
書き換えられているbufを展開するのが原因だと思います。ですから
グローバル変数としてバッファを取って、変更しないようにしておけば
回避できます。また整数型でうまくいくのは、整数型は引数にポインタでは
なく値そのものがわたるので、logtaskが正常に展開できるからです。

また、syslog_writeにはもうひとつモードがあって、バッファリング
しないで表示するモードもあります。確か、LOG_NOTICEとなっている
箇所をLOG_EMERGに変えると、バッファリングしない出力になります。
この場合はすべての文字を出力するまで関数から抜けてきません。

syslogにこのような2つのモードを設けることで、
優先度の低いタスクの重要なメッセージが取りこぼされたり、
優先度の高いタスクのどうでもいいデバッグメッセージによって
ドミノエフェクトが発生する危険性を低くすることができます。

以上 参考になれば幸いです。

//-------------------------------------------------
//Takayuki WAKABAYASHI (わかばやし たかゆき)
//  mailto: takayuki @ ertl.ics.tut.ac.jp
//-------------------------------------------------
//豊橋技術科学大学 工学研究科 情報工学専攻
//  組込みリアルタイムシステム研究室
//    Embedded and realtime system laboratory
//      Dept. of information and computer science
//        Toyohashi univ. of technology