23

AVRマイクロコントローラー(ATMega328P)で実行されているCプログラムで問題が発生しました。スタック/ヒープの衝突によるものと思いますが、確認したいと思います。

スタックとヒープごとにSRAMの使用状況を視覚化する方法はありますか?

注:プログラムはavr-gccでコンパイルされ、avr-libcを使用します。

更新:私が抱えている実際の問題は、mallocの実装が失敗している(returning NULL)ことです。すべてのmallocingは起動時に発生し、すべてのfreeingはアプリケーションの最後に発生します(実際には、アプリケーションの主要部分が無限ループにあるため、これは決して発生しません)。したがって、断片化は問題ではないと確信しています。

4

8 に答える 8

24

http://www.avrfreaks.net/index.php?name=PNphpBB2&file=viewtopic&t=62968、http://www.avrfreaks.net/index.php?name=avr-size説明されているように、ユーティリティを使用してRAMの静的使用量を確認できます。 PNphpBB2&file = viewtopic&t = 82536http: //www.avrfreaks.net/index.php?name = PNphpBB2&file = viewtopic&t = 95638 、 およびhttp://letsmakerobots.com/node/27115



avr-size -C -x Filename.elf

(avrサイズのドキュメント:http ://ccrma.stanford.edu/planetccrma/man/man1/avr-size.1.html )

IDEでこれを設定する方法の例に従います。Code::Blocksで、プロジェクト->ビルドオプション->ビルド前/ビルド後の手順->ビルド後の手順には、次のものが含まれます。

avr-size -C $(TARGET_OUTPUT_FILE) また
avr-size -C --mcu=atmega328p $(TARGET_OUTPUT_FILE)

ビルド終了時の出力例:

AVR Memory Usage
----------------
Device: atmega16

Program:    7376 bytes (45.0% Full)
(.text + .data + .bootloader)

Data:         81 bytes (7.9% Full)
(.data + .bss + .noinit)

EEPROM:       63 bytes (12.3% Full)
(.eeprom) 

データはSRAMの使用量であり、コンパイル時にコンパイラーが認識している量のみです。また、実行時に作成されるもの(特にスタックの使用)のためのスペースも必要です。

スタックの使用状況(ダイナミックRAM)を確認するには、http://jeelabs.org/2011/05/22/atmega-memory-use/から

これは、現在使用されているRAMの量を決定する小さなユーティリティ関数です。

int freeRam () {
  extern int __heap_start, *__brkval; 
  int v; 
  return (int) &v - (__brkval == 0 ? (int) &__heap_start : (int) __brkval); 
}

そして、そのコードを使用したスケッチは次のとおりです。

void setup () {
    Serial.begin(57600);
    Serial.println("\n[memCheck]");
    Serial.println(freeRam());
}

freeRam()関数は、ヒープの終わりとスタックに最後に割り当てられたメモリの間に存在するバイト数を返すため、スタック/ヒープが衝突する前に実質的にどれだけ大きくなるかを示します。

スタック/ヒープの衝突を引き起こしていると思われるコードの前後で、この関数の戻り値を確認できます。

于 2011-08-25T13:04:04.513 に答える
13

あなたはmallocが失敗してNULLを返していると言います:

最初に確認する必要がある明らかな原因は、ヒープが「いっぱい」であるということです。つまり、mallocに要求したメモリは使用できないため、割り当てることができません。

覚えておくべき2つのシナリオがあります。

a:16 Kのヒープがあり、すでに10 Kをmallocし、さらに10Kをmallocしようとします。ヒープが小さすぎます。

b:より一般的には、16 kのヒープがあり、malloc / free / realloc呼び出しを何度も実行していて、ヒープが50%未満です。「フル」:mallocを1Kで呼び出しても、失敗します。どうしたのですか。回答-ヒープの空き領域が断片化されています-返される可能性のある連続した1Kの空きメモリがありません。Cヒープマネージャは、これが発生したときにヒープを圧縮できないため、一般的に悪い方法です。断片化を回避するための手法はありますが、これが本当に問題であるかどうかを知ることは困難です。どの動的メモリ操作が実行されているかを把握できるように、mallocとfreeにロギングシムを追加する必要があります。

編集:

すべてのmallocは起動時に発生すると言うので、断片化は問題ではありません。

その場合、動的割り当てを静的割り当てに簡単に置き換えることができます。

古いコード例:

char *buffer;

void init()
{
  buffer = malloc(BUFFSIZE);
}

新しいコード:

char buffer[BUFFSIZE];

これをどこでも実行すると、すべてが使用可能なメモリに収まらない場合、リンカーは警告を表示する必要があります。ヒープサイズを減らすことを忘れないでください。ただし、一部のランタイムioシステム関数は引き続きヒープを使用する可能性があるため、ヒープを完全に削除できない場合があることに注意してください。

于 2009-06-22T12:21:46.167 に答える
3

通常のアプローチは、既知のパターンでメモリを埋めてから、どの領域が上書きされているかを確認することです。

于 2009-06-06T20:22:06.957 に答える
3

埋め込みターゲットでヒープ/動的割り当てを使用しないでください。特に、このような限られたリソースを持つプロセッサの場合。プログラムが大きくなるにつれて問題が再発するため、アプリケーションを再設計してください。

于 2009-06-11T09:37:44.080 に答える
2

スタックとヒープの両方を使用している場合は、もう少し注意が必要です。ヒープを使用しない場合に行ったことを説明します。原則として、私が(組み込みCソフトウェアのドメインで)働いてきたすべての企業は、ヒープメモリの可用性の不確実性を回避するために、小さな組み込みプロジェクトにヒープを使用することを避けてきました。代わりに、静的に宣言された変数を使用します。

1つの方法は、起動時にスタック領域の大部分を既知のパターン(0x55など)で埋めることです。これは通常、ソフトウェア実行の早い段階で、起動コードのmain()の開始直後、またはおそらくmain()の開始前の小さなコードによって実行されます。もちろん、その時点で使用中の少量のスタックを上書きしないように注意してください。次に、ソフトウェアをしばらく実行した後、スタックスペースの内容を調べて、0x55がまだ損傷していない場所を確認します。「検査」の方法は、ターゲットハードウェアによって異なります。デバッガーが接続されていると仮定すると、マイクロの実行を停止してメモリを読み取ることができます。

メモリアクセスブレークポイント(通常の実行ブレークポイントよりも少し凝ったもの)を実行できるデバッガーがある場合は、スタックスペースの最も遠い制限など、特定のスタック位置にブレークポイントを設定できます。これは、スタック使用量の範囲に達したときに実行されているコードのビットを正確に示すため、非常に便利です。ただし、デバッガーがメモリアクセスブレークポイント機能をサポートしている必要があり、「ローエンド」デバッガーにはないことがよくあります。

ヒープも使用している場合は、スタックとヒープが衝突する場所を予測できない可能性があるため、少し複雑になる可能性があります。

于 2009-06-07T08:25:27.597 に答える
1

スタックを1つだけ使用していて(RTOSなどではない)、スタックがメモリの最後にあり、成長していると仮定します。ヒープはBSS / DATA領域の後に始まり、成長します。実際にスタックポインターをチェックし、衝突で失敗するmallocの実装を見てきました。あなたはそれを試みることができます。

mallocコードを適応させることができない場合は、(リンカーファイルを使用して)メモリの先頭にスタックを配置することを選択できます。一般に、スタックの最大サイズを知っている/定義することは常に良い考えです。先頭に置くと、RAMの先頭を超えて読み取るときにエラーが発生します。ヒープは最後にあり、それが適切な実装である場合、おそらく最後を超えて成長することはできません(代わりにNULLを返します)。良いことは、2つの別々の問題に対して2つの別々のエラーケースがあることを知っていることです。

最大スタックサイズを見つけるには、メモリをパターンで埋め、アプリケーションを実行して、それがどこまで進んだかを確認します。Craigからの返信も参照してください。

于 2009-06-10T23:43:29.650 に答える
0

ヒープのコードを編集できる場合は、メモリの各ブロックに2、3バイトの余分なバイト(このような低リソースではトリッキー)を追加できます。これらのバイトには、スタックとは異なる既知のパターンが含まれている可能性があります。これにより、スタック内に表示されることでスタックと衝突した場合、またはその逆の場合に手がかりが得られる可能性があります。

于 2009-06-09T04:33:33.383 に答える
0

Unixライクなオペレーティングシステムでは、パラメータが0のsbrk()という名前のライブラリ関数を使用すると、動的に割り当てられたヒープメモリの最上位アドレスにアクセスできます。戻り値はvoid*ポインターであり、任意のスタック割り当て変数のアドレスと比較できます。

この比較の結果を使用する場合は注意が必要です。CPUとシステムアーキテクチャによっては、スタックが任意の上位アドレスからダウンし、割り当てられたヒープがローバウンドメモリからアップする場合があります。

オペレーティングシステムには、メモリ管理に関する他の概念(OS / 9など)があり、ヒープとスタックを空きメモリのさまざまなメモリセグメントに配置する場合があります。これらのオペレーティングシステム(特に組み込みシステムの場合)では、システムが一致するサイズのメモリセグメントを割り当てることができるように、アプリケーションの最大メモリ要件を事前に定義する必要があります。

于 2009-06-22T12:07:06.167 に答える