web-dev-qa-db-ja.com

ヒープメモリ(malloc / new)を使用すると、非決定的なプログラムが作成されますか?

数か月前に宇宙アプリケーション用のCでリアルタイムシステム用のソフトウェアの開発を開始し、C++を使用したマイクロコントローラー用のソフトウェアの開発も始めました。このようなシステムには、ヒープオブジェクトを作成してはならない(したがってmalloc/newを使用しない)という経験則があります。これは、プログラムが非決定的になるためです。人々が私に言ったとき、私はこの声明の正しさを検証することができませんでした。したがって、これは正しいステートメントですか?

私の混乱は、私が知る限り、決定論とは、プログラムを2回実行すると、まったく同じ実行パスが得られることを意味します。私の理解では、これはマルチスレッドシステムの問題です。同じプログラムを複数回実行すると、毎回異なる順序で異なるスレッドが実行される可能性があるためです。

73

リアルタイムシステムのコンテキストでは、反復可能な「実行パス」以上に決定論があります。もう1つの必須プロパティは、キーイベントのタイミングが制限されていることです。ハードリアルタイムシステムでは、許可された時間間隔の外側(その間隔の開始前または終了後)に発生するイベントは、システム障害を表します。

このコンテキストでは、特にプログラムに割り当て、割り当て解除、および再割り当てのさまざまなパターンがある場合、動的メモリ割り当ての使用は非決定性を引き起こす可能性があります。割り当て、割り当て解除、および再割り当てのタイミングは時間とともに変化する可能性があるため、システム全体のタイミングを予測できなくなります。

72
Peter

述べたように、コメントは間違っています。

non-deterministic behaviorでヒープマネージャを使用すると、非決定的動作のプログラムが作成されます。しかし、それは明らかです。

確定性に欠ける動作をするヒープマネージャーの存在は、それほど明白ではありません。おそらく最も有名な例は、プールアロケーターです。 N * Mバイトの配列と、Nビットのavailable[]マスクがあります。割り当てるために、最初に利用可能なエントリ(ビットテスト、O(N)、確定的上限)をチェックします。割り当てを解除するには、使用可能なビット(O(1))を設定します。 malloc(X)は、XをMの次に大きい値に切り上げて、適切なプールを選択します。

特にNとMの選択が高すぎる場合、これはあまり効率的ではないかもしれません。また、低すぎる値を選択すると、プログラムが失敗する可能性があります。ただし、NおよびMの制限は、動的メモリ割り当てのない同等のプログラムよりも低くなる可能性があります。

39
MSalters

C11 標準または n1570 には、mallocが決定的である(またはそうでない)とは記載されていません。また、Linux上の malloc(3) などの他のドキュメントもありません。ところで、多くのmalloc実装は フリーソフトウェアです

しかし、mallocは失敗する可能性があり(実際に失敗します)、そのパフォーマンスは不明です(デスクトップでのmallocへの典型的な呼び出しは、実際にはでしょうマイクロ秒未満で済みますが、非常に負荷の高いコンピューターでは、おそらくさらに数ミリ秒かかる、奇妙な状況を想像できます。 スラッシングについて読みます )。また、Linuxデスクトップには ASLR (アドレス空間レイアウトのランダム化)があるため、同じプログラムを2回実行すると、異なるmalloc- edアドレス(仮想アドレス空間内)処理する)。 BTW here は決定論的(詳しく説明する必要があるという特定の仮定の下)ですが、実用的に役に立たないmalloc実装。

決定論とは、プログラムを2回実行すると、まったく同じ実行パスになることを意味します。

これは、物理環境が変化しているため、ほとんどの組み込みシステムでは実際に間違っています。たとえば、ロケットエンジンを駆動するソフトウェアは、推力、抗力、または風速などがexactlyと同じであることを期待できませんある打ち上げから次の打ち上げまで。

