組み込みシステムとWEBアプリのグローバル変数の扱いについて

元ネタは、以下からなんですが、もともと新人が C++ で書いたプロトコルがばしばしグローバル変数を叩いていてなんともならん!が発端らしいので、ちょっと筋が違うかもしれないけど、ネタ的に興味深いところがあるので、ちょっと書き連ねておきます。

結論から先に言えば、組み込みシステムの割り込みで使うグローバル変数と、WEBアプリ(ブラウザで作る方)の割り込みはかなり違うので比較ができません。加えて、サーバーサイドのWEBアプリというかWEBシステムも「割り込み」や「非同期」の扱いが、組み込みの非同期とはかなり違います。

組み込みの非同期

先のツイートについては、組み込みの非同期については釈迦に説法のような気もするのですが、このブログの対象としてはあまり知らない分野だと思うので、いちおう。

組み込みにしてもOSあり/なしがあるわけですが、OSを使うにせよ使わないにせよ、「割り込み処理」(インターラプト)内の処理は気を付ける必要があります。いわゆる、ハードウェアの割り込み処理(I/Oポートからのインターラプト)とOSのシグナル、ソフトウェアからの割り込み処理に分けられるわけで、この割り込み処理の中では、

  • スタックが異なる
  • ランタイム(C言語ランタイムなど)の初期化有無がある
  • スレッド(メモリ空間)が異なる場合がある
  • CPUレベルでスイッチングしている

という面がある。

組み込みのC言語プログラミングの場合、C言語ランタイムが初期化されていない場合が多いので通常の関数(printfとかputsとか)が使えないときがある。スタックの切り替えやメモリ空間の切り替えが起こっている可能性もある(ハードウェアのインターラプトの場合はそう)のだけど、これは組み込み用のCPUの場合は、仮想メモリを使わない場合が多いので、プロセスごとに気を付ける必要があるという面もある。

そんなわけで、IntelのようにCPUレベルで仮想化されているわけではないので、プロセス(iTronプロセスみたいに)ごとに相手のプロセス空間を汚さないように注意する必要がある。と同時に、グローバル変数にデータを置くと、自動的に共有メモリとして扱えるので、インターラプト関係では結構便利なので、ついつい使ってしまう、という弱点がある。

先のツイートの元ネタのプロトコル通信でグローバル変数を使っていてあかん、というのはそれで、通信自体はハードウェア的に非同期で走っていてかつ、受信側も非同期で動いているとグローバル変数のメモリ空間は取り合いになってしまうという面がある。そのあたりは、iTron型のリアルタイムOSなのか、OSなしなのかが不明なのでなんとも言えないけど、

  • 全体のメモリが少ないので、プロセス間通信はグローバル変数で受け渡ししてしまうことが多い。
  • ただし、ハード割り込みの関係で、ポーリング形式になりがちな解析部分での非同期化が必要になり、単純な1個のグローバル変数では難しく、リングバッファにするかー、とか考えないといけない。場合によるが。

そんなわけで、組み込みプログラミングに置いて、グローバル変数と割り込みは絶妙な感じにしないといけない。あと、メモリ周りの制限がきついので C++ で大量に new/delete するのは困る。別途 allocater を作るわけだが…という話が待っている。

WEBアプリ(クライアントサイド)の非同期

React.js や Vue.js のように一見、async/await や Promise クラスを使って非同期処理をしているように見えるが、ベースは JavaScript なのでシングルスレッドで動いている。このあたりは見かけ上、非同期処理(特に GUI部分と)をしているように見える、あるいはプログラミングできるというだけになる。

特にシングルページアプリケーション(SPA)の場合は、非同期処理とはいえWeb APIを呼び出している間「待たない」という処理だけなので、組み込みプログラミングでいうところのハードウェア割り込みというものが存在しない。ボタン操作やキーボード操作などは、イベント処理として扱われているので、低レベルな話で言えば OS のポーリング処理に属している。

なので、非同期処理内(ラムダ式とか)であっても、他のクラスを使うこともできるし、グローバル変数(のようなもの)に対しての処理もプログラム言語での仕様でしかなく、ハードウェア上の制限ではない。

特にブラウザアプリケーションの場合は、グローバル変数というものは存在しない。コード内に var 変数として書くこともできるが、これはソースコードを跨げない。ちょうど、C言語のファイル内のスコープと同じになる。唯一、ブラウザ自身を示す window オブジェクトというのがあって、これを媒介してグローバル変数として扱うこともできる。乱暴だが window.a とすれば、a というグローバル変数が作れて別のソースコードから参照可能になる。

まあ、何が言いたいかというと、react.js や vue.js に flux, vuex というストアがあってですね。実質グローバル変数化されるわけですが、これ、ルールを決めて window.* にアクセスすればそれで充分では?と思うのです。実際 vuex が廃れてしまって mvvm パターンの pinia 標準化されつつあるわけで、「グローバル変数」を嫌うあまりに、プロセス/アプリケーションが共通でもつべきメモリ空間、の置き所に混乱が生じてしまっているような気がするのです。まあ、結局のところグローバル変数に落ち着きそうですが。

で、WEBアプリの場合は非同期でラムダ式を使っていたとしても、実質的にはシングルスレッドで動いているのでグローバル変数(windows.* へのアクセス)は競合しません。将来的にネイティブな typescript が wasm 上で実装さえたりすると競合するようになるかもしれませんが、いまのところ大丈夫です。

余談ですが、react, vue ともにビルドをするとひとつの javascript に圧縮されるので、中身で書いた var 変数はグローバル変数化します。ですが、ビルドする前に別のソースコードの変数を参照する手段がない(export/importすれば別だけど)ので、実質グローバル変数がないのです。これは、そういう文法を作ればクリアできる問題かもしれません。

そんなわけで、組み込みプログラミングとWEBアプリでの「グローバル変数」の立ち位置が異なるのと、それにかかわる「非同期処理」のレベルが異なるので、一概にどっちがどうという訳ではありませんね。という話です。

カテゴリー: 開発 パーマリンク