トヨタ カムリのETCSにはなぜグローバル変数が1万1000個あったのか、についての推測

http://eetimes.jp/ee/articles/1311/11/news072.html

推測というよりも半ば妄想混じりであるという点を先に述べておく。そもそも私は車載関連の開発自体との縁が全くない。

ただ、車載ではないがミッションクリティカルな組み込み開発(下手すると死人がでてしまう分野)の人から本件のヒントとなりそうな話を昔聞いたので、うろ覚えだが書いておく。

その前に、記事からは不明な点:

  • 1万1000個のグローバル変数は、本当にグローバルなグローバル変数だったのか?
    • 要するにファイルスコープ変数(static修飾子を付けたグローバル変数)は1万1000個の中に含まれているのか、いないのか?
    • 仮に含まれているとしたら、その割合は?
    • 仮に含まれていないとしたら、ではstatic修飾子付きの変数は何個あったのか?
  • 仮に全てstatic修飾子が付いていないグローバル変数だったとして、その中に実際にはファイルスコープとして振る舞っている変数はあるのか、ないのか?
    • 仮に存在するとしたら、その割合は?
    • 世の中にはファイルスコープで十分な変数なのにstaticを付けない/付け忘れる人がいるのです。

あと突っ込み:

  • (上記の記事の書き方として、なのか?)MISRA-Cへの対応が不十分、という指摘の部分が、どうにも「MISRA-Cのルールを全て遵守する == 善」という構図があるように見えてしまうのだが……。
  • 基本は「『93個の必要要件と34個の勧告要件』のうちどれをどのように採用するか決めて、その経緯などをドキュメント化する(どの項目を完全遵守するか、どの項目を部分的に採用するか/どの条件の場合にルール違反を許容するか、どのルールを守らないのか/守らない理由は、その他)→決めた内容を守らせる」という使い方をするのがMISRA-Cだったと思うのだが。私の勘違い?
  • まあでも、トヨタのアレはMISRA-Cから採用しているルール数が少なすぎないか、とは思う。
  • もしかしてトヨタはMISRA-Cに基づいたルールの作成をせずに独自の内規でやっていたのか?(この項は完全に私の妄想)。

さて、「グローバル変数が1万1000個」の件である。C言語を使用している、という前提で書くとしよう(MISRA-Cへの言及があるから、的外れではないだろう)。

世の中の大概のコンピュータはプログラム内蔵方式を採用しているので、プログラムは主記憶(RAM)にロードされてから、主記憶上のプログラムが実行される。このあたりは基本情報技術者にも出てくる話。

ではプログラムは主記憶上にどのような配置でロードされるのか? 一般的に言われるのは、こんな感じだ:

  • 主記憶の先頭アドレス側に実行コード(機械語)が配置される。
  • その次にグローバル変数やstatic修飾子付きの変数群が配置される。
  • その次に動的メモリルーチン(malloc(3)とか)で確保されるメモリ領域が配置される。
  • 主記憶の末尾アドレス側から先頭アドレス側に向かって、関数呼び出しで使用されるスタック領域が配置される。

(ググれば適当なイメージ画像が出てくると思うので、ここには載せない)

この辺りは採用されるOSその他によって微妙に異なる(例えば、仮想記憶が入ってきてしまうので適切な例ではないが、Unixのプロセスのアドレス空間とか、Windowsでのそれとか)。あと仮想記憶の有無によっても異なる気がするが、ここでは仮想記憶を使用していないと仮定して話を進める。

グローバル変数やstatic修飾子付きの変数は、プログラム実行時に主記憶上の特定の位置に固まって配置される。では内部変数はどこに配置されるのか? スタック領域に、関数呼び出しの度に動的に確保され、関数が終了すると解放される(解放されるといっても、他の関数呼び出しで上書きされない限り、使用していた領域に古い値は残ったままだ)。

つまりグローバル変数やstatic修飾子付きの変数は、プログラム実行中に常に主記憶上の同じアドレスに存在し続けるが、関数の引数や内部変数のアドレスは(スタック領域の範囲内ではあるものの)一定ではない。

ここで問題なのは、例えば主記憶として使用するメモリのバグなどで変数が書き換わってしまう問題を捉えようとしたとき、主記憶上のアドレスが変化しないグローバル変数やstatic修飾子付きの変数をモニタリングし続けるのは比較的楽だが、アドレスが一定ではない関数の引数や内部変数では難しい、ということだ。

というか、このエントリの最初に書いた「車載ではないがミッションクリティカルな組み込み開発(下手すると死人がでてしまう分野)の人」から聞いたのが、正にこの内容そのままだ。その人の開発現場では、本当にこのような理由で内部変数の代わりに外部変数を使用するスタイルで開発していたらしい。

この話を聞いた時は、まだ技術レベルが低かった私は「へーそうなんだー」状態だった。しかし今考えてみれば、メモリのバグでスタック領域の値が書き換わってしまったら、関数呼び出しから戻る部分でプログラムが暴走する可能性がある。この点はどう考えていたのだろう?

それはともかく、トヨタ カムリのETCSグローバル変数が1万1000個あったという話の背景には、本エントリで書いたような理由も含まれているのではないかと思う。

追伸

もしかしたら、例えばメモリのバグによるデータ書き換えのような問題は、ハードウェア側の対策(エラー検出機構付きのメモリを使うとか、ミラーリング用のメモリを追加するとか)で解決できるのかもしれない。

仮にそうだとしたら、本来は「ハードウェア追加/改修によるコスト」と「ソフトウェア側で対応しようとしてバグを作りこんでしまう可能性と、それによるコスト」を比較するなどのリスク分析をした上で、どちらを採用するか決めるべき話だ。

もっとも「士農工商メカエレキソフト」な世の中なので、十中八九ソフトウェア側で対応することになる。それによってバグが混入し、後で大問題となった場合、責められるのはソフトウェア開発者だ。

まあ、例えばカムリの2012年の販売台数は404,886台なので、1台につき100円コストダウンできれば、それだけで4,000万円以上の儲けとなる(製造業では同様の理由で部品点数を減らすなどの工夫がなされているものだ)。このロジックを用いて「ハードウェア追加で1台につき100円部品代が上がると4,000万円の利益が吹っ飛ぶぞ」とか言われると、反論しにくいものだ。

ソフトウェア側での特別対応を起因として、どの程度プログラムが難しくなり、どの程度バグを混入してしまう可能性があり、それによって最悪どの程度のコスト(例えばリコールによる改修費用とか)がかかりうるのか――うまい具合に説明する方法はないだろうか?