(だから私はあなたがリアルタイムシステムが決定論的であると信じているか、望んでいることに驚いている!決してそうではない!おそらくあなたは WCET を気にかけている。 キャッシュ

ところで、いくつかの「リアルタイム」または「組み込み」システムは、独自のmalloc(またはそのバリアント)を実装しています。 C++プログラムは、標準の container sで使用可能な allocator -sを持つことができます。 this および that なども参照してください。

そして、組み込みソフトウェアの高レベルのレイヤー(自動運転車とその 計画 ソフトウェアを考えてください)は確かにヒープ割り当てを使用し、おそらく ガベージコレクションも使用しています テクニック(その一部は「リアルタイム」)ですが、一般に安全性は重要とは見なされていません。

21

tl; dr:動的メモリ割り当てが本質的に非決定的(同一の実行パスに関して定義したように)であるわけではありません。通常、プログラムを予測不能にします。具体的には、任意の入力シーケンスに直面してアロケーターが失敗するかどうかを予測することはできません。

非決定的なアロケーターを持つことができます。これは、オペレーティングシステムがアドレスレイアウトのランダム化などを使用するリアルタイムの世界では実際に一般的です。もちろん、それはあなたのプログラムを非決定的にするでしょう。

しかし、これは興味深いケースではないので、完全に決定的なアロケーターを想定しましょう:同じシーケンスの割り当てと割り当て解除は、常に同じ場所に同じブロックをもたらし、それらの割り当てと割り当て解除は常に制限された実行時間を持ちます。

これで、プログラムは決定論的になります。同じ入力のセットは、まったく同じ実行パスになります。

問題は、入力に応じてメモリを割り当てたり解放したりすると、割り当てが失敗するかどうかを予測できないことです(失敗はオプションではありません)。

まず、プログラムがメモリをリークする可能性があります。したがって、無期限に実行する必要がある場合、最終的に割り当ては失敗します。

ただし、リークがないことを証明できたとしても、使用可能なメモリよりも多くのメモリを要求する可能性のある入力シーケンスがないことを知っておく必要があります。

しかし、プログラムが使用可能なメモリよりも多くのメモリを必要としないことを証明できたとしても、アロケータは割り当てと解放のシーケンスに応じてメモリを断片化し、最終的に割り当てを満たす連続ブロックを見つけることができない場合があります全体として十分な空きメモリがあります。

病理学的な断片化につながる一連の入力がないことを証明するのは非常に困難です。

断片化がないことを保証するようにアロケーターを設計できます(たとえば、1つのサイズのブロックのみを割り当てることによって)が、呼び出し側にかなりの制約を課し、無駄のために必要なメモリ量を増やす可能性があります。また、呼び出し側は、リークがないこと、および入力のシーケンスに関係なく、必要な合計メモリに満足できる上限があることをまだ証明する必要があります。この負担は非常に高いため、実際には、動的メモリ割り当てを使用しないようにシステムを設計する方が簡単です。

12
Adrian McCarthy

リアルタイムシステムの対処法は、実行される実行パスに関係なく、プログラムが特定の計算とメモリの制限を厳密に満たさなければならないことです(入力に応じてかなり変化する可能性があります)。では、このコンテキストでの汎用的な動的メモリ割り当て(malloc/newなど)の使用はどういう意味ですか?つまり、開発者はある時点で正確なメモリ消費量を判断できず、結果のプログラムがメモリと計算能力の両方の要件を満たすことができるかどうかを判断することはできません。

10
VTT

はい、正しいです。言及するアプリケーションの種類については、発生する可能性のあるすべてを詳細に指定する必要があります。プログラムは、仕様に従って最悪のシナリオを処理し、それ以上、それ以上、またはそれ以上のメモリを正確に確保する必要があります。 「取得する入力の数がわからない」という状況は存在しません。最悪のシナリオは固定数で指定されます。

プログラムは、最悪のシナリオまですべてを処理できるという意味で決定論的でなければなりません。

ヒープのまさに目的は、実行中のプログラム/プロセス/スレッドの量が決定的ではないPCなどで、いくつかの無関係なアプリケーションがRAMメモリを共有できるようにすることです。このシナリオは、リアルタイムシステムには存在しません。

さらに、セグメントは時間の経過とともに追加または削除されるため、ヒープの性質は非決定的です。

詳細はこちら: https://electronics.stackexchange.com/a/171581/6102

7
Lundin

ヒープアロケーターに反復可能な動作がある場合(同じ割り当てのシーケンスと空き呼び出しが同じブロックのシーケンスをもたらすため、(できれば)同じ内部ヒープ状態)、呼び出しのシーケンスが変更されると、ヒープの状態が大幅に変わる可能性があります、予測不可能な方法でメモリ割り当てエラーを引き起こす可能性のあるフラグメンテーションにつながる可能性があります。

ヒープの割り当てが、組み込みシステム、特には禁じられていることで嫌われている理由。航空機や宇宙船の誘導や生命維持システムなどのミッションクリティカルなシステムでは、本質的に非同期イベントに応答して発生する可能性のあるmalloc/free呼び出しのシーケンスで考えられるすべてのバリエーションをテストする方法はありません。

解決策は、各ハンドラーがその目的のために1つのメモリを確保することです。これらのハンドラーが呼び出される順序は(少なくともメモリの使用に関する限り)関係ありません。

5
chqrlie

ハードリアルタイムソフトウェアでヒープを使用する場合の問題は、ヒープの割り当てが失敗する可能性があることです。ヒープがなくなったらどうしますか?

あなたは宇宙アプリケーションについて話している。非常に厳しいノーフェイル要件があります。メモリをリークする可能性がないため、少なくともセーフモードコードを実行するには十分ではありません。転倒してはいけません。 catchブロックを持たない例外をスローしないでください。おそらく、メモリが保護されたOSがないので、1つのクラッシュしたアプリケーションが理論的にはすべてを取り出すことができます。

おそらくヒープをまったく使いたくないでしょう。プログラム全体のコストを上回るメリットはありません。

通常、非決定的とは何か他のものを意味しますが、この場合の最良の読み方は、プログラム全体の動作を完全に予測可能にすることです。

3
Joshua

GHSからの整合性RTOSの紹介:

https://www.ghs.com/products/rtos/integrity.html

およびLynxOS:

http://www.lynx.com/products/real-time-operating-systems/lynxos-178-rtos-for-do-178b-software-certification/

LynxOSおよびIntegrity RTOSは、他の多くが当局(FAAなど)によって承認または認証されていないため、宇宙アプリケーション、ミサイル、航空機などで使用されるソフトウェアの1つです。

https://www.ghs.com/news/230210r.html

宇宙アプリケーションの厳しい基準を満たすために、Integrity RTOSは、ソフトウェアが仕様どおりに動作する正式な検証、つまり数学的に証明されたロジックを実際に提供します。

これらの基準の中で、ここから引用するには:

https://en.wikipedia.org/wiki/Integrity_(operating_system)

そしてここ:

Green Hills Integrity動的メモリ割り当て

これは:

enter image description here

私は正式な方法の専門家ではありませんが、おそらくこの検証の要件の1つは、メモリ割り当てに必要なタイミングの不確実性を取り除くことです。 RTOSでは、すべてのイベントは相互にミリ秒離れて正確に計画されます。また、動的メモリ割り当てには、常にタイミングの問題があります。

数学的には、タイミングとメモリ量に関する最も基本的な仮定からすべてが機能していることを証明する必要があります。

そして、ヒープメモリの代替案を考えると:static memory。アドレスは固定され、割り当てられたサイズは固定されています。メモリ内の位置は固定されています。したがって、メモリの十分性、信頼性、可用性などについて非常に簡単に推論できます。

2
Peter Teoh

簡潔な答え

データ値またはそれらの統計的不確実性の分布には、たとえば、malloc/freeを待機する必要がある非再現性の時間量から導き出すことができる、第1レベルまたは第2レベルのトリガーシンチレータデバイスにいくつかの影響があります。

最悪の側面は、それらがハードウェアではなくメモリの状態(およびその履歴)の物理現象に関係していないことです。

その場合の目標は、これらのエラーの影響を受けたデータから元のイベントシーケンスを再構築することです。再構成/推測されたシーケンスもエラーの影響を受けます。この反復が常に安定したソリューションに収束するとは限りません。正しいとは言わない。あなたのデータはもはや独立していません...あなたは論理的な短絡のリスクを負います...

より長い答え

あなたは「人々がそれを言ったとき、私はこの声明の正しさを検証できなかった」と述べた
純粋に仮説的な状況/ケーススタディを提供しようと思います。

リソースを節約する必要があるシステムで、CCDまたは第1レベルと第2レベルのシンチレータートリガーを扱うと想像してみましょう(宇宙にいます)。
バックグラウンドがMAXBINCOUNTx%になるように獲得率が設定されます。

  • バーストがあり、カウントが急上昇し、ビンカウンターがオーバーフローします。
    すべてが必要です。最大取得レートに切り替えて、バッファを終了します。
    追加のバッファが終了するまで、より多くのメモリを解放/割り当てます。
    あなたは何をしますか?

    1. overflow(データパッケージのタイミングを適切にカウントしようとする)を危険にさらす反作用を維持しますが、この場合、過小評価に移動しますその期間のカウント?
    2. 時系列の穴?を導入するカウンターを停止します。

    ご了承ください:

    • 割り当てを待つと、transient(または少なくともその始まり)が失われます。
    • 何をするにしても、あなたの記憶の状態に依存し、それは再現できません。
  • 代わりに、信号はmaxbincountを中心にハードウェアから許可される最大取得レートで可変であり、イベントは通常より長くなります。
    スペースを完成させて、さらに...を求めます。その間、上記と同じ問題が発生します。
    オーバーフローおよび系統的ピークは、時系列の過小評価またはホールをカウントしますか?

2番目のレベルを移動しましょう(1番目のレベルのトリガーでも可能です)。

ハードウェアから、保管または送信できるよりも多くのデータを受け取ります。
時間または空間でデータをクラスター化する必要があります(2x2、4x4、... 16x16 ... 256x256 ...ピクセルスケーリング...)。

前の問題からの不確実性は、エラー分布に影響する可能性があります。
maxbincountに近いカウントの境界線のピクセルを持つCCD設定があります("where"に依存します)。
これで、CCDにシャワーを浴びることができますが、カウントの総数は同じですが、統計的な不確実性が異なる(待機時間によって導入される部分)、1つの大きなスポットになります...

たとえば、ローレンツ分布を期待している場合、ガウス分布(Voigt)とのコンボリューションを取得できます。または、2番目がdirtyGaussianで本当に支配的な場合.

2
Hastur